'CQRS and cross cutting concerns like ABAC for authorization reasons
Let's assume a monolithic web service. The architectural design is based on the DDD and divides the domain into sub-domains. These are structured according to the CQRS pattern. If a request arrives via the presentation layer, in this case a RESTful interface, the controller generates a corresponding command and executes it via the command bus.
So far so good. The problem now is that certain commands may only be executed by certain users, so access control must take place. In a three-layer architecture, this can be solved relatively easily using ABAC. However, to be able to use ABAC, the domain resource must be loaded and known. The authentication is taken over by the respective technology of the presentation layer and the authenticated user is stored in the request. For example, JWT Bearer Tokens using Passport.js for the RESTful interface.
Approach #1: Access Control as part of the command handler
Since the command handler has access to the repository and the aggregate has to be loaded from the database anyway in order to execute the command, my first approach was to transfer access control to the command handler based on ABAC. In case of a successful execution it returns nothing as usual, if access is denied an exception is thrown.
// change-last-name.handler.ts
async execute(command: ChangeUserLastNameCommand): Promise<void> {
const user = await this._userRepository.findByUsername(command.username);
if (!user) {
throw new DataNotFoundException('Resource not found');
}
const authenticatedUser = null; // Get authenticated user from somewhere
// Handle ABAC
if(authenticatedUser.username !== user.username) {
throw new Error();
}
user.changeLastName(command.lastName);
await this._userRepository.save(user);
user.commit();
}
However, this approach seems very unclean to me. Problem: In my opinion, access control shouldn't be the responsibility of a single command handler, should it? Especially since it is difficult to get the authenticated user or the request, containing the authenticated user, at this level. The command handler should work for all possible technologies of the presentation layer (e.g. gRPC, RESTful, WebSockets, etc.).
Approach #2: Access control as part of the presentation layer or using AOP
A clean approach for me is to take the access control from the handler and carry it out before executing the command. Either by calling it up manually in the presentation layer, for example by using an AccessControlService
which implements the business security rules, or by non-invasive programming using AOP, whereby the aspect could also use the AccessControlService
.
The problem here is that the presentation layer does not have any attributes of the aggregate. For ABAC, the aggregate would first have to be loaded using the query bus. An ABAC could then be carried out in the presentation layer, for example in the RESTful controller.
That's basically a good approach for me. The access control is the responsibility of the presentation layer, or if necessary an aspect (AOP), and the business logic (domain + CQRS) are decoupled. Problem: The main problem here is that redundancies can arise from the database point of view. For the ABAC, the aggregate must be preloaded via query in order to be able to decide whether the command may be executed. If the command is allowed to be executed, it can happen that it loads exactly the same aggregate from the database again, this time simply to make the change, even though the aggregate has already been loaded shortly before.
Question: Any suggestions or suggestions for improvement? I tried to find what I was looking for in the literature, which was not very informative. I came across the following Security in Domain-Driven Design by Michiel Uithol, which gives a good overview but did not answer my problems. How do I address security concerns in a CQRS architecture? Or are the redundant database access negligible and I actually already have the solution?
Solution 1:[1]
I would handle authentication and the overall authorization in the infrastructure, before it reaches the command handlers, because it is a separate concern.
It is also important to handle authentication separately from authorization, because there are separate concerns. It can become pretty messy if you handle authentication and authorization at the same time.
Then you I would do final authorization in the handler (if needed), for example if you have a command AddProductToCart, then I would make sure that the user who initially created the cart-aggreagate is the same as the one making the AddProductToCart command.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tore Nestenius |