DDD CQRS – per-query and per-command authorization

cqrsdomain-driven-design

Summary

Should authorization in CQRS/DDD be implemented per-command/query or not?

I am developing for the first time an online application using more or less strictly the DDD CQRS pattern. I bumped into some problem, which I can't really get my head around.

The application I am building is a ledger application allowing people to create ledgers, as well as allowing other people to view/edit/delete them, such as employees. The creator of a ledger should be able to edit the access rights of the ledger he created. Could even change ownership. The domain has two aggregates TLedger and TUser.

I read a lot of posts with the DDD / CQRS keyword concerning security, authorization, etc. Most of them stated that authorization was a Generic Subdomain, unless one was building a security application.

In this case, the core domain is a certainly an accounting domain interested with transactions, balancing and accounts. But the functionality of being able to manage fine grained access to the ledgers is also required. I'm wondering how to design this in DDD/CQRS terms.

It's stated in the DDD tutorials all around the place that the commands are part of the ubiquitous language. They're meaningful. They're concrete actions that represent the "real thing".

Because all those commands and queries are actual actions that users would execute in "real life", should the implementation of the authorization be coupled with all these "commands" and "queries" ? A user would have authorization to execute TLedger.addTransaction() but not TLedger.removeTransaction() for example. Or, a user would be allowed to execute the query "getSummaries()" but not "getTransactions()".

A tri-dimensional mapping would exist in the form of user-ledger-command or user-ledger-query to determine the access rights.

Or, in a decoupled way, named "permissions" would be registered for a user. Permissions that would then be mapped for specific commands. For example, permission "ManageTransactions" would allow a user to execute "AddTransaction()", "RemoveTransaction()", etc.

  1. Permissions mapping user -> ledger -> command/query

  2. Permissions mapping user -> ledger -> permission -> command/query

That's the first part of the question. Or in brief, should authorization in CQRS/DDD be implemented per-command or per-query? Or, should authorization be decoupled from the commands?

Secondly, concerning authorization based on permissions. A User should be able to manage the permissions on his Ledgers or on the Ledgers he's been allowed to manage.

  1. Authorization management commands happens in the Ledger

I thought of adding the events/commands/handlers into the Ledger aggregate, such as grantPermission(), revokePermission(), etc. In this case, enforcing those rules would happen into the command handlers. But this would require all commands to include the id of the user who issued that command. Then I would check into the TLedger if the permission exists for that user to execute that command.

For example :

class TLedger{ 
    function addTransactionCmdHandler(cmd){
        if (!this.permissions.exist(user, 'addTransaction')
            throw new Error('Not Authorized');
    }
}
  1. Authorization management commands in the User

The other way around would be to include the permissions into the TUser. A TUser would have a set of permissions. Then, in the TLedger command handlers, I would retrieve the user and check if he has permission to execute the command. But this would require me to fetch the TUser aggregate for every TLedger command.

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. Another domain with service

Another possibility would be to model another authorization domain completely. This domain would be interested in access rights, authorization etc. The accounting subdomain would then use a service to access this authorization domain in the form of 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);
    })

}

What decision would be the most "DDD/CQRS" way?

Best Answer

For the first question I've been struggling with something similar. More and more I'm leaning towards a three-phased authorization scheme:

1) Authorization at the command/query level of "does this user ever have permission to execute this command?" In an MVC app this could probably be handled at the controller level, but I'm opting for a generic pre-handler that will query the permissions store based on the current user and the executing command.

2) Authorization inside the application service of "does this user "ever* has permission to access this entity?" In my case this will probably end up being an implicit check simply by means of filters on the repository -- in my domain this is basically a TenantId with a little more granularity of OrganizationId.

3) Authorization that relies on a transient properties of your entities (such as Status) would be handled inside of the domain. (Ex. "Only certain people can modify a closed ledger.") I'm opting to put that inside the domain because it it relies heavily on the domain and business logic and I'm not really comfortable exposing that in other places.

I'd love to hear others' responses to this idea -- tear it to shreds if you want (just provide some alternatives if you do :) )

Related Topic