web-dev-qa-db-fra.com

Ce que l'on entend, "un utilisateur ne devrait pas décider s'il s'agit d'un administrateur ou non. Les privilèges ou le système de sécurité devraient".

L'exemple utilisé dans la question Passez des données minimales à une fonction touche le meilleur moyen de déterminer si l'utilisateur est un administrateur ou non. Une réponse commune était:

user.isAdmin()

Cela a invité un commentaire répété plusieurs fois et a été élu plusieurs fois:

Un utilisateur ne devrait pas décider s'il s'agit d'un administrateur ou non. Les privilèges ou le système de sécurité devraient. Quelque chose étant étroitement couplé à une classe ne signifie pas que c'est une bonne idée de faire partie de cette classe.

J'ai répondu,

L'utilisateur ne décide rien. L'objet utilisateur/table stocke des données sur chaque utilisateur. Les utilisateurs réels ne veulent pas tout changer sur eux-mêmes.

Mais ce n'était pas productif. Il existe clairement une différence de perspective sous-jacente qui rend la communication difficile. Quelqu'un peut-il m'expliquer pourquoi user.isadmin () est mauvais et peignez une brève esquisse de ce à quoi elle ressemble à "à droite"?

Vraiment, je ne vois pas l'avantage de séparer la sécurité du système qu'elle protège. Tout texte de sécurité dira que la sécurité doit être conçue dans un système dès le début et examiné à chaque étape du développement, de déploiement, de maintenance et même de fin de vie. Ce n'est pas quelque chose qui peut être boulonné sur le côté. Mais 17 à la hausse des votes jusqu'à présent sur ce commentaire indique que je manque quelque chose d'important.

55
GlenPeterson

Pensez à l'utilisateur en tant que clé et aux autorisations comme valeur.

Différents contextes peuvent avoir différentes autorisations pour chaque utilisateur. Au fur et à mesure que les autorisations sont pertinentes sur le contexte, vous devez modifier l'utilisateur pour chaque contexte. C'est donc mieux que vous mettez cette logique ailleurs (par exemple la classe de contexte) et recherchez simplement les autorisations si nécessaire.

Un effet secondaire est que cela pourrait rendre votre système plus sécurisé, car vous ne pouvez pas oublier d'effacer le drapeau d'administrateur pour des endroits où il n'est pas pertinent.

12
Wilbert

Ce commentaire a été mal libéré.

Le point n'est pas de savoir si l'objet utilisateur "décide" s'il s'agit d'un administrateur, d'un utilisateur d'essai, d'un superutilisateur ou d'un fichier dans ou hors de l'ordinaire . Évidemment, cela ne décide aucune de ces choses, le système logiciel en général, tel que mis en œuvre par vous, fait. Le point est de savoir si la classe "utilisateur" devrait représenter les rôles de Le principal que ses objets représentent ou si cela incombe à une autre classe.

40
Kilian Foth

Je n'aurais pas choisi de verser le commentaire original comme il a été libéré, mais cela identifie un potentiellement problème légitime.

Plus précisément, les préoccupations que la séparation de mandat sont l'authentification vs. Autorisation .

Authentification fait référence au processus de connexion et d'obtention d'une identité. C'est comme ça que les systèmes savent qui vous êtes et utilisé pour des choses comme la personnalisation, la propriété d'objet, etc.

Autorisation fait référence à Qu'est-ce que vous êtes autorisé à faire, et ceci (généralement) est pas Déterminé par Qui vous êtes. Au lieu de cela, il est déterminé par une politique de sécurité comme des rôles ou des autorisations, qui ne se soucient pas des choses comme votre nom ou votre adresse électronique.

Ces deux peuvent changer orthogonalement l'un à l'autre. Par exemple, vous pouvez modifier le modèle d'authentification en ajoutant des fournisseurs OpenID/Openauuth. Et vous pourriez modifier la politique de sécurité en ajoutant un nouveau rôle ou en passant de la RBAC en ABAC.

Si tout cela passe dans une classe ou une abstraction, votre code de sécurité, qui est l'un de vos outils les plus importants pour Atténuation des risques, devient ironiquement, à haut risque.

