10

I have a custom authorization class that inherits from FilterAttribute and implements IAuthorizationFilter. I am using the latest version of Ninject w/ asp.net MVC 3 support.

The problem I have is I am using constructor injection to inject a repository. But by the the time OnAuthorization is called, the repository is null. Here is the code...

public class MyAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
    {
        private readonly IMyRepo _MyRepo;

        public MyAuthorizeAttribute() { }
        public MyAuthorizeAttribute(IMyRepo myRepo)
        {
            _MyRepo= myRepo; //this gets initialized
        }


        public void OnAuthorization(AuthorizationContext filterContext)
        {
            _MyRepo.DoStuff(); //<< Null, wtf

        }
    }

Filter Binding:

Bind<IMyRepo>().To<MyRepo>().InRequestScope();


this.BindFilter<MyAuthorizeAttribute >(System.Web.Mvc.FilterScope.Controller, null).WhenControllerHas<MyAuthorizeAttribute >();

Update: One thing I noticed is this filter is at controller level. I have other filters at action scope that seem to work properly...could that be the reason?

Update 2: I've confirmed that if I change the filter scope to action, then the repository is available OnAuthorization (not null).

This works below, however I need at controller scope, not action.

this.BindFilter<MyAuthorizeAttribute >(System.Web.Mvc.FilterScope.Action, null).WhenActionMethodHas<MyAuthorizeAttribute >();
B Z
  • 9,363
  • 16
  • 67
  • 91
  • It looks like your MyAuthorizeAttribute gets called with default constructor. – dexter Apr 06 '11 at 20:01
  • @dexter, dont' think that is the case. I put breakpoint and ctor with repo is called – B Z Apr 06 '11 at 20:04
  • Have you set up the filter binding in the Ninject module. Look at the sample code under section "Dependency Injection for Filters" in http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/#more-2004 – WorldIsRound Apr 06 '11 at 20:08
  • @WorldIsRound yeah, I updated the bindings in the question. thx – B Z Apr 06 '11 at 20:12
  • You may have set up the binding, but just in case, are you setting up the binding association between IMyRepo and Repo implementation? – WorldIsRound Apr 06 '11 at 20:24
  • @WorldIsRound, yeah, I am. The repo is also used in other places. I can see it getting injected when I step through the code. It is just null by the time OnAuthorization is called. – B Z Apr 06 '11 at 20:27

3 Answers3

10

Attributes do not support constructor injection as they are created by the .NET Framework and are not under control of Ninject. If you really want to use a FilterAttribute (which I do not recommend) you'll have to use property injection.

Instead continue what you just began. You need a filter implementing IAuthorizationFilter (not derived from FilterAttribute, just remove it from your code above) and additionally an ordinary attribute to mark the controllers/actions.

Then change the binding:

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

See: https://github.com/ninject/ninject.web.mvc/wiki/MVC3

The problem with you current implementation is that it is found once as filter attribute and once added as normal filter. One for these instances will have the repo injected an the the repo is null for the other one.

NOTE: you can derive from an existing FilterAttribute if this simplifies your implementation. But do not use it as a attribute in this case but use it as an ordinary filter.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
  • All makes sense except for the "Note" part. Just out of curiosity, why does it seem to work when I change the scope to action instead of controller? – B Z Apr 06 '11 at 21:15
  • Changing to action will change that you don't have two filters on the controller anymore but one on the action (with a repo reference) and one on the controller (repo == null). I'm not sure about the execution order and if .NET executes all IAuthorizationFilters or just until one is successful. The one on the controller should still result in a NullRef Exception. But probably it isn't executed. – Remo Gloor Apr 06 '11 at 21:38
  • Thanks for the answer and follow up Remo – B Z Apr 07 '11 at 14:13
  • You say to use an ordinary attribute to mark the controllers. Is this just an empty (non functional) class that derives from Attribute? Does the code in this class ever execute? – BZink Apr 28 '11 at 03:11
6

It is better to extend the AuthorizeAttribute class so that authorization works correctly with cached requests. You will also need to use Ninject.Web.Mvc

You will need to use Ninject property injection to use your repository. Constructor injection will not work with Attributes.

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    [Inject]
    public IMyRepo MyRepo { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return base.AuthorizeCore(httpContext);
    }
}
Rohan West
  • 9,262
  • 3
  • 37
  • 64
  • 2
    thanks, this is what i originally had but then started receiving some "“The underlying provider failed on Open” exceptions from EF... dunno if they were related to using authorize filter or not so i decided to try the ctor injection and here i am... – B Z Apr 06 '11 at 21:19
  • Hi Rohan, just to be clear. The constructor injection will not work because in the constructor we pass the attribute values (params array). Also when you say property injection - is [Inject] attribute on top of property enough to achieve that with Ninject? Can you share with as the Ninject binding - how is that defined. Do you need to specify there the input parameter? I am having an issue with similar implementation. In my attribute I am accessing dbcontext through services. My dbcontext have setting InRequestScope, and this couses an exception dbcontext is being disposed . – Lyubomir Velchev Apr 30 '15 at 14:23
  • that disposed exception is happening when I hit my logic in the filter. I red that https://github.com/ninject/Ninject.Web.Mvc/wiki/Filters-and-Scoped but it doesn't make much sense to me how exactly to implement it..... – Lyubomir Velchev Apr 30 '15 at 14:27
0

Just thought that I would add my solution here as seems to work fine.

Created a class that extents AuthorizeAttribute and takes repository interface in constructor.

This class then overrides the AuthorizeCore function:

public class MyRoleAttribute : AuthorizeAttribute
{
    private ICRepository repository;

    public MyRoleAttribute(ICRepository Repo)
    {
        repository = Repo;
    }

protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        //Check if user authenticated
        if (!httpContext.Request.IsAuthenticated)
            return false;

         //Can access items in the query string if needed
         var id = (httpContext.Request.RequestContext.RouteData.Values["id"] as string)
         ??(httpContext.Request["id"] as string);

          //Can access repository that has been injected
          if (repository.IsGroupCreator(.....))
            {

                return true;

            }
            else
            {

                return false;

            }
    }
}

Then for the repository injection to work I added the following code to the mvc NinjectWebCommon.cs file:

kernel.BindFilter<MyRoleAttribute>(FilterScope.Action, 0).When(
(controllerContext, actionDescriptor) => actionDescriptor.ActionName == "MyAction");

This then allows me to control what actions I need the attribute on and ninject takes care of the repository injection. Hope this helps someone.

craigvl
  • 280
  • 2
  • 9