user4864716
user4864716

Reputation:

How can I use a variable in [Authorize(Roles="")]

I have an MVC 5 C# intranet web application where we have over 30 Active Directory roles in use, and permissions are often a changing thing due to the business culture.

To make things easy for myself, I thought I would try something like this to determine who is allowed access to a controller action or child action.

/* This function runs a LINQ query and outputs a comma delimited string of 
   approved active directory roles.
*/

    private static string _approvedRoles = 
            Helpers.QueryableExtensions.GetApprovedRoles("FourCourseAudit");

    // GET: FourCourseAudits    
    [Authorize(Roles = _approvedRoles)]
    public ActionResult Index(string searchBy="All", 
          string orderBy="Campus", string orderDir="Asc")
    {

    // and so on... 

Unfortunately, I get this compile time error: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.

This is where I am after trying other approaches with the _approvedRoles variable, such as public const string and public string. I placed the GetApprovedRoles function in the model, in the repository (where it is now), and in the body of the controller.

I know the roles are good because if I use this: [Authorize(Roles="DOMAIN\Role1,DOMAIN\Role2")] it works. And that's not a feasible option for me because the roles change and this is a very large MVC site. Is there some way I can let Roles be a variable?

Upvotes: 10

Views: 13622

Answers (4)

dalcam
dalcam

Reputation: 1077

This is a simple version for any who want it:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    public AuthorizeRolesAttribute(params string[] roles)
    {
        Roles = String.Join(",", roles);
    }
}

Usage is then:

[AuthorizeRoles(RoleConstant1,RoleConstant2)]

Upvotes: 11

Christos
Christos

Reputation: 53958

In order the compilation of your code to be successfull, you should do what is stated in the error message you have posted.

For instance, you could try something like this:

[Authorize(Roles = "Admin,User")]

In simple terms the values you pass to the an attribute as parameters should be known at the time of compilation. They cannot be evaluated at the runtime.

Update

As I stated in my comment below:

The only alternative is to build a custom attribute and get there the approved roles. Then you could check if the current's user role is in the approved roles and your custom attribute - your filter - would allow or not to the use to call this action.

Upvotes: 1

mfanto
mfanto

Reputation: 14418

The arguments need to be known at compile time, but your query happens at run time.

The easiest way to do this is to create a custom AuthorizeAttribute. Inside the Authorize() method, you can do any checks you want, including querying a database. You can also pass custom parameters into the constructor, if you want more flexibility in reusing the attribute. As an example:

public class RoleAuthorizeAttribute : AuthorizeAttribute
{
    // or inject it
    private DbContext _db = new DbContext();

    private string _filter;

    public RoleAuthorizeAttribute(string filter)
    {
        _filter = filter;
    }

    /// <summary>
    /// Check authorization
    /// </summary>
    /// <param name="filterContext"></param>
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var currentUser = HttpContext.Current.User;

        // do some checks, query a database, whatever
        string approvedRoles =  Helpers.QueryableExtensions.GetApprovedRoles(_filter);

        if (!currentUser.IsInRole(...))
        {
            filterContext.Result = new RedirectToRouteResult("Error", new RouteValueDictionary());
        }
    }
}

And to use:

[RoleAuthorize("FourCourseAudit")]

Upvotes: 13

DarkVision
DarkVision

Reputation: 1423

you can always define new class that extend from AuthorizeAttribute and ovveride AuthorizeCore and HandleUnauthorizedRequest and create a static class with all constant role declare that how i do in my application.

example of code in my custom class attribute inside AuthorizeCore method

    public string Groups { get; set; } // this will be my new attribute
    var groups = Groups.Split(',').ToList();

    var context = new PrincipalContext(ContextType.Domain,"myDomain");
    var userPrincipal = UserPrincipal.FindByIdentity(context,IdentityType.SamAccountName,httpContext.User.Identity.Name);

    foreach(var group in groups){ // will iterate the attribute and check if that user log is in that group
     if(userPrincipal.IsMemberOf(context, IdentityType.Name, group)){
                            return true;
     }
   }

Upvotes: 0

Related Questions