CQRS applying cross cutting concerns such as security
Asked Answered
N

2

5

Suppose I have a complex system where there large trees of people. Simple thoughts are employees / manager relationship, many employees report to one manager. Now in addition to manager there are support staff that are capable of acting on the behalf of the manager can manipulate the managers' employees.

In a CQRS system how would you model a message for a hypothetical action of "edit employee" where the invoker of the action is a support staff. The action can only succeed if the staff member as per the manager security relationship is acting upon an employee in their realm.

Verifying the security of this would involve querying the database to validate that the person being modified is indeed inside the employee chain of that manager.

Where would this query occur? Prior to originating the "edit employee" message?

If the data is upfront validated before originating the message, in an eventually consistent system suppose that before the "edit employee" message has been processed a separate action has occurred that would have removed the authority of the user to complete the "edit employee" action. If the the command handler doesn't validate the security concerns of that message, the message would still succeed even though the user no longer the authority to execute it.

This would seem to imply that double sided validation, similar to UI validation & server side validation would be the best course of action. However the method of completing that validation seems as though it would violate key tenets to CQRS.

What approach(es) are best when having to deal with these and other similar cross cutting concerns when using CQRS?

Nicolis answered 25/1, 2012 at 21:30 Comment(2)
IMO there is no general answer... I would always validate on the command handler side at least and optionally "up-front" (which could be in the part that accepts a message into the queue)Ungrateful
I also think it is important to distinguish between true cross-cutting concerns like authentication, simple authorization (is this user allowed to perform an action of this type), from Business Rules that govern whether something is allowed for a specific entity.Accost
A
4

I'd probably skip CQRS entirely for this domain and have the web tier talk directly to the DB tier (no messaging). Simple optimistic concurrency should handle the few conflicts that would happen.

Accost answered 26/1, 2012 at 11:59 Comment(7)
Can you explain your thought process as to the why of your first statement?Nicolis
Because it's the simplest solution that could possibly work :)Accost
That most certainly IS true, however if simplest was always the best course of action there would be no existence of the CQRS pattern at all?Nicolis
@ChrisMarisic simple and naive are not the same. Being able to deal with a pure read model without the necessity to wade through the complexities of a complicated business model can be very simple in comparision to the alternative. It can also be simpler to be able to model business behavior without having to figure out which of the object's getters are really needed to enforce business rules and wich are only there for one of the many views.Generally spoken, mixing read and write can (and often does) lead to an intangible mess which is very far from simple.Decoration
@DennisTraub I'm not really sure what point(s) you were trying to convey there, can you rephrase some of your comment?Nicolis
@ChrisMarisic what I'm trying to say is that patterns like CQRS can help in making complex systems simpler.Decoration
+1 for Udi's statement. Just query the DB to determine if to a particular activity is allowed. Depending upon the complexity of your employee trees, Joe Celko has an excellent book on working with trees in SQL: Trees and Hierarchies in SQL.Yorgo
Z
5

First, I agree with @Yahia's comment that there isn't a general answer. With that said, here's how I'd approach it.

To begin with, I would probably go with the double validation -- once in my controller when the request was first received, and then later in my domain as it's processing the command. Some may not agree with that, but I would rather prevent a command from being issued and letting the user know immediately that they were not authorized to perform some action rather than having the command go through and rely on eventual consistency for some error notice to alert the user after the fact that they could not perform the action.

So, in terms of pseudo-code, here's my approach to Edit Employee:

Controller

[HttpPost]
ActionResult Edit(Employee emp){

  //get employee org information from _employeeRepository
  //validate if _loggedInUserID is able to edit emp.ID

  if(isValid) {
    //construct command
    _commandService.EnqueueCommand(new EditEmployee(emp.ID, emp.Name, emp.Salary));
  } else {
    return View("PermissionError");
  }

  return Redirect("EmployeeProperties");
}

So here my command service picks up the command and routes it to the appropriate AR in my domain, which would be Employee.

Employee Domain

protected void EditEmployee(userID, employeeID, employeeName, salary){
  //get employee org information from _employeeRepository
  //validate if userID is able to edit employeeID

  if(isValid) {
    //apply event
    ApplyEvent(new EmployeeEdited(userID, employeeID, employeeName, salary));
  }
}

So I would apply the same security check in both my controller and in my domain. I would have this, probably, as an encapsulated method (well, probably an encapsulated criteria class that I would pass to the repository).

So I hope this helps out with how I would approach this scenario. Let me know if there are questions and I'll elaborate in my answer.

I hope this helps. Good luck!

Zia answered 26/1, 2012 at 1:50 Comment(1)
Why would you apply it at both levels? Isn't the domain layer the point where everything should pass through and thus concerns such as security and validation should be handled there??Melisamelisande
A
4

I'd probably skip CQRS entirely for this domain and have the web tier talk directly to the DB tier (no messaging). Simple optimistic concurrency should handle the few conflicts that would happen.

Accost answered 26/1, 2012 at 11:59 Comment(7)
Can you explain your thought process as to the why of your first statement?Nicolis
Because it's the simplest solution that could possibly work :)Accost
That most certainly IS true, however if simplest was always the best course of action there would be no existence of the CQRS pattern at all?Nicolis
@ChrisMarisic simple and naive are not the same. Being able to deal with a pure read model without the necessity to wade through the complexities of a complicated business model can be very simple in comparision to the alternative. It can also be simpler to be able to model business behavior without having to figure out which of the object's getters are really needed to enforce business rules and wich are only there for one of the many views.Generally spoken, mixing read and write can (and often does) lead to an intangible mess which is very far from simple.Decoration
@DennisTraub I'm not really sure what point(s) you were trying to convey there, can you rephrase some of your comment?Nicolis
@ChrisMarisic what I'm trying to say is that patterns like CQRS can help in making complex systems simpler.Decoration
+1 for Udi's statement. Just query the DB to determine if to a particular activity is allowed. Depending upon the complexity of your employee trees, Joe Celko has an excellent book on working with trees in SQL: Trees and Hierarchies in SQL.Yorgo

© 2022 - 2024 — McMap. All rights reserved.