DDD CQRS: autorización por consulta y por comando


15

Resumen

¿Debería implementarse la autorización en CQRS / DDD por comando / consulta o no?

Estoy desarrollando por primera vez una aplicación en línea utilizando más o menos estrictamente el patrón DDD CQRS. Me topé con algún problema, que realmente no puedo entender.

La aplicación que estoy creando es una aplicación de libro mayor que permite a las personas crear libros mayores, así como también que otras personas puedan verlos, editarlos o eliminarlos, como los empleados. El creador de un libro mayor debería poder editar los derechos de acceso del libro mayor que creó. Incluso podría cambiar la propiedad. El dominio tiene dos agregados TLedger y TUser .

Leí muchas publicaciones con la palabra clave DDD / CQRS sobre seguridad, autorización, etc. La mayoría de ellas declararon que la autorización era un Subdominio genérico , a menos que uno estuviera creando una aplicación de seguridad.

En este caso, el dominio central es ciertamente un dominio contable interesado en transacciones, saldos y cuentas. Pero también se requiere la funcionalidad de poder administrar el acceso de grano fino a los libros de contabilidad. Me pregunto cómo diseñar esto en términos DDD / CQRS.

En los tutoriales de DDD se afirma que los comandos son parte del lenguaje omnipresente. Son significativos. Son acciones concretas que representan la "cosa real".

Debido a que todos esos comandos y consultas son acciones reales que los usuarios ejecutarían en la "vida real", ¿la implementación de la autorización debería estar asociada con todos estos "comandos" y "consultas"? Un usuario tendría autorización para ejecutar TLedger.addTransaction () pero no TLedger.removeTransaction (), por ejemplo. O, un usuario podría ejecutar la consulta "getSummaries ()" pero no "getTransactions ()".

Existiría un mapeo tridimensional en forma de user-ledger-command o user-ledger-query para determinar los derechos de acceso.

O, de forma desacoplada, los "permisos" denominados se registrarían para un usuario. Permisos que luego se asignarían para comandos específicos. Por ejemplo, el permiso "ManageTransactions" permitiría a un usuario ejecutar "AddTransaction ()", "RemoveTransaction ()", etc.

  1. Usuario de mapeo de permisos -> libro mayor -> comando / consulta

  2. Usuario de mapeo de permisos -> libro mayor -> permiso -> comando / consulta

Esa es la primera parte de la pregunta. O, en resumen, ¿se debe implementar la autorización en CQRS / DDD por comando o por consulta? O, ¿se debe desacoplar la autorización de los comandos?

En segundo lugar, en relación con la autorización basada en permisos. Un usuario debe poder administrar los permisos en sus Ledgers o en los Ledgers que se le ha permitido administrar.

  1. Los comandos de administración de autorización se producen en el Libro mayor

Pensé en agregar los eventos / comandos / controladores en el agregado de Ledger , como grantPermission (), revokePermission (), etc. En este caso, la aplicación de esas reglas sucedería en los controladores de comandos. Pero esto requeriría que todos los comandos incluyan la identificación del usuario que emitió ese comando. Luego, me registraría en el TLedger si existe el permiso para que ese usuario ejecute ese comando.

Por ejemplo :

