Implementación de DDD: usuarios y permisos

16

Estoy trabajando en una pequeña aplicación que intenta comprender los principios del diseño basado en dominios. Si tiene éxito, esto podría ser un piloto para un proyecto más grande. Estoy tratando de seguir el libro "Implementando el diseño impulsado por el dominio" (por Vaughn Vernon) e intentando implementar un foro de discusión simple y similar. También he revisado las muestras IDDD en github. Tengo algunas dificultades para adoptar la identidad y el acceso a mi caso. Déjame darte información de fondo:

  • (Con suerte) entiendo el razonamiento detrás de la separación de los usuarios y la lógica de permisos: es un dominio de soporte, y es un contexto limitado diferente.
  • En el dominio central, no hay usuarios, solo Autores, Moderadores, etc. Estos se crean al llegar al contexto de Identidad y Acceso utilizando un servicio y luego traduciendo los objetos de Usuario recibidos a Moderador.
  • Las operaciones de dominio se llaman con un rol relacionado como parámetro: por ejemplo:

    ModeratePost( ..., moderator);

  • El método del objeto de dominio verifica si la instancia de Moderador dada no es nula (la instancia de Moderador será nula si el usuario que se le preguntó desde el contexto de Identidad y Acceso no tiene la función de Moderador).

  • En un caso, realiza una comprobación adicional antes de modificar una publicación:

    if (forum.IsModeratedby(moderator))

Mis preguntas son:

  • En el último caso, ¿no se mezclan nuevamente las preocupaciones de seguridad en el dominio principal? Anteriormente, los libros decían "con quién puede publicar un tema, o bajo qué condiciones está permitido. Un foro solo necesita saber que un autor está haciendo eso ahora".

  • La implementación basada en roles en el libro es bastante sencilla: cuando un Moderador es el dominio principal, trata de convertir el ID de usuario actual en una instancia de Moderador o en un Autor cuando lo necesita. El servicio responderá con la instancia apropiada o un valor nulo si el usuario no tiene el rol requerido. Sin embargo, no puedo ver cómo podría adaptar esto a un modelo de seguridad más complejo; nuestro proyecto actual para el que estoy probando tiene un modelo bastante complejo con grupos, ACL, etc.

Incluso con reglas que no son muy complejas, como: "Una publicación debe ser editada solo por su propietario o un editor", este enfoque parece fallar, o al menos no veo la forma correcta de implementarlo.

Al preguntar el contexto de Identidad y Acceso para una instancia de OwnerOrEditor no se siente bien, y terminaría con más y más clases relacionadas con la seguridad en el dominio central. Además, necesitaría pasar no solo el ID de usuario, sino también el identificador del recurso protegido (la identificación de la publicación, el foro, etc.) al contexto de seguridad, que probablemente no debería importarles estas cosas (¿es correcto? )

Al extraer los permisos para el dominio central y verificarlos en los métodos de los objetos de dominio o en los servicios, terminaría en el primer lugar: mezclar las preocupaciones de seguridad con el dominio.

He leído en alguna parte (y tiendo a estar de acuerdo) que estas cosas relacionadas con los permisos no deberían ser parte del dominio central, a menos que la seguridad y los permisos sean el dominio central en sí. ¿Una regla simple como la dada anteriormente justifica que la seguridad sea parte del núcleo del dominio?

