Background
I would like to authorize an ApplicationUser
based on their ApplicationRole
s' RoleClaim
s. I would like to implement this like so:
public class RoleClaimAuthorizeAttribute : AuthorizeAttribute
{
public RoleClaim RoleClaim { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
foreach (ApplicationRole role in Roles)
{
if ((RoleClaim & role.Claims) > 0)
{
return true;
}
}
return false;
}
}
Then I could decorate controller actions like so:
[RoleClaimAuthorize(RoleClaim =
RoleClaim.CanCreateRoles |
RoleClaim.CanReadRoles |
RoleClaim.CanDeleteRoles |
RoleClaim.CanUpdateRoles
)]
//
// GET: /Roles/
public ActionResult Index()
{
return View(_roleManager.Roles);
}
Question
The problem I'm encountering is any method I can find of reaching an ApplicationUser
's ApplicationRole
s from my custom authorize attribute returns a string array of ApplicationRole.Name
not an array of ApplicationRole
so I can't get to ApplicationRole.Claims
. I'm also using Unity instead of Owin to handle ApplicationRoleManager
so I can't request ApplicationRoleManager
via HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>()
.
So how can I get to a collection of ApplicationRole
objects for the current user and thus ApplicationRole.Claims
?
Or if it's a more appropriate solution, how can I store a string array of the current ApplicationUser
's ApplicationRole
s' RoleClaim
s in HttpContext
much like how roles are stored? I'm aware my authorization attribute couldn't work as described in this situation but it's a situation I can work with nonetheless.
Relevant Classes
ApplicationUser
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser<Guid, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public override Guid Id { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, Guid> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, Guid> manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
}
ApplicationRole
public class ApplicationRole : IdentityRole<Guid, ApplicationUserRole>
{
public ApplicationRole() : base()
{
this.Id = Guid.NewGuid();
}
public ApplicationRole(string name)
: this()
{
this.Name = name;
}
public ApplicationRole(string name, params string[] claims)
: this(name)
{
Claims = (RoleClaim)Enum.Parse(typeof(RoleClaim), String.Join(",", claims));
}
public RoleClaim Claims { get; set; }
}
RoleClaim
[Flags]
public enum RoleClaim : int
{
CanCreateUsers = 1,
CanReadUsers = 2,
CanUpdateUsers = 4,
CanDeleteUsers = 8,
CanCreateRoles = 16,
CanReadRoles = 32,
CanUpdateRoles = 64,
CanDeleteRoles = 128,
CanCreateTests = 256,
CanReadTests = 512,
CanUpdateTests = 1024,
CanDeleteTests = 2048
}
ApplicationRoleManager
public class ApplicationRoleManager : RoleManager<ApplicationRole, Guid>
{
public ApplicationRoleManager(IRoleStore<ApplicationRole, Guid> store) : base(store)
{
}
}
If you registered the role manager in Unity you could retrieve everywhere, including your custom attribute, simply by calling following method:
Or if you don't want to use the resolver directly you could use Unity's property injection feature so Unity automatically injects the role manager in the custom attribute which in explained here. Then call
roleManager.FindByNameAsync()
method to retrieve the role object.But this approach is not recommended because in each call your code hits the database to retrieve claims. It is much better to store user's claims in
ClaimsIdentity
when user signs in then retrieve them within the attribute like this:Now
RoleClaims
are added as claims when user signs in. You could retrieve them in the attribute like this: