Adding Security

Here we are again ready for another installment. This time around I’m going to be looking at the security of the application. What do I mean by that? Really I mean authentication and authorization. This is another one of those things that is far easier to add at the beginning (like the resources from last time) rather than once there is a large code base. For this edition I’m not going to go into the full detail as I have before as I think you know the process I go through now, but I will be highlighting the main areas of interest. Don’t forget that all the source is available from Google Code. To recap, this is my normal process:

  • Create a test that doesn’t compile
  • Add the minimal code so the build succeeds
  • Add the implementation so that the tests pass

Without further ado, here is the new class diagram which contains the changes for this iteration which we’ll talk about below:

Authentication
I’m going to start off with some pretty simple authentication to get the ball rolling. Of course I’m always thinking about improvements and future refactorings, I’m sure we will revisit this area again. I’m currently thinking about storing a user name and password for each user. A user should be able to enter their credentials on a sign in page which submits to an action that authenticates them and stores their user id in the session. Then I’ll create a filter to check the session and load up the user before each action. If the session expires or the loading of the user fails we will redirect to a nice error page and cancel execution of the requested action.

So here is a controller unit test that tests the authentication of correct credentials:

As you can see here I’m calling the repository directly, but I think I’ll be introducing a service layer pretty soon which would be a nicer interface. I will probably increase the security too by only storing the password hash and matching on that instead, but that’s for another day. The next unit test is for our authentication filter:

And here is the implementation of this filter, notice that we add the current user to the controller property bag for convenience:

Finally for authentication, I’ve created a number of acceptance tests that test the filter and security controller. The problem with these tests is that they rely on some test data that needs to be in your database before they pass. So inside the Core assembly is a folder for sql scripts where I’ve added a script for populating the database with test data. Make sure you run this script before running the tests. To make acceptance tests easier to write I’ve created a SignIn method that encapsulates the sign in process that can be called from other tests as all other pages are now protected:

And here is an example of using this method in a simple test:

As I mentioned above, I’ve created a number of acceptance tests so take a look at the source for more examples.

Authorization
For authorization I’d like to define a number of fine grained permissions that control each feature of the application such as CreateIssues, ViewIssues, EditIssues, DeleteIssues etc. Then these permissions could be grouped together into roles. There might be some built-in roles in the system (such as Administrator that have all permissions), but a user should be able to modify the permissions in a role and even create their own roles. These roles could then be granted to a user. The user’s roles (and therefore their set of permissions) should then control which parts of the application they can access. I’d like this implemented in the easiest way possible for the developer to use and should control which controller actions can be accessed and whether or not to show a particular UI element in the views.

How are we going to do this? After some research and experimenting here is my current solution:

The User domain class should implement the .NET IPrinciple interface. This enables us to store the user object anywhere that supports IPrinciple (like the monorail context). I’ve looked into the built in security support in .NET but I had some issues. The main problem was that it made unit testing much harder. I’d prefer to add attributes that do not impact on testing. It required passing literal strings around which isn’t great. The attributes were also a bit hard to read. So I’ll create a custom attribute to mark the actions with the permission that is required. Then I’ll create an authorization filter to check the permissions on an action and for the current user and only allow execution to continue if the user has the authority to.

Looking at the class diagram above, notice that our User class now has a collection of Roles and a Role has a collection of Permissions. A permission is a system entity that we want to code against. So it makes sense for it to be an enum. We will therefore create our own custom mapping to render the permissions list for a role into a comma separated list which can be stored in one field in the Role table (mainly to reduce the sql joins when loading a user). There is also a new method in the User class called HasPermission which will take a Permission (enum) and check whether that user has the given permission.

Here are the User.HasPermission tests that now pass (after going through the normal process outlined above):

I’ve created a number of unit tests for the new AuhorizationFilter. Here is one that tests the normal success case:

I guess the more interesting parts of this is the actual implementation of the custom attribute, how it’s used and the AuhorizationFilter:

You mark a controller action like this:

Here is the AuthenticationFilter that checks the permissions:

As you can see from the filter, if the current action requires a permission we get the current user and check to make sure they have the authority to execute it. If they do execution continues as normal but if they don’t we redirect them to a nice error page. If the action doesn’t require any permissions the filter allows execution to continue. Sweet!

One final part of authorization is hiding UI elements based on permissions. For example, on the issue list page is a “New Issue” link that takes you to the new issue form. Obviously we want to hide this link to users who do not have the permission to create issues. If you remember, as part of our authentication filter we store the current user in the property bag. This allows us to do this:

I’m not a fan of passing literal strings around but I didn’t know of another way in NVelocity. So I’ve overriden the HasPermission user method to accept a string which is converted to a Permission enum. If this conversion fails an exception is thrown which is ok.

So after all that we now have 24 passing tests:

What’s next?
Well I think a service layer will be required plus some improvements to the repository calls we are making. We’ll also need to improve the issues list with paging etc and add some more properties to an issue. Once we’ve done that I’d like to spend some time on the UI and also on how issues are organized. Possibly via Client – Project – Version – Cycle. But more on this later.

As always the full source is available via Google Code. Thanks to BenL for pointing out the missing files etc that I forgot to commit, oops!

Url: http://code.google.com/p/pixelbugs/
Svn Revision: r9

Advertisements

2 Responses

  1. MR has a built in component for handling display of elements based on role membership:

    http://www.castleproject.org/monorail/documentation/trunk/viewcomponents/security.html

    Might be worth a look!

  2. Thanks Ben, I’ve had a look and this component is good if you are using the build-in .NET security and have implemented IPrinciple.IsInRole. I haven’t yet so this doesn’t currently fit. I might create my own version of this component to work with my implementation, but we’ll see how it goes.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: