Reputation: 2917
I would like to implement a whitelist approach that will apply an [Authorize(Roles = "Admin")]
attribute by default. Then I would like to specify [AllowAnonymous]
or [AllowMember]
on whitelisted actions.
So I need to create an attribute similar to AllowAnonymous
, but only giving access the "Member" role. (Like AllowAnonymous
, it should override any Authorize
attributes that may be in effect on controllers as global filters.)
I initially tried to inherit from AllowAnonymousAttribute
, but I find it is sealed. I've googled "Inherit allowanonymous" but the answers find me out of my depth.
Am I wise in my approach, and how can I create an attribute such as that?
Update
Following NightOwl888's advice and some code from this page, I have:
Created two Attributes
, one to allow members, the other the public
Inherited the AuthorizeAttribute to create a new one that I will apply as a global filter
Inserted a couple of methods into the AuthorizeCore() method that check for the attributes and return true
I hope I'm not doing anything daft in the code below... I'd appreciate a heads up (or down) if it looks ok (or not).
Thanks.
namespace FP.Codebase.Attributes
{
public class AllowPublicAccessAttribute : Attribute
{}
public class AllowMemberAccessAttribute : Attribute
{}
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
filterContext.HttpContext.Items["ActionDescriptor"] = filterContext.ActionDescriptor;
base.OnAuthorization(filterContext);
}
private bool IsAllowPublicAccessAttributeAppliedToAction(ActionDescriptor actionDescriptor)
{
return (actionDescriptor.IsDefined(typeof(AllowPublicAccessAttribute), inherit: true)
|| actionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowPublicAccessAttribute), inherit: true));
}
private bool IsAllowMemberAccessAttributeAppliedToAction(ActionDescriptor actionDescriptor)
{
return (actionDescriptor.IsDefined(typeof(AllowMemberAccessAttribute), inherit: true)
|| actionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowMemberAccessAttribute), inherit: true));
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var actionDescriptor = httpContext.Items["ActionDescriptor"] as ActionDescriptor;
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (IsAllowPublicAccessAttributeAppliedToAction(actionDescriptor))
{
return true;
}
if (IsAllowMemberAccessAttributeAppliedToAction(actionDescriptor) && user.IsInRole("Member"))
{
return true;
}
if (!user.Identity.IsAuthenticated)
{
return false;
}
var _usersSplit = SplitString(Users);
var _rolesSplit = SplitString(Roles);
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
return true;
}
// copied from https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/AuthorizeAttribute.cs
internal static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
}
Upvotes: 1
Views: 635
Reputation: 56869
All of the behavior of AllowAnonymous
attribute is coded into the AuthorizeAttribute.OnAuthorize
method. So, if you want to reuse this behavior, the best approach is to inherit AuthorizeAttribute
and override the AuthorizeCore
method instead.
Upvotes: 1