6

I'm working on implementing my first clean architecture and CQRS application, I've stumbled across a bit of logic that's leaving me a bit stumped. I'm working on integrating authorization and authentication into my application, but I'm having some trouble trying to figure out how to architect a portion of my application that will handle command validation. My system is multitenant and currently shares a database across all of my clients, and I need to implement some sort of system that will enable me to perform all of my verification in a way that is straightforward and not too tightly coupled.

In my system, I have a implementations for both Users and Clients. Users can belong to any number clients. These users also have varying levels of permissions granted to them (but this is managed by my roles systems and mostly irrelevant here). My WebAPI is designed in such a way that I limit authorization based on a fixed number of Roles and Claims. These largely remain static, so I don't need much flexibility here.

My main source of headache is determining "User-Client" interaction permissions. Namely, I want to determine if a user has access to client that they're attempted to update records for. One approach would be to add UserId to all of my commands and to individually check the permissions during each command and query. This seems tedious and prone to issues.

Another approach I considered was to define an interface or base class that merely added the client and user Ids to any object in which they were required, but this had the adverse affect of exposing those implementations to my WebAPIs (via swagger and the UserId/ClientId being a portion of the Request/Command object).

One final approach would be to make my underlying commands still implement those interfaces, but have my controller contain minimal logic to map an API request into one of my command objects. Again, this would be tedious and would start leaking my logic into my controllers.

Overall, it's as if I need some additional structure in my application in which I can pipe any object containing a ClientId into so that I can keep the Authentication and Authorization logic out of my core app. However, I'm mostly stumped, and I'm looking for ways that I can simplify my application by minimizing the overhead of adding client-dependent commands and queries.

If it's of any relevance, some of the core tools and technologies I'm leveraging are:

  • .Net Core 2.1
  • Mediatr
  • Entity Framework

And my core application implements commands in a similar fashion to:

public class CreateProductCommand : IRequest
{
    public int ClientId { get;set; }
    public int ParentProductId { get; set; }
    public string ProductName { get; set; }
    // other creation specific props here
}

