Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend AuthorizeAttribute and check the user's roles

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.

like image 360
Brendan Vogt Avatar asked Feb 25 '11 13:02

Brendan Vogt


2 Answers

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();
    }
like image 67
Tomas Avatar answered Oct 03 '22 14:10

Tomas


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;
    }
}
like image 26
Tomas Aschan Avatar answered Oct 03 '22 14:10

Tomas Aschan