J'ai travaillé avec des systèmes où l'authentification et l'autorisation étaient trop étroitement couplées. Dans un système, il y avait deux bases de données utilisateur parallèles, chacune pour un type de "rôle". La personne ou l'équipe qui l'a conçu apparemment n'a jamais considéré que d'un seul utilisateur physique peut être dans les deux rôles, ou qu'il pourrait y avoir certaines actions communes à plusieurs rôles, ou qu'il pourrait y avoir des problèmes avec les collisions d'identité de l'utilisateur. C'est un exemple certes extrême, mais c'était/est incroyablement Douleur au travail avec.

Microsoft et Sun/Oracle (Java) se réfèrent au Agrégation d'informations d'authentification et d'autorisation en tant que principale de sécurité . Ce n'est pas parfait, mais cela fonctionne assez bien. Dans .net, par exemple, vous avez IPrincipal, lequel encapsule le IIdentity - le premier étant A Politique ( Autorisation) Objet tandis que ce dernier est un identité (authentification). Vous pouvez raisonnablement remettre en question la décision de mettre un à l'intérieur de l'autre, mais la chose importante est que la plupart du code que vous écrivez sera pour une seule des abstractions ce qui signifie qu'il est facile à tester et à refacteur.

Il n'y a rien de mal avec un User.IsAdmin champ ... Sauf si Il y a aussi un User.Name champ. Cela indiquerait que le concept "utilisateur" n'est pas correctement défini Et c'est tristement, une erreur très courante parmi les développeurs qui sont un peu mouillées derrière les oreilles en matière de sécurité. En règle générale, la seule chose qui devrait être partagée par identité et stratégie est l'ID utilisateur, qui, qui n'est pas coïncidemment, c'est exactement comment il est mis en œuvre dans les deux Windows et * Nix Security Models.

Il est totalement acceptable de créer des objets de wrapper qui encapsulent à la fois l'identité et la politique. Par exemple, cela faciliterait la création d'un écran de tableau de bord où vous devez afficher un message "Hello" en plus de divers widgets ou de liens que l'utilisateur actuel est autorisé à accéder. Tant que ce wrapper juste wraps l'identité et les informations de politique, et ne prétend pas la posséder. En d'autres termes, tant que ce n'est pas présenté comme un racine agrégée.

Un modèle de sécurité simpliste toujours semble comme une bonne idée lorsque vous avez la première conception d'une nouvelle application, à cause de Yagni et de tout cela, mais cela finit presque toujours à revenir à vous mordre plus tard, car , surprise surprise, de nouvelles fonctionnalités sont ajoutées!

Donc, si vous savez ce qui vous convient le mieux, vous conserverez des informations d'authentification et d'autorisation séparées. Même si "l'autorisation" en ce moment est aussi simple qu'un drapeau "isadmin", vous serez toujours mieux désactivé si cela ne fait pas partie de la même classe ou de la même table que les informations d'authentification, de sorte que si votre politique de sécurité doit être nécessaire. Changer, vous n'avez pas besoin de chirurgie reconstructive sur vos systèmes d'authentification qui fonctionnent déjà bien.

30
Aaronaught

