Thursday, May 04, 2006
I recently had to help a team who had problems with their role based security implementation. They weren’t sure where to locate role based security checks in their application. Their current design wasn't working. They held users, roles, use cases and method names in a database. The method names described web service methods and they mapped onto particular use cases that applied to particular roles. Each user was assigned a role. This way they could do a lookup as each method was called and find out if the logged on user was allowed to run it. If they weren't, the web service threw an exception, which the application caught and used to alert the user that they were doing something illegal. Of course, it's better to enforce role-based rules at the UI. A button that the user isn't allowed to click should be invisible to them, or disabled, rather than allowing them to click it and get an error message. To that end, the other part of their security mechanism linked use-cases to control names, so that controls could be enabled or disabled depending on whether the user belonged to a role assigned to that control's use case. There were several problems with this model. The first and most obvious one was that there were two different mechanisms to do the same thing; the method access based checks and the control-based checks. Both enforced access rules to certain bits of functionality, but had two different implementations and two different schemas in their security database. There was ample opportunity for support people to configure conflicting rules for the UI and the web services. The second problem, and probably a more fundamental one was the assumption that use cases mapped exactly to web service method calls. For example, my method call, 'Save Customer', would map to a particular use case. But what if one use case said that only an admin person could change a customer's credit status, while another one said that a clerical user could change a customer's address. Does it make sense in our application design to split Save Customer into several use case based calls? Probably not. There was a real danger that the security design would have driven out a very awkward physical implementation. A further assumption was that they wouldn't have data related security requirements. For example, say that the accounts department could only update one type of customer, but another type could only be updated by the call center. The customer type is defined by a property of customer, but of course they both use the same Save Customer call. And imagine the customer types can be user defined, there's no way you can implement that with a static security model. I think the fundamental problem with the whole approach was not seeing role based security roles for what they are: business rules. Just like a customer's credit limit, whether a user of a particular role can edit a customer is a business rule. Like all business rules in our Domain Driven application design, the role based security checks should be implemented in our Business Entities (Business Objects, or whatever you call them). If the rules are expressed as properties of our domain entities, Customer.CanEdit, for example, you can both bind them to the UI, by binding the CanEdit property to the Save button's Enabled property, and apply them at lower levels of the application architecture. You could query Customer.CanEdit in your Data Access Layer before running the customer update, or query the same property in your web service. Putting rule based security checks in your domain objects also makes sense from the point of view of application design. Since they are business rules they sit nicely alongside the other rules that apply to a particular entity and can interact with them if need be. You can code rules that maybe intersect a user rule, a property of the entity and another entity. Best of all you can do it all in the same place.
Posted by Mike Hadlow at 9:06 am