Including dependencies in AuthorizeAttribute in a class library

I had an interesting design problem with the class library that I am writing. I have a custom AuthorizeAttribute implementation that I want clients to use like this:

[Protected("permission_name")] 

In the above code, PermissionAttribute is inherited from AuthorizeAttribute and uses the local default (DefaultContext created using HttpContext).

Behind the scenes, the attribute uses the SecurityService to check users, roles and permissions (the SecurityService itself uses the client-provided save service, which they can connect to the root directory of their application).

Thus, my attributes need a reference to the SecurityService function. Since attribute constructors can only have compile time constants, I cannot use constructor injection.

I don’t want to force my clients to use the DI structure - they should be able to discover and bind the necessary dependencies at the root of their composition without using the IoC library if they wish.

Here are my options:

  • The library uses a singleton SecurityService.
  • Use an injection of properties that will work, but
    • it makes the addiction seem optional, but it’s not, and
    • I do not know where I can inject properties in an MVC application for the authorize attribute.

Possible solution 2. above is to make an instance of SecurityService as a static attribute property at application startup and use the guard clause to prevent it from being set more than once, for example:

 class ProtectedAttribute : ... { private static ISecurityService _SecurityService ; public static ISecurityService SecurityService { get { return _SecurityService ; } set { if (_SecurityService != null) throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ; _SecurityService = value ; } } } 

SecurityService can be an abstract facade of a service so that it can be extended / replaced by another implementation.

Is there a better way to solve this problem?

UPDATE: adding some code to show how I will do this:

Add a public attribute property that returns the name of the permission:

 public class ProtectedAttribute : ... { private string _Permission ; public string Permission { get { return _Permission ; } /*...*/ } public ProtectedAttribute(string permission) { /*...*/ } } 

Set the authorization filter and configure the dependency via Ninject (if using Ninject):

 using Ninject.Web.Mvc.FilterBindingSyntax; public class MyModule : Ninject.Modules.NinjectModule { public override void Load() { // mySecurityService instance below can have a singleton lifetime - perfect! this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0) .WhenActionMethodHas<ProtectedAttribute>() .WithConstructorArgument("securityService", mySecurityService) .WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ; } } 

Oh it's ... beautiful sniffle

+6
source share
2 answers

With ASP.NET MVC 3, you can use the constructor installation with action filters thanks to the new IFilterProvider . Thus, you no longer need to decorate your controller actions with action filters. You can apply them thanks to this interface and use the marker attribute.

And if you do not want to execute it manually, you can always use an existing DI infrastructure, such as Ninject, which provides a free way to determine the dependencies of an action filter.

+7
source

My applications inherit from the Application base class, which provides the IOC container.

 public interface IInjectableApplication { IUnityContainer Container { get; } } 

Then I have a base attribute class that knows about it

 public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{ protected T ResolveItem<T>(ResultExecutedContext context) { var app = context.HttpContext.ApplicationInstance as IInjectableApplication; if (app == null) { throw new NullReferenceException("Application is not IInjectable."); } T c = (T)app.Container.Resolve(typeof(T)); if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); } return c; } } 

This is not so far. Injection, since attributes are not built “normally”, this provides similar behavior. There is no reason why it should not be adapted to other IOC

0
source

Source: https://habr.com/ru/post/905323/


All Articles