Eh bien, au moins il viole principe de responsabilité unique . Devrait-il y avoir des changements (niveaux d'administration multiples, des rôles encore plus différents?) Ce type de conception serait assez désordonné et affreux de maintenir.

Considérez l'avantage de séparer le système de sécurité de la classe d'utilisateurs, de sorte que les deux peuvent avoir un rôle unique et bien défini. Vous vous retrouvez avec un nettoyant, plus facile à maintenir la conception globale.

28
Zavior

Objet.isx () est utilisé pour représenter une relation claire entre l'objet et x, qui peut être exprimée en tant que résultat booléen, par exemple:

Eau.isboiling ()

Circuit.isopen ()

Utilisateur.isadmin () suppose une seule signification de "administrateur" dans chaque contexte du système, qu'un utilisateur est, dans chaque partie du système, un administrateur.

Bien que cela semble assez simple, un programme du monde réel ne correspondra presque jamais à ce modèle, il sera très certainement nécessaire d'administrer une ressource [x] mais non [y] ou pour des types d'administrateurs plus distincts (un projet [projet ] Administrateur vs a Administrateur [System]).

Cette situation nécessite généralement une enquête sur les exigences. Rarement, si jamais, un client souhaite que la relation utilisatrice.Aisadmin () représente réellement, alors je voudrais hésiter à mettre en œuvre une telle solution sans clarification.

9
FMJaguar

L'affiche a simplement eu un design différent et plus compliqué à l'esprit. À mon avis, User.isAdmin() va bien. Même si vous introduisez plus tard un modèle d'autorisations compliquées, je vois peu de raisons de déplacer User.isAdmin(). Plus tard, vous souhaiterez peut-être diviser la classe d'utilisateurs en un objet représentant un utilisateur connecté et un objet statique représentant les données sur l'utilisateur. Ou vous ne pouvez pas. Économisez demain pour demain.

6
kevin cline

pas vraiment un problème ...

Je ne vois pas de problème avec User.isAdmin(). Je préfère certainement cela à quelque chose comme PermissionManager.userHasPermission(user, permissions.ADMIN), qui, dans le nom sacré du SRP, le code est moins clair et n'ajoute rien de la valeur.

Je pense que SRP est interprété un peu à peu près par certains. Je pense que c'est bien, même préférable pour une classe d'avoir une interface riche. SRP signifie juste qu'un objet doit déléguer à des collaborateurs tout ce qui ne relève pas de sa responsabilité. Si le rôle d'administrateur d'un utilisateur est quelque chose de plus impliqué qu'un champ booléen, il pourrait très bien de bien vouloir loger que l'objet de l'utilisateur déléguer à une autorisationManager. Mais cela ne signifie pas que ce n'est pas aussi une bonne idée d'un utilisateur de conserver sa méthode isAdmin. En fait, cela signifie que lorsque votre candidature est diplômée du simple à complexe, vous souhaitez avoir à modifier le code qui utilise l'objet utilisateur. IOW, votre client n'a pas besoin de savoir ce qu'il faut vraiment pour répondre à la question de savoir si un utilisateur est un administrateur.

... mais pourquoi voulez-vous vraiment savoir?

Cela dit, il me semble que vous avez rarement besoin de savoir si un utilisateur est un administrateur, sauf pour pouvoir répondre à une autre question, comme si un utilisateur est autorisé à effectuer une action spécifique, disons comme mettre à jour un widget. Si tel est le cas, je préférerais avoir une méthode sur widget, comme isUpdatableBy(User user):

boolean isUpdatableBy(User user) {
    return user.isAdmin();
}

Ainsi, un widget est responsable de savoir quels critères doivent être satisfaits pour qu'il soit mis à jour par un utilisateur et qu'un utilisateur est responsable de savoir s'il s'agit d'un administrateur. Cette conception communique clairement à ses intentions et facilite la graduation de la logique commerciale plus complexe si et à ce moment-là.

[Éditer]

le problème avec non ayant User.isAdmin() et en utilisant PermissionManager.userHasPermission(...)

Je pensais ajouter à ma réponse pour expliquer pourquoi je préfère grandement appeler une méthode sur l'objet utilisateur d'appeler une méthode sur un objet PermissionManager si je veux savoir si un utilisateur est administrateur (ou a un rôle d'administration).

Je pense qu'il est juste de supposer que vous allez toujours dépendre de la classe d'utilisateurs où que vous ayez besoin de poser la question est cet utilisateur un administrateur? C'est un Dépendance Vous ne pouvez pas vous débarrasser de. Mais si vous avez besoin de transmettre l'utilisateur à un autre objet pour poser une question à ce sujet, cela crée une nouvelle dépendance sur cet objet sur celui que vous avez déjà sur l'utilisateur. Si la question se pose beaucoup, c'est beaucoup d'endroits où vous créez une dépendance supplémentaire et que vous devrez peut-être changer si l'une des dépendances la demande.

Comparez ceci avec déplacer la dépendance dans la classe d'utilisateurs. Maintenant, soudainement, vous avez un système dans lequel le code client (code qui doit poser la question est cet utilisateur un administrateur ) n'est pas couplé à la mise en œuvre de la manière dont Cette question est répondue. Vous êtes libre de changer complètement le système d'autorisation et il vous suffit de mettre à jour une méthode dans une classe pour le faire. Tout le code client reste le même.

Insister sur ne pas avoir une méthode isAdmin sur l'utilisateur de crainte de créer une dépendance dans la classe d'utilisateurs sur le sous-système d'autorisations, est à mon avis de ne pas passer à un dollar pour gagner un centime. Bien sûr, vous évitez une dépendance dans la classe d'utilisateurs, mais au coût de la création d'une dans chaque lieu où vous devez poser la question. Pas une bonne affaire.

5
KaptajnKold

Cette discussion m'a rappelé un blog post de Eric Lippert, qui a été porté à mon attention dans une discussion similaire sur la conception, la sécurité et l'exactitude de la classe.

Pourquoi tant de classes-cadres sont-elles scellées?

En particulier, Eric soulève le point:

4) sécurisé. Le point de polymorphisme entier est que vous pouvez transmettre des objets qui ressemblent à des animaux mais sont en fait des girafes. Il y a des problèmes de sécurité potentiels ici.

Chaque fois que vous implémentez une méthode qui prend une instance d'un type non scellé, vous devez écrire cette méthode pour être robuste face aux instances potentiellement hostiles de ce type. Vous ne pouvez pas compter sur aucun invariant que vous savez être vrai de vos implémentations, car une page Web hostile peut sous-classer votre implémentation, remplacez les méthodes virtuelles pour faire des trucs qui gâchent votre logique et la transmet à chaque fois que je scelle une classe Je peux écrire des méthodes qui utilisent cette classe avec la confiance que je connais ce que cette classe fait.

Cela peut sembler comme une tangente, mais je pense que cela fait du point de vue avec le point, d'autres affiches ont soulevé à propos de [~ # ~ # ~ # ~]principe de responsabilité unique . Considérez la classe malveillante suivante comme une raison de ne pas mettre en œuvre une méthode ou une propriété User.IsAdmin.

public class MyUser : User
{

    public new boolean IsAdmin()
    {
        // You know it!
        return true;
    }

    // Anything else we can break today?

}

Certes, c'est un peu un étirement: personne ne suggère pas de faire IsAmdmin une méthode virtuelle, mais cela pourrait se produire si votre architecture d'utilisateur/de rôle a fini par devenir bizarrement complexe. (Ou peut-être pas. Pensez à public class Admin : User { ... }: une telle classe peut avoir exactement ce code ci-dessus.) Obtenir un vecteur de binaire malveillant n'est pas un vecteur d'attaque commun et a beaucoup plus de potentiel pour le chaos que d'une bibliothèque d'utilisateurs obscur, puis Cela pourrait être la escalade de privilège bug qui ouvre la porte aux vrais shenanigans. Enfin, si un exemple de binaire ou d'objet malveillant a trouvé son chemin dans le temps d'exécution, il n'est pas beaucoup plus un étirement d'imaginer que le "privilège ou le système de sécurité" est remplacé de manière similaire.

Mais prenez le point d'eric à cœur, si vous mettez votre code utilisateur dans un type particulier de système vulnérable, vous avez peut-être perdu le jeu.

Oh, et juste pour être précis pour le dossier, je suis d'accord avec le postulat dans la question: "Un utilisateur ne devrait pas décider s'il s'agit d'un administrateur ou non. Les privilèges ou le système de sécurité devraient. " Une méthode User.IsAdmin est une mauvaise idée si vous exécutez le code sur le système, vous ne restant pas en commande à 100% du code - vous devriez plutôt faire Permissions.IsAdmin(User user).

1
Patrick M

Le problème ici est:

if (user.isAdmin()) {
   doPriviledgedOp();
} else {
   // maybe we can anyway!
   doPriviledgedOp();
}

Soit vous avez configuré votre sécurité correctement auquel cas la méthode privilégiée doit vérifier si l'appelant a une autorité suffisante - auquel cas le chèque est superflu. Ou vous n'avez pas et faites confiance à une classe non privilégiée pour prendre ses propres décisions sur la sécurité.

0
James Anderson