0
votes

I have an application wherein users can make new Roles. Some actions are only accessible by certain roles. To check whether a user is allowed to do a certain action, I use a custom AuthorizeAttribute, similar to https://stackoverflow.com/a/40300184.

[AuthorizeRoles(Permission.Unlink, Permission.Link)] 
[HttpGet("link")]
    public IActionResult Link(int id)
    {
        ...
    }

The AuthorizeRolesAttribute class:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    public AuthorizeRolesAttribute(params Permission[] permissions)
    {   
        Roles = GetRoles(permissions);
    }
}

GetRoles:

public static string GetRoles(params Permission[] permissions)
{
    DataRowCollection rows = DatabaseHelper.RoleTable.Rows;
    List<string> allowedRoles = new List<string>();
    foreach (DataRow row in rows)
    {
        bool allowed = true;
        foreach (Permission permission in permissions)
        {
            if ((bool)row[permission.ToString()] == false)
                allowed = false;
        }
        //if all required permissions are true in this role it is added to the allowed roles
        if (allowed)
            allowedRoles.Add(row["ROLE"].ToString());
    }
    return string.Join(",", allowedRoles);
}

When the application starts, each method with the AuthorizeRolesAttribute calls the GetRoles method to determine what roles are allowed to use the method. This works fine for existing roles, however, when a new role is added. The attribute doesn't re-evaluate the roles. I need the attribute to update and allow the new role to use the method without having to restart the application.

I have tried to run the following code after adding a new Role. (As suggested by https://stackoverflow.com/a/12196932)

typeof(UsersController).GetMethod(nameof(UsersController.Link)).GetCustomAttributes(false);

This does cause the AuthorizeRolesAttribute to call GetRoles() again, and this does return a string with the new Role in it. However, when trying to access the 'Link' method as a user with the new Role, I get a 403 Forbidden status.

1
Then your problem is with the code that validates your roles.Ian Kemp
Roles that already exist are validated correctly though. Also, after restarting the application the new Role is validated correctly. So it seems to me like the GetRoles() method is working fine. However, for some reason, the AuthorizeRoles attribute doesn't act accordingly.Chris

1 Answers

0
votes

I found a solution. Instead of this:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    public AuthorizeRolesAttribute(params Permission[] permissions)
    {   
        Roles = GetRoles(permissions);
    }
}

I now have this:

public class AuthorizeRolesAttribute : Attribute, IAuthorizationFilter 
{
    private readonly Permission[] permissions;
    public AuthorizeRolesAttribute(params Permission[] permissions)
    {
        this.permissions = permissions;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        string[] roles = Authentication.GetRoles(permissions).Split(",");
        bool allowed = context.HttpContext.User.Claims.Any(c => c.Type.Contains("role") && roles.Contains(c.Value));
        if (!allowed)
            context.Result = new ForbidResult();
    }
}