How to extend AuthorizeAttribute and check the user's roles

20,254

Solution 1

Would it not be better if MyAuthorizeAttribute accepted an IList ( or similar) That way it is both typesafe, but you don't have to use bit flags. Bit flags are great if you want to save the reult, but this is the other way.

Edit (Now with examples):

Attribute:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public Role[] RoleList { get; set; }


    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }
        IPrincipal user = httpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            return false;
        }
        //Only role access is implemented here
        /*if ((this._usersSplit.Length > 0) && !this._usersSplit.Contains<string>(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
        {
            return false;
        }*/
        if ((RoleList.Length > 0) && !RoleList.Select(p=>p.ToString()).Any<string>(new Func<string, bool>(user.IsInRole)))
        {
            return false;
        }
        return true;
    }

}

Controller:

[MyAuthorize(RoleList = new []{Role.Administrator , Role.SuperAdministrator} )]
    public ActionResult Create()
    {
        return View();
    }

Solution 2

If I understand correctly, your problem here is not with inheriting the AuthorizeAttribute, but rather with comparing enum values. You probably want an enum type that you can use as a bit flag - if so, take a look at the section about Enumeration Types in the C# Programming guide especially the second part, "Enumeration Types as Bit Flags".

To clarify a bit:

Instead of just checking Roles!=0, you could now do something like this:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    base.OnAuthorization(filterContext);

    // Here you get an enum indicating the roles this user is in. The method
    // converts the db information to a Role enum before it is returned.
    // If the user is not authenticated, the flag should not be set, i.e. equal 0.
    Role userRole = GetUserRolesFromDatabase();

    // Bitwise comparison of the two role collections.
    if (Roles & userRole > 0)
    {
        // The user is in at least one of the roles in Roles. Return normally.
        return;
    }

    // If we haven't returned yet, the user doesn't have the required privileges.
    new HttpUnauthorizedResult(); 
}

To make the comparison easier, you could use the following extension method on your enum:

public static class RolesExtensions
{
    public static bool HasAnyOf(this Roles r1, Roles roles)
    {
        return (r1 & roles) > 0;
    }
}
Share:
20,254
Brendan Vogt
Author by

Brendan Vogt

Wedding photographer and videographer from Paarl, South Africa. Join me on my new adventure in wedding photography and videography at Brendan Vogt Photo &amp; Video.

Updated on February 26, 2020

Comments

  • Brendan Vogt
    Brendan Vogt about 4 years

    I am busy writing my own custom attribute for my action method called MyAuthorizeAttribute, I am still busy writing the code, here is my partial code:

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class  MyAuthorizeAttribute : AuthorizeAttribute
    {
       public new Role Roles;
    
       public override void OnAuthorization(AuthorizationContext filterContext)
       {
          base.OnAuthorization(filterContext);
    
          if (Roles != 0)  // Did it this way to see what the value of Roles was
             return;
    
          // Here I am going to get a list of user roles
          // I'm doing my own database calls
    
          filterContext.Result = new HttpUnauthorizedResult();
       }
    }
    

    Here is my Role enum:

    public enum Role
    {
       Administrator = 1,
       SuperAdministrator = 2
    }
    

    My action method:

    [MyAuthorize(Roles = Role.Administrator|Role.SuperAdministrator)]
    public ActionResult Create()
    {
       return View();
    }
    

    The reason why I did not use Roles = "Administrator,SuperAdministrator" was because the roles are hard-coded. I don't want to have a 100 places to change if the role name changes.

    Given my method, when it gets to if (Roles != 0) then Roles total value is 3, how would I check to see if these 2 roles is in the list of user roles for a specific user?

    Am I doing it correct here? If not how would I otherwise implement this? It doesn't have to be the way that I did it in.