LittlePilgrim
fuente
Tal vez pueda encontrar lo que necesita aquí: stackoverflow.com/a/23485141/329660 Además, el hecho de que el contexto de Control de acceso conozca un ID de recurso no significa que tenga conocimiento de dominio sobre qué tipo de entidad es ese recurso o para qué sirve. hace.
guillaume31
Gracias, he visto esa publicación anteriormente, mi problema es exactamente lo que dice la edición al final: me gustaría mover el control de acceso fuera de mi dominio principal, pero siento que me he topado con mi implementación. Sin embargo, su sugerencia sobre la ID del recurso tiene sentido: como no uso el concepto de Usuario o Rol en el dominio central sino roles concretos, tal vez podría usar el concepto de Recurso en el BC de seguridad y asignarlos al concreto relacionado concepto de dominio Vale la pena intentarlo, gracias!
LittlePilgrim
¿Las muestras de código en el enlace al menos no responden a "No puedo ver cómo podría adaptar esto a un modelo de seguridad más complejo" ?
guillaume31
Mi problema no es con la implementación del modelo de seguridad en sí, no puedo ver cómo debo mapear estas reglas más complicadas en el dominio. ¿Cómo debería cambiar el mapeo Usuario -> Autor si no se trata de un simple modelo basado en roles en el lado de la seguridad? Pasar ID de recursos al otro contexto podría funcionar, HasPermissionToEdit(userId, resourceId)pero no me parece correcto contaminar la lógica del dominio con estas llamadas. ¿Probablemente debería verificar esto en los métodos de servicio de la aplicación, antes de invocar la lógica de dominio?
LittlePilgrim
Por supuesto, debería estar en los servicios de aplicaciones ... Pensé que estaba claro en partes del código como UserService @AccessControlList[inf3rno]en la respuesta a la que me vinculé.
guillaume31

Respuestas:

6

A veces es difícil distinguir entre las reglas de control de acceso real y los invariantes de dominio que bordean el control de acceso.

Especialmente, las reglas que dependen de datos solo disponibles en el curso de una determinada pieza de lógica de dominio podrían no ser fácilmente extraíbles del dominio. Por lo general, se llama a Control de acceso antes o después de realizar una operación de dominio, pero no durante.

El assert (forum.IsModeratedBy(moderator))ejemplo de Vaughn Vernon probablemente debería haber estado fuera del Dominio, pero no siempre es factible.

Tendría que pasar no solo el ID de usuario, sino también el identificador del recurso protegido (el ID de la publicación, el foro, etc.) al contexto de seguridad, que probablemente no debería preocuparse por estas cosas (¿es correcto?)

Si hay un BC de seguridad y desea que maneje esa lógica, no tiene que saber qué es un foro en detalle, sino:

  • Podría tener conocimiento de conceptos como "moderado por" y otorgar o denegar derechos de acceso en consecuencia.
  • Podría tener una lógica de adaptador que se suscriba a los eventos de Core Domain y los traduzca a pares de valores de clave simples (recurso, usuarios autorizados) para que el BC de seguridad los almacene y use.
guillaume31
fuente
Como ambas respuestas son útiles y más o menos apuntan a la misma dirección, las he votado a ambas. Acepté este, ya que @ guillaume31 ha respondido más o menos a mi pregunta sobre la implementación de Vernon y continuaré con mi implementación en función de su sugerencia sobre el uso de recursos en Security BC.
LittlePilgrim
Tengo que decir que creo que esto es todo lo contrario de mi respuesta.
Ewan
1
Tal vez ahora estoy demasiado confundido, pero mi interpretación fue (para ambas respuestas): 1. Mantenga las preocupaciones de seguridad fuera del dominio y use el BC de seguridad como servicio 2. Llame al servicio antes de invocar cualquier objeto de dominio 3. El servicio hará el mapeo de usuarios / acls a moderadores, autores, etc. moderator = securityService.GetModerator(userId, forumId) 4. La lógica de dominio se implementará en estos objetos como en moderator.EditPost () 5. Los métodos como EditPost no sabrán nada sobre conceptos de seguridad, no habrá verificaciones adicionales allí
LittlePilgrim
Todavía estoy buscando respuestas / instrucciones sobre esto, pero descubrí que cualquier lógica de autorización que se base en el estado actual del objeto (como si actualmente está asignado a un moderador) es, de hecho, una lógica de negocios que pertenece a su dominio y, además, que si no está en su dominio, corre el riesgo de terminar en un estado no válido si el modelo puede actualizarse simultáneamente. Por ejemplo, si valida la propiedad utilizando una política y luego va a actualizar ese objeto, en muchos dominios esa propiedad está sujeta a cambios y la acción puede dejar de ser válida.
Jordan
A menos que tenga un contexto de colaboración muy complejo, probablemente pueda darse el lujo de implementar un modelo de simultaneidad optimista aquí usando el control de versiones, pero si sus comprobaciones no se realizan dentro o al menos en una instancia agregada particular, entonces sus comprobaciones pueden no ser consistentes con el actual estado del objeto en el momento en que persiste sus cambios.
Jordan
5

