Ninject Binding Attribute to Filter with Constructor Arguments
Asked Answered
P

2

18

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?

Planimetry answered 29/11, 2011 at 3:29 Comment(0)
P
21

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)
    
Planimetry answered 29/11, 2011 at 21:35 Comment(2)
Any ideas as to what has to be done if the class deriving from FilterAttribute has constructor with multiple parameters (i.e. RequireRoles class in above example having constructor with multiple parametes) then how can the parameters be mapped in .WithConstructorArgumentFromActionAttribute<TAttribute>() ??Cryolite
To make the BindFilter<>() method available, you can add a reference to Ninject.Web.Mvc.FilterBindingSyntax.Hoover
T
10

and to bring the BindFilter extension method into scope don't forget to add;

using Ninject.Web.Mvc.FilterBindingSyntax;
Tjon answered 16/7, 2012 at 12:26 Comment(1)
This is not an answer, it should be posted as a comment to the solution answer.Uralic

© 2022 - 2024 — McMap. All rights reserved.