How to use Ninject to inject services into an authorization filter?
Asked Answered
H

2

7

I am using asp.net mvc 3, ninject 2.0 and the ninject mvc 3 plugin.

I am wondering how do I get service layers into my filter(in this case an authorization filter?).

I like to do constructor inject so is this possible or do I have to property inject?

Thanks

Edit

I have this for property inject but my property is always null

  [Inject]
        public IAccountService AccountServiceHelper { get; set; }


        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            // check if context is set
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            // check if user is authenticated
            if (httpContext.User.Identity.IsAuthenticated == true)
            {
                // stuff here
                return true;
            }

            return false;
        }



    /// <summary>
    /// Application_Start
    /// </summary>
    protected void Application_Start()
    {

        // Hook our DI stuff when application starts
        IKernel kernel = SetupDependencyInjection();

        RegisterMaps.Register();

        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

    }


    public IKernel SetupDependencyInjection()
    {
        IKernel kernel = CreateKernel();
        // Tell ASP.NET MVC 3 to use our Ninject DI Container
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));

        return kernel;
    }

    protected IKernel CreateKernel()
    {
        var modules = new INinjectModule[]
                          {
                             new NhibernateModule(),
                             new ServiceModule(),
                             new RepoModule()
                          };

        return new StandardKernel(modules);
    }


public class ServiceModule : NinjectModule
{
    public override void Load()
    {
        Bind<IAccountService>().To<AccountService>();
    }

}

Edit

I upgraded to ninject 2.2 and get finally got it work.

Edit 2

I am going to try and do the constructor way for my authorize filter but I am unsure how to pass in the Roles. I am guessing I have to do it through ninject?

Edit 3

This is what I have so far

 public class MyAuthorizeAttribute : AuthorizeAttribute 
    {
        private readonly IAccountService accountService;

        public MyAuthorizeAttribute(IAccountService accountService)
        {
            this.accountService = accountService;
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            return base.AuthorizeCore(httpContext);
        }
    }

  this.BindFilter<MyAuthorizeAttribute>(FilterScope.Controller, 0)
                .WhenControllerHas<MyAuthorizeAttribute>();

  [MyAuthorize]
    public class MyController : BaseController
    {
}

It tells me it want's a no parameter constructor. So I must be missing something.

Horton answered 15/6, 2011 at 20:37 Comment(0)
G
14

The problem with filters is that they are attributes. And if you define a constructor of an attribute that expects some dependency you will never gonna be able to apply it to any method: because all values that you pass to attributes must be known at compile time.

So basically you have two possibilities:

  1. Use Ninject to apply the filter globally instead of decorating your controllers/actions with it:

    public interface IFoo { }
    public class Foo : IFoo { }
    
    public class MyFooFilter : AuthorizeAttribute
    {
        public MyFooFilter(IFoo foo)
        {
    
        }
    }
    

    and then configure the kernel:

    kernel.Bind<IFoo>().To<Foo>();
    kernel.BindFilter<MyFooFilter>(FilterScope.Action, 0).When(
        (controllerContext, actionDescriptor) => 
            string.Equals(
                controllerContext.RouteData.GetRequiredString("controller"),
                "home",
                StringComparison.OrdinalIgnoreCase
            )
    );
    
  2. Use property injection:

    public interface IFoo { }
    public class Foo : IFoo { }
    
    public class MyFooFilter : AuthorizeAttribute
    {
        [Inject]
        public IFoo Foo { get; set; }
    }
    

    and then configure the kernel:

    kernel.Bind<IFoo>().To<Foo>();
    

    and decorate some controller/action with your custom filter:

    [MyFooFilter]
    public ActionResult Index()
    {
        return View();
    }
    