La autenticación y autorización es un mal ejemplo para DDD.

Ninguna de estas cosas es parte de un Dominio a menos que su empresa cree productos de seguridad.

El requisito comercial o de dominio es, o debería ser, "Requiero autenticación basada en roles"

Luego verifica el rol antes de llamar a una función de dominio.

Cuando tenga requisitos complejos como 'Puedo editar mis propias publicaciones pero no otras', asegúrese de que su dominio separe la función de edición EditOwnPost()y de EditOthersPost()que tenga una función simple para mapear roles

También puede separar la funcionalidad en Objetos de dominio, por ejemplo, Poster.EditPost()y Moderator.EditPost()este es un enfoque más orientado a objetos, aunque su elección puede depender de si su método está en un Servicio de dominio o en un Objeto de dominio.

Sin embargo, si elige separar el código, la asignación de roles se realizará fuera del dominio. así, por ejemplo, si tiene un controlador 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);
    }
}

Como puede ver, aunque el mapeo de roles se realiza en la capa de alojamiento, la lógica compleja de lo que constituye editar su propia publicación o la de otros es parte del dominio.

El dominio reconoce la diferencia de las acciones, pero el requisito de seguridad es simplemente que "la funcionalidad puede estar limitada por roles" .

Esto es quizás más claro con la separación de objetos de dominio, pero esencialmente está verificando el método que construye el objeto en lugar del método que llama al método de servicio. Su requisito, si aún desea expresarlo como parte del dominio, se convertiría en 'solo los moderadores pueden construir el objeto moderador'

Ewan
fuente
44
Verificar el rol "estáticamente" es un poco simplista. ¿Qué sucede si un moderador no tiene permiso para editar la publicación de otro moderador? ¿No debería esta verificación ser parte del dominio?
Réda Housni Alaoui
2
@ RédaHousniAlaoui También me pregunto sobre esto. No puedo pensar en una forma de manejar esto que no sea incluir alguna mención de Usuarios / Moderadores en el dominio, o realizar algún tipo de lógica dentro de ese ApiController para obtener el papel del autor de la publicación. Ninguno de estos parece correcto, y esta es una cosa bastante común en mi experiencia que alguna orientación clara sería extremadamente útil.
Jimmy
1
@Erwan, el caso de uso del que estoy hablando es dinámico. Basar la frase "Autenticación y autorización es un mal ejemplo para DDD" en ejemplos de hello world es deshonesto. DDD está aquí para evitar la complejidad accidental y permitir gestionar la complejidad del dominio. Los permisos dinámicos no son una complejidad accidental ni algo que no sucede en la vida real.
Réda Housni Alaoui
1
En mi humilde opinión, el problema con su solución es que no satisface al cliente. El cliente a menudo quiere poder cambiar esas relaciones dinámicamente. Además, eso también ocurre cuando un proveedor proporciona el mismo software empresarial a diferentes compañías. Si el software no se puede modificar, el proveedor finalmente morirá.
Réda Housni Alaoui
1
"Pero generalmente se reconoce como una" cosa mala "su configuración de seguridad se vuelve inmanejable con el tiempo, lo que significa que su aplicación se vuelve insegura". Con el diseño y prueba correctos, es totalmente manejable. Pero, desde mi XP, para producir el diseño correcto, el dominio debe verificar el permiso. La alternativa es la utopía.
Réda Housni Alaoui