Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DI into a Requirement/Policy in ASP.NET MVC 6

I'm looking for a way to program a custom authorization filter in ASP.NET 5 as the current implementation relies in Policies/Requirements which in turn rely solely in the use of Claims, thus on the umpteenth and ever-changing Identity System of which I'm really tired of (I've tried all it's flavors).

I have a large set of permissions (over 200) which I don't want to code as Claims as I have my own repository for them and a lot faster way to be check against it than comparing hundreds of strings (that is what claims are in the end).

I need to pass a parameter in each attribute that should be checked against my custom repository of permissions:

[Authorize(Requires = enumPermission.DeleteCustomer)]

I know this is not the most frequent scenario, but I think it isn't an edge case. I've tried implementing it in the way described by @leastprivilege on his magnificent post "The State of Security in ASP.NET 5 and MVC 6: Authorization", but I've hit the same walls as the author, who has even opened an issue on the ASP.NET 5 github repo, which has been closed in a not too much clarifying manner: link

Any idea of how to achieve this? Maybe using other kind of filter? In that case, how?

like image 398
Vi100 Avatar asked Nov 13 '15 11:11

Vi100


1 Answers

Following is an example of how you can achieve this scenario:

Let's assume you have a service called IPermissionStore which validates if a given user has the required permissions specified on the attribute.

public class MyCustomAuthorizationFilterAttribute : Attribute, IFilterFactory, IOrderedFilter
{
    private readonly Permision[] _permissions;

    public MyCustomAuthorizationFilterAttribute(params Permision[] permissions)
    {
        _permissions = permissions;
    }

    public int Order { get; set; }

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        var store = serviceProvider.GetRequiredService<IPermissionStore>();

        return new MyCustomAuthorizationFilter(store, _permissions)
        {
            Order = Order
        };
    }
}

public class MyCustomAuthorizationFilter : IAuthorizationFilter, IOrderedFilter
{
    private readonly IPermissionStore _store;
    private readonly Permision[] _permissions;

    public int Order { get; set; }

    public MyCustomAuthorizationFilter(IPermissionStore store, params Permision[] permissions)
    {
        _store = store;
        _permissions = permissions;
    }

    public void OnAuthorization(AuthorizationContext context)
    {
        // Check if the action has an AllowAnonymous filter
        if (!HasAllowAnonymous(context))
        {
            var user = context.HttpContext.User;
            var userIsAnonymous =
                user == null ||
                user.Identity == null ||
                !user.Identity.IsAuthenticated;

            if (userIsAnonymous)
            {
                Fail(context);
            }
            else
            {
                // check the store for permissions for the current user
            }
        }
    }

    private bool HasAllowAnonymous(AuthorizationContext context)
    {
        return context.Filters.Any(item => item is Microsoft.AspNet.Authorization.IAllowAnonymous);
    }

    private void Fail(AuthorizationContext context)
    {
        context.Result = new HttpUnauthorizedResult();
    }
}

// Your action
[HttpGet]
[MyCustomAuthorizationFilter(Permision.CreateCustomer)]
public IEnumerable<string> Get()
{
    //blah
}
like image 74
Kiran Avatar answered Oct 04 '22 02:10

Kiran