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?
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With