Grimy answered 15/6, 2011 at 20:45 Comment(24)
@Darin Dimitrov - I guess I will try property inject. I am having problems with it. I will update my post.Horton
@chobo2, take a look at the examples I provided about the two possibilities you have.Grimy
@Darin Dimitrov- I been reading this -> planetgeek.ch/2010/11/13/… and they seem to recommend again property inject as it is backwards compatibility and I guess in the future will be replaced. If I go with option 1. How does the attribute tag look on the controllers/action methods. If I continue with property inject what am I missing as I seem to have what you have but my property is null. I know the binding works cuz I use the binding in constructor inject and it is not null.Horton
@chobo2, if you go with 1 you cannot apply the attribute on your controllers/actions as I explained in my answer. You need to conditionally apply it using Ninject.Web.Mvc.FilterBindingSyntax; as I showed in my answer. In my example I apply the MyFooFilter on all actions of HomeController.Grimy
@Darin Dimitrov - but in the example above are you not applying it to your action? It looks like an attribute to me.Horton
@chobo2, in point 1. I don't apply the attribute manually to an action: that would be impossible. I use the NInject filter binding syntax to do this: look at the BindFilter<T> extension method I use on the kernel.Grimy
@Darin Dimitrov - why would that be applied to all your actions of HomeController if it is on a action level? Would that not need to be on the controller level?Horton
@chobo2, because that's the condition I wrote when I configured my kernel: I conditionally applied it based on the route value of the controller which I check against Home.Grimy
@Darin Dimitrov - So your ninject is saying when you see your FoolFilter in a the home controller bind it to that action result?Horton
@Darin Dimitrov - So that would mean I have to Pass Roles through niject. As I am making an authorization attirbute so I need the ability to pass in Roles. Usually that Was Roles ="Role1"Horton
@chobo2, no, my syntax is saying: when the controller token of a route equals Home then apply the custom filter (and of course do the constructor injection). As far as roles are concerned, you could still pass them in the lambda expression based on the action. Use RouteData.GetRequiredString("controller") to get the current action and based on its value set any roles on the filter.Grimy
@Darin Dimitrov - So I am passing it through ninject through lambda ? Also what is weird is I don't have BindFilter<> eventhough I upgraded to ninject 2.2 and updated the ninject mvc 3 plugin.Horton
@chobo2, did you brought the BindFilter<T> extension method into scope by using Ninject.Web.Mvc.FilterBindingSyntax;?Grimy
@Darin Dimitrov - Weird I have to use Kernel.BindFilter<>() it does not show up otherwise. Where Bind<> I do not.Horton
@chobo2, yeah weird, I've installed the latest Ninject.MVC3 NuGet package and it has always worked great for me.Grimy
@Darin Dimitrov - I also get this error now Error 102 Assembly 'Ninject.Web.Mvc, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7' uses 'Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7' which has a higher version than referenced assembly 'Ninject, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7'Horton
@Darin Dimitrov - When I look at the ninject.dll that I updated through nuget it says Version = "2.0.0.0" but I think it should be version 2.2.1.4Horton
@chobo2, it seems like you have messed up everything :-) Start from scratch: create a new ASP.NET MVC 3 application using the default visual studio template, install the Ninject.MVC3 NuGet package, try the code I showed here in the generated App_Start/NinjectMVC3.cs file, have fun...Grimy
@Darin Dimitrov - I don't get that error(have not tested your stuff yet...one problem at a time) in a new project but I need to figure it out in this project I can't just move the entire site to a new soultionHorton
@chobo2, here's what you could do in this case: perform the steps I suggested in a brand new project. Make it work. Make sure you understand how it works. Once you are confident copy and update the assemblies and adapt the code in your existing project.Grimy
Note that you can also configure that the filter shall apply if there is a attribute on the action and pass arguments using this filter using 'WhenActionHas<SomeAttribute>()'. But you HAVE to use a new Attribute and NOT the filter! See the chapters "Conditional bindings for filters" and "Filter Configurations" in my blog.Heavyarmed
Hi guys, please don't use the commenting system as a chat room. It is for leaving a few comments and prods for more information to a question or answer, not for long debates. The reason behind this is that most of the time (and this is one of them), a lot if not all the comments belong as edits to the question/answer to make that more complete. If I have to read a half-page answer + 3 pages of comments, the focus on the comments is too big. Please edit in pertinent details into the answer instead. If you really need to chat, find/create a chat-room on the Chat site, link at the top of the pageEternize
@Darin Dimitrov - OK I think I fianlly got it to update but now. I get see edit.Horton
@Darin Dimitrov - Ok I got it to work with ninject 2.2. I am not going to try your stuff but I am still unclear on how the roles will work.Horton

© 2022 - 2024 — McMap. All rights reserved.