15

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?

  • 1
    Great question -- I've been trying to tackle similar issues and none of the literature seems to address it directly. I was a little confused by the second half of your question. It sounded like you were wondering about where to put management of permissions (adding or removing permisions) but the examples shown are for adding a transaction, so it looks more like the second half is asking "how should I query permissions". Can you clarify that part please? – emragins Nov 6 '15 at 19:04
  • Each transaction could have execution policy. Each user should belong to one ore more groups, each group would have an access profile specifying which transactions are allowed. At run-time, before executing a transaction, the policy is checked against the aggregated profiles for the executing user. Of course, this is easier said than done. – NoChance Feb 4 '16 at 23:02
5

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 :) )

  • I think you have some valid points regarding different "layers" of authorisation. A system I was working on had different types of users - registered users and staff members. The command/query handler permissions did a basic check on the user type. If it was staff, it always went through. If it was a registered user then it was only allowed if certain conditions are met (e.g., permissions on an aggregate). – magnus Jan 20 '16 at 21:59
0

I would implement authorization as part of your Authorization BC but deploy it as an action filter into your Ledger system. This way, they can be logically decoupled from each other - your Ledger code should not have to call Authorization code - but your still get high performance in-process authorization of each incoming request.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.