views:

3820

answers:

4

I am having a problem setting the Authorize attribute Role value from a variable. The error message says it requires a const variable. When I create a const type variable it works fine but I am trying to load the value from the Web.Config file or anything else that will allow the end user to set this. I'm using integrated Windows authentication since this is an intranet only application.

Is there a way to check the users role from a controller? I will use this in an if statement to authenticate instead of an attribute.

[Authorize(Roles = Config.GMPUser)]
public ActionResult Index()
   {
      return View();
   }
+2  A: 

You can use User.InRole( "RoleName" ) within a controller.

EDIT: The code below will not work since GetCustomAttributes() apparently returns a copy of each attribute instead of a reference to the actual attribute. Left as answer to provide context for other answers.

As far as setting it in the authorize attribute, the only idea that I have is to set it to the empty string in the attribute definition, then use reflection on the controller's type to get and modify the CustomAttribute property corresponding to the AuthorizeAttribute (i.e., the one whose type is AuthorizeAttribute) for each method you care about. You should be able to set the Roles property to your configuration item that way.

var attributes = typeof(MyController).GetMethod("Index")
                                     .GetCustomAttributes(typeof(AuthorizeAttribute),
                                                          false)
                 as AuthorizeAttribute;

attributes[0].Roles = Config.GMPUser;

I suppose that you would do this in your Global.asax file on application start so that it only needs to be done once.

tvanfosson
You can't modify attributes - the change doesn't "stick".
Marc Gravell
@Marc -- had never tried and was only grasping at straws...
tvanfosson
Thanks the User.IsInRole is the solution I'm going to implement. How would I implement this on a per controller basis? I would rather not have to implement it for every action when I don't need to.
tsquillario
+3  A: 

Attributes are burned at compile time, so as indicated, you can only use them with constants. You also can't change attributes, since even if it seems to let you, it doesn't "stick" next time you fetch the value back out. tvanfosson's User.InRole( "RoleName" ) is probably the best option (he has my +1).

Just to illustrate the issue with updating attributes:

class FooAttribute : Attribute
{
    public string Bar { get; set; }
}
static class Program
{
    [Foo(Bar="abc")]
    public static void Main()
    {
        MethodInfo method = typeof(Program).GetMethod("Main");
        var attrib = (FooAttribute) Attribute.GetCustomAttribute(method, typeof(FooAttribute));
        Console.WriteLine("Original: " + attrib.Bar);
        attrib.Bar = "def";
        Console.WriteLine("Updated: " + attrib.Bar);
        attrib = (FooAttribute)Attribute.GetCustomAttribute(method, typeof(FooAttribute));
        Console.WriteLine("Look again: " + attrib.Bar);
    }
}

Prints:

Original: abc
Updated: def
Look again: abc
Marc Gravell
+3  A: 

I have a class called StringResources that provides access to static string values. I ran into the same snag and solved the problem in the following manner. I inherited from the AuthorizeAttribute class and added a method override for the AuthorizeCore method. The functionality of the method had a call to IsInRole() and passes in the static string property. That did the trick. The only problem is having to create separate classes for each role (or for combinations of roles in whatever manner your business logic dictates).

public class SystemAdministratorAuthorizationRequiredAttribute
     : AuthorizeAttribute
    {
     protected override bool AuthorizeCore(System.Security.Principal.IPrincipal user)
     {
      return
       user.IsInRole(
       StringResources.AdministrationViewsStrings.SystemAdministratorRoleName
       );
     }
    }
brady gaster
A: 

Create a custom attribute like so: (slightly modified version provided by david hayden's blog http://davidhayden.com)

public class MyCustomAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthorizationService _authorizationService = new MyAuthorizationService();

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
       return _authorizationService.Authorize(httpContext);
    }
}

and apply like so:

[MyCustomAuthorize]
public ActionResult Create()
{
    return View();
}

now you can put all your custom authorization logic inside your own MyAuthorizationService class. Note: in davids solution you can set the internal MyAuthorizationService with the provided accessor. Not sure if you will be able pass in a new MyAuthorizationService() to it. Haven't tried that.

Areg Sarkissian