Implémentation de DDD: utilisateurs et autorisations

16

Je travaille sur une petite application essayant de comprendre les principes de la conception pilotée par domaine. En cas de succès, cela pourrait être un pilote pour un projet plus vaste. J'essaie de suivre le livre "Implémentation d'une conception pilotée par le domaine" (par Vaughn Vernon) et j'essaie de mettre en œuvre un forum de discussion simple et similaire. J'ai également vérifié les échantillons IDDD sur github. J'ai quelques difficultés à adopter l'identité et l'accès à mon dossier. Permettez-moi de donner quelques informations générales:

  • J'espère (je l'espère) comprendre le raisonnement derrière la séparation de la logique des utilisateurs et des autorisations: il s'agit d'un domaine de prise en charge et d'un contexte borné différent.
  • Dans le domaine principal, il n'y a pas d'utilisateurs, juste des auteurs, des modérateurs, etc.
  • Les opérations de domaine sont appelées avec un rôle associé comme paramètre: par exemple:

    ModeratePost( ..., moderator);

  • La méthode de l'objet de domaine vérifie si l'instance Moderator donnée n'est pas nulle (l'instance Moderator sera nulle si l'utilisateur demandé dans le contexte Identity and Access n'a pas le rôle Moderator).

  • Dans un cas, il effectue une vérification supplémentaire avant de modifier une publication:

    if (forum.IsModeratedby(moderator))

Mes questions sont:

  • Dans ce dernier cas, les problèmes de sécurité ne sont-ils pas à nouveau intégrés au domaine principal? Auparavant, les livres stipulaient "avec qui peut publier un sujet, ou dans quelles conditions cela est autorisé. Un forum a juste besoin de savoir qu'un auteur le fait en ce moment".

  • L'implémentation basée sur les rôles dans le livre est assez simple: lorsqu'un modérateur est le domaine principal essaie de convertir l'ID utilisateur actuel en une instance de modérateur ou en un auteur lorsqu'il en a besoin. Le service répondra avec l'instance appropriée ou une valeur nulle si l'utilisateur n'a pas le rôle requis. Cependant, je ne vois pas comment l'adapter à un modèle de sécurité plus complexe; notre projet actuel pour lequel je pilote a un modèle assez complexe avec des groupes, des ACL, etc.

Même avec des règles qui ne sont pas très complexes, comme: "Un article ne doit être édité que par son propriétaire ou un éditeur", cette approche semble s'effondrer, ou du moins je ne vois pas la bonne façon de l'implémenter.

