Security Considerations
Security is always an annoyance, on the one hand, you really need to do something about it, on the other hand, the moment you do, the entire application breaks. It is often useful to build the security infrastructure at the first stages of the application, but that is not always possible.
For various reasons, I just now got to the point where I could deal with security, and I had a radically different set of requirements than I had at the beginning of the project. That left me wondering how to ensure that I didn't forget to add security in the proper places.
A while ago I wrote an article that has shown how you can add generic security features directly into the data access layer. Security was very simple, you literarily could not break the security, because the data access would filter / reject anything that you didn't have authority to do.
The problem with this approach is that it assumes something about the domain. Let us say that I use the usual CRUD permissions. Now, I have business rules (themselves entities) that can be attached to a policy. A user has access to view / edit the policy, but is not allowed to view /edit the associated business rules.
Can you guess what happened when you turn on the security mechanism?
Ouch! The system would behave as if the business rules did not exist if (and only if) the user didn't have permissions to edit / view the business rules. This created the amusing situation where the admin is able to test the business rules but the user's actions completely ignore them. The client wasn't amused, and I learned an important lesson.
I am currently using a two tiers approach for security, I have the authorization service, which contains the logic for allowing/denying permissions from the user:
Note that the authorization service has a lot of methods per scenario. The idea is that all the methods call to the IsAllowed() method, but it leaves me with a much nicer syntax to use. I don't have enough permissions that this approach would be a problem.
Then, in my abstract controller, I have defined:
///<summary>
/// Validate that the user is allowed to use this controller or
/// this use case
///</summary>
public abstract void AssertUserHasPermissions();
Now, I must take action in each and every controller, and decide how I would treat the security implications at each use case.
Here are a few examples:
// In: ChangePasswordController
public override void AssertUserHasPermissions()
{
// Well, anyone can change their password
// nothing to assert
}
// In: BaseAdminController
public override void AssertUserHasPermissions()
{
if(AuthorizationService.IsAdmin==false)
base.AccessForbidden(Resources.YouAreNotAnAdmin);
}
// In: PolicySearchController
public override void AssertUserHasPermissions()
{
// well, basically anyone can try to search for policies
// security here is done by the finders for each scenario.
// AbstractPolicyFinder has AssertSearchingForAsscoiatedCustomersOnly
// and FilterSearchResultsBasedOnAuthorization methods to handle the
// security there.
}
The advantage, I am making explicit the security decisions that I am making, and I make the action very explicit.
It does mean that I am going to return tomorrow for an application that is compromised of a login page and an access denied page, though :-)
(One thing that I would like to recommend is to always have a usable (not necessarily pretty, but usable) UI for the security, and ton of logging.)
Comments
Oren,
Great post, ok, I had to read it twice.
Can you explore this more? I remember reading a post you made, I believe about CRUD access, and you had a nice user / group / role scenario going.
In your code you are doing this in a controller, looks like MVC. Lets assume this is a smart client where you need to also secure the service.
My thoughts have been to have:
1) Security on the client, filtering available actions.
2) Security in the service, doing the same filtering.
3) CRUD security in the DAL as a catch all.
Maybe number 3 is overkill, and possibly problematic?
One additional bit of complexity, lets say you have a query object that allows you to grab all customers who have ordered X in Y days. In other words you do not have an explicit method for this, but a generic query object.
The CRUD security is nice because sensitive (you are not allowed to view this) fields would not be returned as the query would check for read permissions on all requested attributes and not return ones for which you have read permissions.
What are your thoughts?
Number 3 is problematic because of the issues I have outlined above.
The security would be either in the query itself (give me all customers who have ordered X in Y days that are associated with the current user) or with the controller.
I am doing MVC in a web app, btw.
In the WinForms app that I work on, we use the approach you first describe, but only for read-access, and only for "root" objects. In this respect, security is an "aspect" that works no matter how you call the DAL.
Any more than that, and the problem (for us) becomes that our own business logic cannot do what it needs to do unless the user has permissions. For example, part of the process of attaching an Address to a Customer might be that another record is created (a log entry maybe) in some other location. It does not matter that the user cannot create log records manually - the log should still be created. Sure, we can work around the problem, but it ups the complexity, lowers testability and increases the bug count.
The next layer of security is at a higher level, both filtering available client actions (in a similar way to what you described), and hiding data. When the security is close to the UI, it can be defined in terms of the UI (screen-access security). That level of context is not available in the DAL. It also lets the users define security in a way that makes sense to them.
The remaining problem is for other modules of the system that do not use the same framework as the main UI. An example is reports. However, reports are their own beast anyway - a summary report may ignore individual record security, and a detail report should definitely not. I am still struggling with that part.
Oren,
This is an excellent post. I will be using your Assertion idea on the controllers that you used. I like how readable that is and it's very intuitive.
I just got to a point on one of my larger projects that I need to implement really granular permissions. Up to this point it was just checking if they were authenticated or not through a IAuthenticationService like you use above, and checking if they are in one specific role or not.
I was starting to use PrincipalPermission attributes on the actions, but this is starting to become a little messy. Then I just started setting the PrincipalPermission attributes on the controllers themselves, although in some cases certain parts of a controller can be accessed by all roles, while others can only be ran by specific ones. That is where it becomes to sprinkle the attributes through my controller. Another thing I need to do is add more logging as you stated. Log4Net is in place, I just need to add entries under the denied actions which should be fairly easy early on.
Do you have any reccomendations on how to handle more complex scenarios like that?
Sean,
Complex scenarios calls for more granular level of security, so I would generally note that in my assert, and act upon it elsewhere.
I may have something like:
[AllowOnly("Admins")]
public class MyController
{
[AllowEveryone]
public void Login()
{}
}
But if this gets more complex than that, maybe it is time to split the container?
I just found another idea to store object based permissions..
http://www.codeproject.com/useritems/usermgmt.asp
Comment preview