Due how the security of the ASP.NET framework is setup, very frequently ASP.NET developers will have to write custom security controls for their applications. A classic is example is roles/permissions. How do we extend what the framework offers to respond to business needs while keeping the previous features functioning?
Reviewing the current implementation
Your current implementation is probably based on
Role Providers which's what everyone else is doing. That said, you're probably using a
standard solution like this one where you specify a role to the
AuthorizeAttribute:
[Authorize(Roles = "MyRole")]
Assuming you implemented your
RoleProvider correctly authorization should work out of the box.
A custom requirement
The problem is that custom requirements require a more granular solution that cannot be accomplished with Roles because it would require us to write stuff we don't need and doesn't satisfy the business requirements. That's what we get when using an out of the box solution. So, most developers, would probably end up writing custom code.
Permission requirements
The required permissioning model is basic. We have to support the concepts of
User, Groups, Roles, Permissions (or Claims) and the traditional relationships between them.
Users can view everything but only be able to act on certain contexts.
In terms of development, the following use cases derive from it:
- The web app should controls accordingly (that we don't want to do this with JavaScript);
- Each endpoint if the user is authorized and has permissions to call that endpoint;
- There should be a mapping users -> groups; groups -> roles; roles -> permissions;
- When users log in, we need to load their permissions;
- Every message sent to the backend should have a SubmittedBy and that should be validated to guarantee that the user had access to that feature.
- Reject the request if he doesn't have the permission (either in the front or in the backend);
- Log SecurityExceptions or what is relevant for you.
Introducing Claims
Claims are granular permissions linked to resources in your application. You could use it rather than relying on role based permissions. That will allow you to control in detail the access to resource/action rather than having a multitude of roles on the system. I personally like to use a mixture of both so I can use the standard authorization features available in ASP.NET MVC with roles, reading the granular claims/permissions on the more complicated rules.
The Solution
So let's review the solution. I broke it in four parts:
- The basic Infrastructure - the code necessary to provide access to the permissioning system, validation, constants, etc.
- ASP.NET - secure the web app so that all actions are authorized;
- Backend - validating if the requests reaching the backend also respect the same concerns already printed in the previous layers;
- Refactotings - you will probably be required to refactor your framework to apply authorization in certain parts of your application. Or even, creating a generic application filter to auto-process every command sent to it. Topic for a future blog post.
Before writing code though let's not forget some to review some good design practices:
- Code Reuse - centralize your roles in one single class in an assembly so everyone talks the same dialect
- Use consts / readonly strings - please, don't propagate hardcoding your permissions everywhere on your code
- Write some extension methods / helper classes so you respect the single responsability principle
- Performance - I used Dependency Injection to inject from the DB the permissioning configuration into my PermissionController class in case it would be loaded only once and kept in cache for the duration of the application. In case you forgot it, just set some code in your Application_Start as it's your MVC's composition root
- Review security principles
- Write tests
The Basic Infrastructure
The static init method is used to initialize our framework. It gets roles and groups from the db, memory or whatever you're using. That way, the logic is db-independent. Meaning it can be reused everywhere, is lightweight, can be easily cached and, performant by default.
The other methods are pretty self-descriptive. Interesting is that, because this class is so lightweight, it can be reused trough all your assemblies, tests and even different projects without overhead. And it's also easily extensible but still closed for modifications respecting the
SOLID principle.
Initializing the Framework
- ASP.NET - global.asax;
- A console Application - your static void main;
- Backend - varies according to the technology.
The composition root for my ASP.NET web app is the
Application_Start method. So, the initialization happens like this:
Let's now take a look at how to secure our app. Essentially we want to be covered on:
- Views - render/hide stuff in razor according to user's permissions
- Controllers - authorizing/revoking access on controllers and actions
- ActionFilters - to auto-validate content that's being injected.
Securing our Views
The first obvious step is to hide content user isn't supposed to see. Remember, we should not do this via javascript as
client-side security doesn't work. In order to hide content, we need to tell Razor not to render the content if the user doesn't have access to some resource. That could be done simply like this:
Here, I have a few interesting points:
- using an extention method in my Razor to reduce duplication of code;
- Moved all my permissions to the "Permission" class - to avoid hardcoding permission names troughout the application;
While it's true that the above code would work, more interesting would be trying to implement it elegantly by extending the HtmlHelper to create custom methods such as SecureLink and SecureButton and using those controls in Razor:
The advantage of this approach is to simplify your Razor markup and reuse the logic to validate like this:
@Html.SecureButton("Edit", "btn", Permissions.Edit)
The RequirePermission ActionFilter
In order to secure our controllers, we'll need three things:
- create an attribute to auto-validate requests to a given action/controller;
- secure your actions/controllers trough application filters;
I created the
RequiredPermission Attribute to validate, as an ActionFilter all the requests issued to my app. It looks more a less like:
Adding Authorization to the Views
With all of that out of the way we can now decorate our actions like this:
Take a look at the
RequirePermission ActionFilter above. With one liner in the controller, we got the validation working in our ASP.NET application using our custom security implementation. Now, it's just a matter of manual work to decorate our actions with the validations you want. Remember that we did all this work because we now require granular permission validation.
Securing the Backend
Because we separated the concerns in our application, delegating to the
SecurityController all the logic to validate permissions against user, our backend, whatever it is, can re-utilize it.
Conclusion
On this post we analyzed a custom implementation for securing ASP.NET applications. There's a lot more to talk about this topic that will be reviewed in future blog posts. Keep tuned!
See Also