Ninject Binding Attribute to Filter with Constructor Arguments

asp.net-mvc-3attributesdependency-injectionfilterninject

I read as many answers as I could for this, but they seem to fall short of one detail.

The trouble is when binding an action filter (with a service injected by controller) to a corresponding attribute, I've been unable to figure out how to pass parameter/property values from the attribute to its bound filter. Below is the code, and below that my intended fake-code:

Filter & Attribute

public class AuthorizationFilter : IAuthorizationFilter
{
    private readonly IAuthorizationService _authorizationService;
    private readonly UserRoles _requiredRoles;   // Enum

    public AuthorizationFilter(IAuthorizationService authorizationService, UserRoles requiredRoles)
    {
        _authorizationService = authorizationService;
        _requiredRoles = requiredRoles;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Session == null)
            HandleUnauthorizedRequest(filterContext);
        else {
            var authorized = _authorizationService.IsUserInRole((UserSessionInfoViewModel) filterContext.HttpContext.Session["user"], _requiredRoles);
            if (!authorized)
                HandleUnauthorizedRequest(filterContext);
            // else TODO: deal with cache... 
        }
    }
}

public class RequireRolesAttribute : FilterAttribute
{
    public readonly UserRoles RequiredRoles;

    public RequireRolesAttribute(UserRoles requiredRoles)
    {
        RequiredRoles = requiredRoles;
    }        
}

Filter/Attribute Bindings

kernel.BindFilter<AuthorizationFilter>(FilterScope.Controller, 0)
      .WhenControllerHas<RequireRolesAttribute>();
kernel.BindFilter<AuthorizationFilter>(FilterScope.Action, 0)
      .WhenActionMethodHas<RequireRolesAttribute>();

This should make sure any controller/action decorated with [RolesRequired] is bound to the filter. So far so good. Now I want to declare via the attribute the roles (much like the stock AuthorizeAttribute) and pass those values onto the filter that actually does the authorization.

Intended/Fake Code:

[RequireRoles(UserRoles.Author)]
public ActionResult Index()
{
    // blah
}

Specifically,

What does it take to inform the AuthorizationFilter of the roles? Can the filter/ninject access the arguments passed to the attribute's constructor? Can the filter/ninject pull them from the attributes public property?

For reference, these articles were a huge help, but don't answer this one thing:

Dependency Injection with Ninject and Filter attribute for asp.net mvc

Custom Authorization MVC 3 and Ninject IoC

B Z, Remo Gloor, others… how can I accomplish this?

Best Answer

I have figured it out (thanks to Remo's directions and documentation).

Use the appropriate .WithConstructorArgument extension whether you are binding to a Controller or Action filter. For example binding my action filter looks like this:

kernel.BindFilter<AuthorizationFilter>(FilterScope.Action, 0)
      .WhenActionMethodHas<RequireRolesAttribute>()
      .WithConstructorArgumentFromActionAttribute<RequireRolesAttribute>("requiredRoles", o => o.RequiredRoles);

Once I understood the Func<> signature, it all became clear. The best way I found to handle this was to

  1. make the extension type-specific for my attribute

    .WithConstructorArgumentFromActionAttribute<TAttribute>()
    
  2. fetch the value from the callback object (your attribute) via lambda:

    ("argumentName", o => o.PropertyName)