public class CreateProductCommandHandler : IHandler<CreateProductCommand>
{
    public async Task<Unit> Handle(CreateProductCommand command)
    {
        // check parent permissions, make sure parent product
        // belongs to client who is entered. 
        // -----
        // rest of logic to save

        // -- Ideally the User-Client check would happen before
        // -- the command is ever sent to the handler, so that
        // -- only client-specific logic and permissions are checked.
        // -- As long as the user can edit the specific client, anything
        // -- that happens to the client is determined by standard business 
        // -- and domain logic.
    } 
}
6
  • 1
    Despite the title and the tags I don't see any Clean Architecture, or CQRS concerns here – candied_orange Nov 28 '18 at 5:23
  • But why don't you get the current authenticated user from the session or similar? – Constantin Galbenu Nov 28 '18 at 7:45
  • @ConstantinGalbenu I will very likely get the user from the session, my question is about finding a good approach to authorize a user in my business logic without explicitly including them in my commands. I don't want my users passing their own UserIds into my commands, but I also don't want 2 separate commands (one from the user to the api, and the api to the application layer). – JD Davis Nov 28 '18 at 14:51
  • @JDDavis but why do you need to put the userId in the command if you can get it from the session? – Constantin Galbenu Nov 28 '18 at 16:08
  • @ConstantinGalbenu my controller actions are incredibly slim and basically pipe commands directly to my application through mediatr like so: Task<IActionResult> Product(CreateProductCommand command) => Ok(_mediatr.Send(command);. I'm trying to keep as much logic out of my controller as possible. – JD Davis Nov 28 '18 at 21:41
7

Looks to me like your issue is:

I have a thread acting on behalf of a user. I don't wish to pollute my nice clean business logic with authorisation details. But my data store needs authorisation details to enforce control of the data.

From your description it sounds pretty critical that User A does/does not have access to Client B. That sounds very much like a Business Concern. So the first answer is its pretty obvious: Your business logic should care about which user is doing what. Passing the user details through the appropriate business logic directly is the clean way to handle this. This gives many positives: its clear, its clean, its easily tested, and the API is not coupled to the Data Store.

If need be there will be a point in your Business Logic where you can translate from talking about a User, to specifically talking about Authorised to do X. Also there is no reason why this User information could not be made more anemic, or enriched in different parts of your Business Logic.

Pragmatically, if the thread only serves one User at a time, use Thread Local storage and shove a reference to the user there. Later in your data store (aka wherever you need it) access that reference. This is a dreadful solution. It enforces direct coupling between API and Data Store (the recipient location), the Business Concern is not in the Business Logic, and you have a non-intuitive, indirect argument, that may not be set or cleaned up properly affecting future calls on the thread. In short what you make back from tedium, you will pay back over and over again in bugs and change resistance.


Added extra answer

Possible Solutions

  1. Attribute with some Aspect-Orientated programming.
  2. an Abstract Class/interface with some Aspect-Orientated programming.
  3. A Generic class that wraps a lambda/interface.
  4. a Meta Program that constructs an Abstract class for derivation.
  5. a Meta Program that constructs a tailored class which wraps a lambda/interface.

Solutions 1 and 2 uses your languages aspect orientation (C# attributes) to detect the "authorisation required" functions, and intercept calls to them. It will throw an exception on unauthorised, but permit the call if authorised.

class command
{
    public command(User user);

    public User user {get; }

    [AuthorisedFor("xyz")]
    public void action(object a);

    [AuthorisedFor("xyz")]
    public void action(User user, object a);
}

Solution 1 requires you to directly decorate the authorisation required functions, and provide an properties/arguments for user information.

Solution 2 allows you to pre-specify the authorisation required for the standard functions, and provide the properties/arguments for user information.

Solution 3 is a decorator and captures through its constructor the knowledge about authority required, the permit and deny functions. The only issue is that the Command interface will either force you to use Object arguments, a Generic Type and a limit on arguments, or require copies per downstream command interface.

class command<T>
{
    public command(User user, Authority[] required, Action<T> permit, Action<T> deny);

    public void action(T arguments);
}
class command2
{
    public command(Authority[] required, SomeInterface permit, SomeInterface2 deny);

    public void action();
}

Solutions 4 and 5 are just not easily achieved in C#. You would essential need to write code that JIT's a new Base Class and derivations, or decorator classes for the various interfaces. I only include them for completeness.

The run-time flexible options are 3 or 5. The other solutions 1, 2 or 4 are more reasonable for compile time.

User and Authorisation Passing

Orthogonal to which solution you pick for handling permit/deny, those solutions will need access to the user/authorisations. These could be stored in various forms of storage:

  • global
  • thread local
  • object property
  • function argument

Picking Global or thread local is going to give you the implicit user passing you desire, it will however complicate testing and be a source of non-intuitive bugs.

Picking an Object property or a function argument is going to require that you pass the user/authorisation information through your commands. It will make the information requirement explicit, simplify testing, and reduce the capacity for non-intuitive bugs. It will require more typing.

My preference is for explicitly passing the user/authorisations down. However there may be a reason that makes the implicit Global/Thread local option the better choice.

8
  • "From your description it sounds pretty critical that User A does/does not have access to Client B. " This is the quintessential difference between authentication and authorization. Authenticate to enter the restricted area (basic user login). Authorize any action taken within restricted area (business). – Neil Nov 28 '18 at 7:57
  • Yes someone accessing the system would have to Authenticate that their Identity is indeed User A. Then and only then would their Authority be known to Permit or Deny particular Actions, and/or access to Data. The point is that for the Data Store to Permit or Deny it needs the Authenticated User's Authority, and perhaps even their Identity for auditing purposes. This necessitates that relaying and acting on this information is a concern of the code between the API and the Data Store. Excepting some edge-cases, that means a Business Concern implemented in Business Logic. – Kain0_0 Nov 28 '18 at 8:33
  • I am perfectly fine with handling my authorization logic in the application layer, I'm just trying to find some strategy that allows me to authorize my users that doesn't require me to explicitly include the users in my command object. I'm envisioning some sort of interface that my commands can implement and have them go through a validator of sorts with the user being one parameter and the interface be the other. But I would also like it to be more of an automatic thing that simply runs on any command with that interface. – JD Davis Nov 28 '18 at 14:53
  • so in my example (stackoverflow.com/questions/54694332/…) would you recommend having the command itself do the auth-ing? as the auth is part of the business logic to decide if they are allowed to continue executing? or would you still suggest an auth decorator that intercepts the needed parameters to do the checks? – user78252 Feb 14 '19 at 17:13
  • @RhysW I would recommend orthogonality, how that is best implemented depends largely on the code base you have and how you wish to architect it. I would argue against using global/thread storage to identify the user because it makes it harder to perform task dispatch/thread pools and a number of other techniques where process/threads swap between authentication levels. As for Commands, I believe that they should be associated with an Authority. That could a guard clause within the command, a factory that only returns appropriate commands by authority, or an attribute with a pre-execution hook. – Kain0_0 Feb 17 '19 at 23:17
1

Create IPermissionValidator that you inject from command's constructor using dependency injection. When you need to check permissions for CreateProduct you call _permissionValidator.ValidateCreateProduct(command.ParentProductId);. If user doesn't have permission it will throw exception PermissionValidationException that your web api will catch and return correct HTTP status code. If you need to way to check if user has permission and not throw, add _permissionValidator.TryValidateCreateProduct(command.ParentProductId); that returns bool true if it's valid. IPermissionValidator implementation can use dependency injection to get current user and validate accordingly.

This also makes your commands easily testable, because you can implement own IPermissionValidator for tests. You should tests all cases of validation in your commands.

0

I have a very similar setup.

aspnetcore 3.1 performs our authentication—windows authentication—providing Roles and Claims. This is part of our Presentation layer. The identity, is provided to the application layer and matched to application roles.

Be aware, that there are multiple concerns when performing authorization (in CQRS, I would call it command validation).

For example, I have three user roles, ['drafter','reviewer','administrator']. The drafter can create an in-draft document and submit it—changing state to in-review, and a reviewer can approve any items in-review but not see anything in-draft except their own. Where an administrator can see everything—and perform most actions, but not approve/reject anything.

The validation gets complicated, we want to only show accessible objects to each person, but then also reject operations on inaccessible objects for security reasons. Here is some psuedo-logic on a delete validation:

onDeleteValidate(user, docId) {
  let document = _db.GetDocument(docId);
  let isValid = false;

  if (user == null) return false; // fail fast

  if (document.ownerId == user.id && document.status == "IN-DRAFT")
    isValid = true;

  if (user.roles.includes("ADMINISTRATOR"))
    isValid = true;

  return isValid;
}

This kind of code would live as a pipeline filter/command validation in CQRS. However, you can plainly see that the complex logic—e.g. drafters can delete anything they own that's IN-DRAFT—requires that the validation know details about the objects and defines a relationship between the user and the object.

Be aware, that this clearly delineates authentication, where we receive an authenticated identity, and authorization which defines what that identity can do. Additionally, I perform a generic validation against my whole web-api: "is the authenticated user authorized for anything", but the rest needs to live in my application and domain layers.

Another example would be aspnetcore mvc authorization. They use an "identity, object, action" and the validator is basically asking, "can perform to ", which is similar abstraction to my pseudo validator above. See https://docs.microsoft.com/en-us/aspnet/core/security/authorization/secure-data?view=aspnetcore-5.0

Finally, I worked on a complex app that would provide available commands based on the validators. So if you retrieved a document, the security would say "What commands can this perform on this ". The front end could use that to display or hide components pertaining to that document, and the backend could use that to validate action.

One of the important ideas to keep in mind with clean architecture is: "If I rip out this layer and put another one in, will that affect any other layer?" The answer should be "no". I've developed console presentation layers along side web-api presentation layers and switched from Entity Framework persistence layer to Dapper persistence layer as a proof of concept.

Hope that gives you some ideas.

3
  • I've actually massively expanded our architecture and continue to do things in a similar fashion as you do. I did end up building a piece of middleware for the CQRS pipeline that checks for the presence of a custom attribute, and if the attribute is there, it determines if the user has adequate permissions to access the resource. I've also leveraged query filters in EF Core to accomplish similar things. – JD Davis Feb 24 at 22:57
  • How is the developer experience? I haven't fully committed to CQRS because we have to boilerplate 3 files for each command and query with a sum of ~75 lines of code. – Nathan Goings Feb 25 at 3:42
  • There is quite a bit of boilerplate, but it can be simplified by building your own code-generators or with extensions to your IDE. Overall, it's made our applications easy to understand, extend, and test, so I think the trade-off is worth it. I've seen some developers take to just having their request, handler, and validator all live in the same file, but that's a bit cluttered for my tastes. – JD Davis Feb 25 at 15:49

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.