Setting Up FluentSecurity to Use Ninject for Dependency Resolution
A couple of months ago, I blogged about how to secure an ASP.NET MVC application using the excellent library FluentSecurity. I described how to install and configure FluentSecurity; however, I didn’t go into detail about how exactly you would set up a dependency injection framework which is necessary for FluentSecurity to resolve its policy violation handlers.
In this post, you’re going to read about how to use Ninject to register and resolve concrete implementations for a custom FluentSecurity policy violation handler. I assume that you have at least a basic understanding of the concept of dependency injection (or inversion of control). If that shouldn’t be the case, I recommend you read the Wikipedia article on dependency injection before diving into this post.
#Integrating Ninject into Your ASP.NET MVC Application
Given that you already have an ASP.NET MVC application with FluentSecurity set up, install the NuGet package Ninject.MVC3 to integrate Ninject:
Install-Package Ninject.MVC3
After you have installed the above package, you will find a newly created file called NinjectWebCommon.cs
within your application’s App_Start
folder. This is where you configure Ninject and register the services you want resolved.
#Introducing FluentSecurity to Ninject
FluentSecurity doesn’t magically know that it’s supposed to use Ninject to resolve services. To make use of dependency injection, you have to call one of the following two overloads of the ResolveServicesUsing
method in FluentSecurity’s configuration section:
ResolveServicesUsing(Func<Type, IEnumerable<object> servicesLocator)
ResolveServicesUsing(ISecurityServiceLocator securityServiceLocator)
The first overload requires you to pass it a method pointer for its servicesLocator
parameter. That method you point to has to accept a single Type
parameter and return a collection of services that are registered for that specific type. (You can optionally pass in a second parameter that resolves a Type
and returns a single service.)
The second overload accepts an instance of a class that implements the interface ISecurityServiceLocator
. That interface specifies two methods, Resolve
and ResolveAll
, which have the same signature as the method pointers used by the parameters of the first overload.
You’re free to decide which overload you want to use to tell FluentSecurity where to retrieve the required services from. In the end, both provide the same functionality, so which one you use is just a matter of personal preference: You can either implement an interface specifying the methods necessary for resolving services, or you can directly pass the pointers to those methods yourself.
#Implementing a Custom Policy Violation Handler
If a policy is violated, FluentSecurity tries to resolve a policy violation handler that determines what to do. Let’s take the DenyAnonymousAccessPolicy
, for example. If an unauthenticated user requests an action that is secured by that policy, FluentSecurity denies access to the requested action because the policy is violated. It then looks for an appropriate policy violation handler which knows how to handle the specific violation. To be a match for the DenyAnonymousAccessPolicy
, the violation handler has to meet the following criteria:
- Its class name has to be
DenyAnonymousAccessPolicyViolationHandler
. - It has to implement
IPolicyViolationHandler
. - It has to be registered with the used dependency injection framework.
Please note that the first criterion isn’t required in version 2.0 of FluentSecurity. If you register a policy violation handler called DefaultPolicyViolationHandler
, FluentSecurity will use that handler as a fallback if it cannot find a more specific handler for the policy violation.
A possible policy violation handler implementation for the DenyAnonymousAccessPolicy
could look like the following:
public class DenyAnonymousAccessPolicyViolationHandler : IPolicyViolationHandler
{
public ActionResult Handle(PolicyViolationException exception)
{
return new RedirectToRouteResult("SignIn", routeValues: null);
}
}
Please note that you'll have to register a route named SignIn
for the handler to function correctly:
routes.MapRoute("SignIn", "SignIn", new { controller = "Account", action = "SignIn" });
It's up to you what type of ActionResult
you want to return. Instead of redirecting to the SignIn
action, you could also return a special view for unauthorized access, for example.
#Registering a Policy Violation Handler
Before we dive into the FluentSecurity specifics, let’s have a look at how you register services with Ninject in general.
Open the NinjectWebCommon.cs
file and locate the RegisterServices
method. This is where you register the services for your application. You can also do some advanced stuff in there, such as loading Ninject modules. However, that’s a topic on its own and won’t be covered in this post.
Ninject provides a fluent syntax to bind interfaces to concrete implementations. A so-called kernel holds the services binding information. The binding syntax is very clean and reads nicely:
kernel.Bind<T>().To<TImplementation>();
To apply the interface binding to your custom policy validation handler scenario, you can bind the IPolicyViolationHandler
interface to your DenyAnonymousAccessPolicyViolationHandler
as follows:
kernel.Bind<IPolicyViolationHandler>().To<DenyAnonymousAccessPolicyViolationHandler>();
This solution works fine, but it has a catch: For each new policy violation handler that you create, you will have to bind the IPolicyViolationHandler
interface to the newly created handler to register it with Ninject. That’ll become tedious work, and you’ll have to remember to update your bindings every time. Plus, manually registering violation handlers doesn’t follow the Open/Closed Principle.
Enough with Poor Man’s Violation Handler Binding™ — let’s take a look at a better solution.
#Registering All Policy Violation Handlers in an Entire Assembly
A more effective approach to register policy violation handlers is to scan the current assembly for all types implementing IPolicyViolationHandler
and to register all of them at once. The code for that is fairly straightforward:
private static void BindPolicyViolationHandlers(IKernel kernel)
{
Type handlerType = typeof(IPolicyViolationHandler);
Func<Type, bool> typeIsHandler = handlerType.IsAssignableFrom;
IEnumerable<Type> assemblyTypes = Assembly.GetExecutingAssembly().GetTypes();
IEnumerable<Type> handlersInAssembly = assemblyTypes.Where(typeIsHandler);
foreach (Type handler in handlersInAssembly)
{
kernel.Bind<IPolicyViolationHandler>().To(handler);
}
}
You can simply call the above method from the RegisterServices
method like that:
BindPolicyViolationHandlers(kernel);
With that configuration in place, you can now add new policy violation handlers to your application without any further manual work, such as adding Ninject service bindings. Just keep in mind to name new policy violation handlers according to the naming convention <PolicyName>ViolationHandler
and to implement the interface IPolicyViolationHandler
.