En demandant le contexte d'identité et d'accès pour une instance OwnerOrEditor ne me semble pas correct, et je me retrouverais avec de plus en plus de classes liées à la sécurité dans le domaine principal. De plus, je devrais passer non seulement l'ID utilisateur, mais l'identifiant de la ressource protégée (l'identifiant de la publication, le forum, etc.) au contexte de sécurité, qui ne devrait probablement pas se soucier de ces choses (est-ce correct? )

En tirant les autorisations sur le domaine principal et en les vérifiant dans les méthodes des objets de domaine ou dans les services, je me retrouvais à la case départ: mélanger les problèmes de sécurité avec le domaine.

J'ai lu quelque part (et j'ai tendance à être d'accord avec lui) que ces choses liées aux autorisations ne devraient pas faire partie du domaine principal, à moins que la sécurité et les autorisations ne soient le domaine principal lui-même. Une règle simple comme celle donnée ci-dessus justifie-t-elle d'intégrer la sécurité dans le domaine principal?

LittlePilgrim
la source
Vous pouvez peut-être trouver ce dont vous avez besoin ici: stackoverflow.com/a/23485141/329660 En outre, ce n'est pas parce que le contexte de contrôle d'accès connaît un ID de ressource qu'il a une connaissance du domaine sur le type d'entité de cette ressource ou ce qu'elle Est-ce que.
guillaume31
Merci, j'ai vu ce post plus tôt, mon problème est exactement ce que dit la modification à la fin: je voudrais déplacer le contrôle d'accès hors de mon domaine principal, mais je sens que je me suis heurté à un mur avec mon implémentation. Cependant, votre suggestion sur l'ID de ressource est logique: comme je n'utilise pas le concept d'utilisateur ou de rôle dans le domaine principal mais des rôles concrets, je pourrais peut-être utiliser le concept de ressource dans le BC de sécurité et les mapper au béton connexe concept de domaine. Ça vaut le coup, merci!
LittlePilgrim
Les exemples de code du lien ne répondent-ils pas au moins à «Je ne vois pas comment l'adapter à un modèle de sécurité plus complexe» ?
guillaume31
Mon problème n'est pas avec l'implémentation du modèle de sécurité lui-même, je ne vois pas comment mapper ces règles plus compliquées dans le domaine. Comment le mappage Utilisateur -> Auteur devrait-il changer s'il ne s'agit pas d'un simple modèle basé sur les rôles du côté de la sécurité? Passer des ID de ressource à l'autre contexte peut fonctionner, comme, HasPermissionToEdit(userId, resourceId)mais je ne me sens pas bien de contaminer la logique du domaine avec ces appels. Je devrais probablement vérifier ces derniers dans les méthodes de service d'application, avant d'invoquer la logique de domaine?
LittlePilgrim
Bien sûr, cela devrait être dans les services d'application ... Je pensais que c'était clair à partir de parties du code comme UserService @AccessControlList[inf3rno] dans la réponse à laquelle j'étais liée.
guillaume31

Réponses:

6

Il est parfois difficile de faire la distinction entre les règles de contrôle d'accès réelles et les invariants de domaine limitant le contrôle d'accès.

En particulier, les règles qui dépendent de données disponibles uniquement très loin dans le cours d'un morceau particulier de logique de domaine peuvent ne pas être facilement extractibles hors du domaine. Habituellement, le contrôle d'accès est appelé avant ou après une opération de domaine, mais pas pendant.

L' assert (forum.IsModeratedBy(moderator))exemple de Vaughn Vernon aurait probablement dû être en dehors du domaine, mais ce n'est pas toujours faisable.

Il faudrait que je passe non seulement l'ID utilisateur, mais l'identifiant de la ressource protégée (l'identifiant de la publication, le forum, etc.) au contexte de sécurité, qui ne devrait probablement pas se soucier de ces choses (est-ce correct?)

S'il y a un Security BC et que vous voulez qu'il gère cette logique, il n'a pas besoin de savoir ce qu'est un Forum en détail mais:

  • Il pourrait simplement avoir une connaissance de concepts tels que «modéré par» et accorder ou refuser des droits d'accès en conséquence.
  • Vous pouvez avoir une logique d'adaptateur qui s'abonne aux événements du domaine principal et les traduit en paires de valeurs de clé simples (ressource, utilisateurs autorisés) que Security BC doit stocker et utiliser.
guillaume31
la source
Comme les deux réponses sont utiles et pointent plus ou moins dans la même direction, je les ai toutes les deux votées. J'ai accepté celui-ci, car @ guillaume31 a plus ou moins répondu à ma question sur l'implémentation de Vernon et je vais continuer mon implémentation en fonction de son indice sur l'utilisation des ressources dans Security BC.
LittlePilgrim
Je dois dire que je pense que c'est tout le contraire de ma réponse.
Ewan
1
Peut-être que je suis trop confus maintenant, mais mon interprétation était (pour les deux réponses): 1. Gardez les problèmes de sécurité hors du domaine et utilisez la sécurité BC en tant que service 2. Appelez le service avant d'appeler des objets de domaine 3. Le service fera le mappage des utilisateurs / acls aux modérateurs, auteurs, etc. moderator = securityService.GetModerator(userId, forumId) 4. La logique du domaine sera implémentée dans ces objets comme dans moderator.EditPost () 5. Les méthodes comme EditPost ne connaîtront rien des concepts de sécurité, il n'y aura pas de vérifications supplémentaires là
LittlePilgrim
Je suis toujours à la recherche de réponses / directions à ce sujet moi-même, mais j'ai trouvé que toute logique d'autorisation qui repose sur l'état actuel de l'objet (comme s'il est actuellement affecté à un modérateur) est en fait une logique métier qui appartient à votre domaine et en outre que s'il n'est pas dans votre domaine, vous risquez de vous retrouver dans un état non valide si le modèle peut être mis à jour simultanément. Par exemple, si vous validez la propriété à l'aide d'une stratégie, puis allez mettre à jour cet objet - dans de nombreux domaines, la propriété est susceptible de changer et l'action peut ne plus être valide.
Jordan
À moins d'avoir un contexte collaboratif très complexe, vous pouvez probablement vous permettre d'implémenter un modèle de concurrence optimiste ici en utilisant le contrôle de version, mais si vos vérifications ne sont pas effectuées dans ou au moins contre une instance d'agrégation particulière, alors vos vérifications peuvent ne pas être cohérentes avec la réalité état de l'objet au moment où vous persistez vos modifications.
Jordan
5

L'authentification et l'autorisation sont un mauvais exemple pour DDD.

Aucune de ces choses ne fait partie d'un domaine à moins que votre entreprise ne crée des produits de sécurité.

L'exigence commerciale ou de domaine est, ou devrait être, "J'ai besoin d'une authentification basée sur les rôles"

Vous vérifiez ensuite le rôle avant d'appeler une fonction de domaine.

Lorsque vous avez des exigences complexes telles que «Je peux modifier mes propres publications mais pas les autres», assurez-vous que votre domaine sépare la fonction de modification dans EditOwnPost()etEditOthersPost() que vous disposez d'une fonction simple pour le mappage des rôles

Vous pouvez également séparer la fonctionnalité en objets de domaine, tels que Poster.EditPost()et Moderator.EditPost()il s'agit d'une approche plus POO, bien que votre choix puisse dépendre si votre méthode se trouve dans un service de domaine ou un objet de domaine.

Quelle que soit la façon dont vous choisissez de séparer le code, le mappage de rôle se produira en dehors du domaine. donc par exemple si vous avez un contrôleur webapi:

PostController : ApiController
{
    [Authorize(Roles = "User")]
    public void EditOwnPost(string postId, string newContent)
    {
        this.postDomainService.EditOwnPost(postId, string newContent);
    }

    [Authorize(Roles = "Moderator")]
    public void EditOtherPost(string postId, string newContent)
    {
        this.postDomainService.EditOtherPost(postId, string newContent);
    }
}

Comme vous pouvez le voir, bien que le mappage des rôles soit effectué sur la couche d'hébergement, la logique complexe de ce qui constitue la modification de votre propre publication ou d'une autre fait partie du domaine.

Le domaine reconnaît la différence des actions, mais l'exigence de sécurité est simplement que "la fonctionnalité peut être limitée par les rôles" .

C'est peut-être plus clair avec la séparation des objets de domaine, mais vous vérifiez essentiellement la méthode qui construit l'objet au lieu de la méthode qui appelle la méthode de service. Votre exigence, si vous voulez toujours l'exprimer dans le cadre du domaine deviendrait «seuls les modérateurs peuvent construire l'objet modérateur»

Ewan
la source
4
Vérifier le rôle "statiquement" est un peu simpliste. Que faire si un modérateur n'est pas autorisé à modifier le message d'un autre modérateur? Cette vérification ne devrait-elle pas faire partie du domaine?
Réda Housni Alaoui
2
@ RédaHousniAlaoui Je me pose également des questions à ce sujet. Je ne peux pas penser à un moyen de gérer cela autre que d'inclure une mention des utilisateurs / modérateurs dans le domaine, ou d'effectuer une sorte de logique dans cet ApiController pour obtenir le rôle d'auteur de l'article. Aucun de ces éléments ne semble juste, et c'est une chose assez courante dans mon expérience que des conseils clairs seraient extrêmement utiles.
Jimmy
1
@Erwan, le cas d'utilisation dont je parle est dynamique. Baser la phrase "Authentification et autorisation est un mauvais exemple pour DDD" sur des exemples du monde bonjour est malhonnête. DDD est là pour éviter la complexité accidentelle et permettre de gérer la complexité du domaine. Les autorisations dynamiques ne sont pas une complexité accidentelle ni quelque chose qui ne se produit pas dans la vie réelle.
Réda Housni Alaoui
1
À mon humble avis, le problème avec votre solution est qu'elle ne satisfait pas le client. Le client souhaite souvent pouvoir modifier ces relations de manière dynamique. De plus, c'est également ce qui se produit lorsqu'un fournisseur fournit le même logiciel d'entreprise à différentes entreprises. Si le logiciel est mal adapté, le vendeur finira par mourir.
Réda Housni Alaoui
1
"Mais il est généralement reconnu comme une" mauvaise chose ", vos paramètres de sécurité deviennent ingérables au fil du temps, ce qui signifie que votre application devient précaire." Avec une conception et un test corrects, il est totalement gérable. Mais, à partir de mon XP, pour produire la conception correcte, le domaine doit vérifier l'autorisation. L'alternative est l'utopie.
Réda Housni Alaoui