0

We use a custom AuthorizeAttribute to handle a few aspects of user authorization. I need to add access to the database to check a value during authorization. This project uses the Repository pattern and Repos are all instantiated in controller constructors via StructureMap.

Unfortunately, it appears there's no way to use constructor injection with filters. I found an article by Jimmy Bogard (http://lostechies.com/jimmybogard/2010/05/03/dependency-injection-in-asp-net-mvc-filters/) explaining how to use property injection to handle this scenario. Basically, Jimmy's code intercepts the GetFilters method in ControllerActionInvoker and runs BuildUp on each filter to populate the properties. This was just what I needed, so I added the following class -

public class InjectingActionInvoker : ControllerActionInvoker
{
    private readonly IContainer _container;

    public InjectingActionInvoker(IContainer container)
    {
        _container = container;
    }

    protected override FilterInfo GetFilters(
        ControllerContext controllerContext, 
        ActionDescriptor actionDescriptor)
    {
        var info = base.GetFilters(controllerContext, actionDescriptor);

        info.AuthorizationFilters.ForEach(_container.BuildUp);
        info.ActionFilters.ForEach(_container.BuildUp);
        info.ResultFilters.ForEach(_container.BuildUp);
        info.ExceptionFilters.ForEach(_container.BuildUp);

        return info;
    }
}

And then wired it into StructureMap with these lines -

For<IActionInvoker>().Use<InjectingActionInvoker>();
For<ITempDataProvider>().Use<SessionStateTempDataProvider>();

Policies.SetAllProperties(c =>
{
    c.OfType<IActionInvoker>();
    c.OfType<ITempDataProvider>();
    c.WithAnyTypeFromNamespaceContainingType<UserProfileRepository>();
});

And finally, I added the public property to my custom AuthorizeAttribute class -

[SetterProperty]
public UserProfileRepository User { get; set; }

When I run the project and access a secure page, the AuthorizeCore code is hit twice. The first time, my property is set and working properly. However, the second call fails because the property is null. I set a breakpoint in the GetFilters method, and it's only being hit the first time. Unfortunately, I just don't have a strong enough understanding of StructureMap or Filters to know exactly where this is going sideways.

Below are the call stacks, in case that is useful for anyone -

Call #1

AppName.dll!AppName.Filters.SiteAuthorizeAttribute.AuthorizeCore(System.Web.HttpContextBase httpContext) Line 78    C#
[External Code] 
AppName.dll!AppName.Filters.SiteAuthorizeAttribute.OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) Line 31   C#
[External Code]

Call #2

AppName.dll!AppName.Filters.SiteAuthorizeAttribute.AuthorizeCore(System.Web.HttpContextBase httpContext) Line 69    C#
[External Code] 
AppName.dll!AppName.Filters.SiteAuthorizeAttribute.OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) Line 31   C#
[External Code] 
App_Web_1fnmflat.dll!ASP._Page_Views_Shared__Menu_cshtml.Execute() Line 2   C#
[External Code] 
App_Web_1fnmflat.dll!ASP._Page_Views_Shared__Layout_cshtml.Execute() Line 51    C#
[External Code] 

Any StructureMap masters care to share some wisdom? Thanks in advance for any help!

Edit: Here's the code for the _Menu.cshtml file -

@(Html.Kendo().Menu()
      .Name("Menu")
      .Items(items =>
      {
          items.Add().Text("My Dashboard").Action("Dashboard", "Home");
          items.Add().Text("My Account").Action("Edit", "Account");
          items.Add().Text("Purchase/Renew").Action("Index", "Purchase");
          items.Add().Text("Administration")
              .Items(children =>
              {
                  children.Add().Text("Accounts").Action("Index", "UserProfile");
                  children.Add().Text("Coupons").Action("Index", "Coupon");
              });
          items.Add().Text("Logout").Action("Logout", "Logon");
      })
      )

Thanks to some prompting from NightOwl888, I have isolated the issue to the Kendo Menu call. If I place a breakpoint on the final line of _Menu.cshtml and step into, I see DoGetInstance called for my HomeController. As soon as that is completed, OnAuthorization is fired for the second time and my repo property is null.

Anyone know what I'm missing here?

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Phil Figgins
  • 796
  • 1
  • 8
  • 22
  • I don't think you will find an answer unless you can explain why your authorize attribute is being called twice, and who is instantiating it the second time. Is your GetFilters method being called twice? – NightOwl888 Dec 08 '14 at 18:00
  • Sorry, the 2nd call is coming from the rendering of a partial named _Menu.cshtml inside my _Layout.cshtml file (you can see this in the call stacks). – Phil Figgins Dec 08 '14 at 20:09
  • And, no, GetFilters is only being called one time (as stated above). It feels like the class is being cached but the cached version is being stored before the BuildUp has happened? I don't know. – Phil Figgins Dec 08 '14 at 20:11

4 Answers4

0

Since you know that GetFilters is not being called during the second call and the first instance of AuthorizeAttribute is working, then this clearly isn't an issue with your DI setup. If the AuthorizeAttribute instance were being cached, then all of the dependencies would be cached as well, so that isn't it either.

The issue boils down to the fact that your menu isn't calling the FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor); method before using the AuthorizeAttribute. I can only guess as to why though because you didn't provide the code for your menu or its partial method.

You could try overriding ControllerActionInvoker.InvokeAuthorizationFilters instead.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
0

I too was facing the same issue and was trying different methods, finally this worked for me after removing [AllowMultiple = true] and now AuthorizeCore is called once. I am curious how this is linked to AuthorizeCore.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CompanyAuthorizeAttribute : AuthorizeAttribute
{}

before it was

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class CompanyAuthorizeAttribute : AuthorizeAttribute
{}
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Thanks, Mohan. Unfortuantely your fix didn't work for me. Hopefully it does for someone else stumbling on this page! – Phil Figgins Dec 22 '14 at 20:38
  • I got you question wrong. I was working on MVC5 proj and installed StructureMap.MVC5 @GitHub .This gives you basic IOC. To implement IOC for Filters I did below modifications. ActionInvoker expects container obj. public static IContainer Initialize() { var _con = new Container(c => c.AddRegistry()); _con.Configure(c => c.AddRegistry(new ActionFilterRegistry(_con))); return _con; } public class ActionFilterRegistry: Registry { public ActionFilterRegistry(IContainer con) { For().Use(new InjectingActionInvoker(con)); Policies.SetAllProperties(....); } } – Mohan Katari Dec 29 '14 at 13:36
0

Sadly, I haven't been able to track down what is happening. So here's how I've "fixed" the issue for now -

    [SetterProperty]
    public UserProfileRepository User
    {
        get { return _user ?? DependencyResolver.Current.GetService<UserProfileRepository>(); }
        set { _user = value; }
    }

When the injection works the injected value is used, otherwise I use the DependencyResolver to do the dirty work by hand. Not pretty, but I have no other options right now.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Phil Figgins
  • 796
  • 1
  • 8
  • 22
0

I did not get this to work with a custom filter provider, as described in this thread:

How can you unit test an Action Filter in ASP.NET Web Api?

I tried a bunch of options, but ended up injecting a dependency resolving function into my attributes. That way, unit tests can inject a function that returns a fake, mock or whatever, while the application can inject a function that resolves the dependency using the IoC container.

I wrote a short blog post about how I got it to work. Would love to hear what you think of it and if it helped you out:

http://danielsaidi.com/blog/2015/09/11/asp-net-and-webapi-attributes-with-structuremap

Pang
  • 9,564
  • 146
  • 81
  • 122
Daniel Saidi
  • 6,079
  • 4
  • 27
  • 29