1

I have an issue regarding a custom authorization attribule (MVC 5) and a list of enumerations which represent the user's role. First i have this custom authorization attribute

[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public sealed class CustomAuthorizationAttribute : AuthorizeAttribute
{
    public Enums.Roles[] AllowedRoles { get; set; }
}

A role enumeration

public enum Roles
{
    uknown = 0,
    Admin= 100,
    Guest = 200,
}

A static list which I use to restrict user access to certain controllers and methods which i want to use

public static class AuthorizationHelpers
{
   public static readonly Enums.Roles[] AccessToIndivindual = {
       Enums.Roles.Admin,
       Enums.Roles.Guest,
   };
}

In my controller when I use the following and specify the list of enumeration roles, the authorization works as expected

    [CustomAuthorization(AllowedRoles = new[] { Enums.Roles.Admin, Enums.Roles.Guest})]
public class HomeController
{
   ....
}

What i need is to use the static readonly enumeration list with all the roles I want to allow to access the controller/method like AccessToIndivindual. in order to reuse them. I tried something like this

[CustomAuthorization(AllowedRoles = AuthorizationHelpers.AccessToIndivindual )]
public class HomeController
{
   ....
}

but every time I use it like this i get

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

Is there a way i can use a list of roles as enumerations as an attribute parameter to achieve this?

DKar
  • 464
  • 4
  • 17
  • You need to make `AccessToIndivindual` a `const` (not `static`) –  Oct 31 '18 at 21:17
  • I have already tried this. If i use public const Enums.Roles[] AccessToIndivindual = { Enums.Roles.Admin, Enums.Roles.Guest, }; I get "A const field of a reference type other than string can only be initialized with null" so i cannot use it – DKar Oct 31 '18 at 22:44

3 Answers3

1

Attributes are metadata and must be know at compile time, therefore must be a const. A static readonly field is not a const, and an array containing items cannot be declared as a const.

If you want to use a field as a a value for AllowedRoles, then you will need to use a Flags enum.

[Flags]
public enum Roles
{
    uknown = 1,
    Admin= 2,
    Guest = 4,
}

and then you can declare a const as

public const Roles _AccessToIndivindual = Roles.Admin | Roles.Guest;

and use it in your CustomAuthorizationAttribute as

[CustomAuthorization(AllowedRoles = _AccessToIndivindual )]
public class HomeController()
{
    ....
}
  • This is the best solution but I am trying to avoid using flags as this will change the integer value of the enumeration (change the Admin = 100 to Admin = 1) and this is something I am trying to avoid. That's why I am looking for a workaround. If there is no other way around it, then I will have to use flags – DKar Nov 01 '18 at 07:48
  • There is no other workaround - you either need to use `[CustomAuthorization(AllowedRoles = new[] { Enums.Roles.Admin, Enums.Roles.Guest})]` or a `[Flags]` enum because you cannot define an array as a `const` –  Nov 01 '18 at 07:51
0

This is by design and I’ve only seen one way this was worked around in Xunit. They have a similar problem when creating parameterized test cases (theories) using their inlinedata attribute. They introduce the classdata and memberdata attributes that likely use reflection to handle it. See the following post on how others use the it: http://hamidmosalla.com/2017/02/25/xunit-theory-working-with-inlinedata-memberdata-classdata/

tj391
  • 31
  • 4
  • The article shows something similar to what i am trying to accomplish by creating parameterised tests with xunit, but I don't think i can use any of tthat information to solve my problem – DKar Oct 31 '18 at 13:57
0

After spending a lot of time trying to figure out a workaround to this issue by trying to avoid enumeration flags and after reading Stephen Muecke's answer, I came to the conclusion that I had to refactor the whole authorization implementation. So if anyone is trying to implement a similar authorization logic, avoid using static list with enumerated Roles and use flags from the start. A very helpful answer with explanation regarding enumeration flags is posted here.

So here are the changes I had to do to make this work.

public static class Enums
{
  [Flags]
  public enum Roles
  {
    uknown = 0,
    Admin= 1 << 1,
    Guest = 1 << 2
  }
}

create a const enumeration which contains the roles which are authorized to perform an action

public static class AuthorizationHelpers{
    public const Enums.Roles Can_Save_Product = Enums.Roles.Admin| Enums.Roles.Guest;
}

the authorization attribute

[CustomAuthorization(Allowed_Roles = AuthorizationHelpers.Can_Save_Product )]
public class HomeController
{
     .....
}

and finally in my CustomAuthorization attribute

[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public sealed class CustomAuthorizationAttribute : AuthorizeAttribute
{
     public Enums.Roles Allowed_Roles { get; set; }
}

and everytime i want to check if the current role is authorized I can check by using

AuthorizationHelpers.Can_Save_Product.HasFlag(role.ToEnum<Enums.Roles>()))

Also just to mention that when using ToString() against a Flag Enumeration it creates a nice comma seperated string of the authorized roles which can easily converted to an array. Just don't forget to trim the results as there is a space after each comma

this.Allowed_Roles.ToString().Split(',').Select(u=>u.Trim()).ToArray();
DKar
  • 464
  • 4
  • 17