class TLedger{ 
    function addTransactionCmdHandler(cmd){
        if (!this.permissions.exist(user, 'addTransaction')
            throw new Error('Not Authorized');
    }
}
  1. Comandos de gestión de autorizaciones en el Usuario

La otra forma sería incluir los permisos en el TUser. Un usuario de T tendría un conjunto de permisos. Luego, en los controladores de comandos de TLedger, recuperaría al usuario y comprobaría si tiene permiso para ejecutar el comando. Pero esto requeriría que busque el agregado TUser para cada comando TLedger.

class TAddTransactionCmdHandler(cmd) {
    this.userRepository.find(cmd.userId)
    .then(function(user){
        if (!user.can(cmd)){
            throw new Error('Not authorized');
        }
        return this.ledgerRepository.find(cmd.ledgerId);
    })
    .then(function(ledger){
        ledger.addTransaction(cmd);
    })

}
  1. Otro dominio con servicio

Otra posibilidad sería modelar completamente otro dominio de autorización. Este dominio estaría interesado en derechos de acceso, autorización, etc. El subdominio de contabilidad usaría un servicio para acceder a este dominio de autorización en forma de AuthorizationService.isAuthorized(user, command).

class TAddTransactionCmdHandler(cmd) {
    authService.isAuthorized(cmd)
    .then(function(authorized){
        if (!authorized) throw new Error('Not authorized');
        return this.ledgerRepository.find(cmd.ledgerId)
    })
    .then(function(){
        ledger.addTransaction(cmd);
    })

}

¿Qué decisión sería la forma más "DDD / CQRS"?


1
Gran pregunta: he estado tratando de abordar problemas similares y ninguna de la literatura parece abordarlo directamente. Estaba un poco confundido por la segunda mitad de tu pregunta. Parecía que se preguntaba dónde colocar la administración de permisos (agregar o quitar permisos), pero los ejemplos que se muestran son para agregar una transacción, por lo que parece que la segunda mitad pregunta "¿cómo debo consultar los permisos?". ¿Puedes aclarar esa parte por favor?
emragins

Cada transacción podría tener una política de ejecución. Cada usuario debe pertenecer a uno o más grupos, cada grupo tendrá un perfil de acceso que especifica qué transacciones están permitidas. En tiempo de ejecución, antes de ejecutar una transacción, la política se compara con los perfiles agregados para el usuario que ejecuta. Por supuesto, esto es más fácil decirlo que hacerlo.
NoChance

Respuestas:


5

Para la primera pregunta, he estado luchando con algo similar. Cada vez me inclino más hacia un esquema de autorización en tres fases:

1) Autorización en el nivel de comando / consulta de "¿este usuario alguna vez tiene permiso para ejecutar este comando?" En una aplicación MVC, esto probablemente podría manejarse a nivel de controlador, pero estoy optando por un controlador previo genérico que consultará el almacén de permisos en función del usuario actual y el comando de ejecución.

2) ¿La autorización dentro del servicio de aplicación de "este usuario" alguna vez * tiene permiso para acceder a esta entidad? "En mi caso, esto probablemente terminará siendo una verificación implícita simplemente por medio de filtros en el repositorio; en mi dominio esto es básicamente un TenantId con un poco más de granularidad de OrganizationId.

3) La autorización que se basa en propiedades transitorias de sus entidades (como el Estado) se manejaría dentro del dominio. (Ej. "Solo ciertas personas pueden modificar un libro de contabilidad cerrado".) Estoy optando por poner eso dentro del dominio porque depende en gran medida del dominio y la lógica comercial y no me siento realmente cómodo exponiéndolo en otros lugares.

Me encantaría escuchar las respuestas de los demás a esta idea: destrúyala si lo desea (solo proporcione algunas alternativas si lo hace :))


Creo que tiene algunos puntos válidos con respecto a las diferentes "capas" de autorización. Un sistema en el que estaba trabajando tenía diferentes tipos de usuarios: usuarios registrados y miembros del personal. Los permisos del controlador de comando / consulta hicieron una comprobación básica del tipo de usuario. Si era personal, siempre pasaba. Si era un usuario registrado, solo se permitía si se cumplían ciertas condiciones (por ejemplo, permisos en un agregado).
Magnus

0

Implementaría la autorización como parte de su autorización BC pero la implementaría como un filtro de acción en su sistema Ledger. De esta manera, se pueden desacoplar lógicamente entre sí (su código de Ledger no debería tener que llamar al código de autorización), pero aún así obtendrá una autorización en proceso de alto rendimiento de cada solicitud entrante.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.