How to make Custom PrincipalPermissionAttribute handle user or role changes - c#

I have written a custom PrincipalPermissionAttribute that uses an AuthenticationService instead of Thread.CurrentPrincipal as PrincipalPermissionAttribute does.
It works as I like, but if the user logs out and back in, or if the user's roles were to change, the attribute code is never called a second time. I suspect I've not informed the attribute it needs to recheck the permission? Breakpoint set on the CreatePermission method only ever hits once.
Is an attribute code only ever evaluated once? The attribute is currently decorating an event handler for a button click on my View's codebehind.
If I change my method back to use the PrincipalPermissionAttribute then it does work as I expect, logging out and back in as a user without the right role throws the SecurityException I expect. Have I missed overriding a property perhaps?
[Serializable]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class RolePermissionAttribute : CodeAccessSecurityAttribute
{
private readonly PrincipalPermission _revoke = new PrincipalPermission(PermissionState.None);
private readonly PrincipalPermission _allow = new PrincipalPermission(PermissionState.Unrestricted);
private IList<string> _roles;
private readonly IAuthenticationService _authorisationService;
public RolePermissionAttribute(SecurityAction action)
: this(action, ServiceLocator.Current.GetInstance<IAuthenticationService>())
{
}
public RolePermissionAttribute(SecurityAction action, IAuthenticationService authorisationService)
: base(action)
{
_authorisationService = authorisationService;
}
public string Roles { get; set; }
public bool Authenticated { get; set; }
public override IPermission CreatePermission()
{
_roles = (this.Roles ?? string.Empty).Split(',', ';')
.Select(s => s.Trim())
.Where(s => s.Length > 0)
.Distinct()
.ToList();
bool result = false;
if (_authorisationService != null)
{
var principal = _authorisationService.ClientSecurityPrincipal;
if (principal == null)
{
throw new SecurityException("Access Denied. You are not logged in");
}
// If Authenticated is enforced then revoke if user is not authenticated
if (Authenticated && !_authorisationService.IsAuthenticated)
{
throw new SecurityException("Access Denied. You are not authenticated");
}
// Allow if the principal is in any of the roles
result = _roles.Any(principal.IsInRole);
if (!result)
{
throw new SecurityException("Access Denied. You are not in an allowed Role");
}
}
return result ? _allow : _revoke;
}
}
}
Here's the method with the attribute
[RolePermission(SecurityAction.Demand, Authenticated = true, Roles = "Admin")]
private void barButtonItemConfig_ItemClick(object sender, ItemClickEventArgs e)
{
// Do stuff
}

Ok I've figured out how it works. CreatePermission is actually only called once. The IPermission that is returned is the class that checks if the user is in the required role or not.
Because I was returning an unrestricted allow for user A, user B got the same access regardless of their roles.
I need to create my own class that implements IPermission and move my logic into the Demand method. Alternatively (the easier option) assign the Principal from my service to Thread.CurrentPrincipal and use the out of the box PrincipalPermissionAttribute.

Related

How can I create attribute to check if the user has a claim with Identity core 2.2?

I have an application written with C# on the top on ASP.NET Core 2.2 framework.
I want to be able to check if a user has a claim before I allow them access to the action.
I created an AuthorizationHandler to check if the user has the claim like so
public class ClaimExistanceHandler : AuthorizationHandler<MustHaveClaimRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MustHaveClaimRequirement requirement)
{
if (context == null
|| context.User == null
|| context.User.Identity == null
|| !context.User.Identity.IsAuthenticated
|| requirement == null
|| string.IsNullOrWhiteSpace(requirement.Type)
|| context.User.HasClaim(requirement.Type, requirement.Value))
{
context.Fail();
}
else
{
context.Succeed(requirement);
}
await Task.Yield();
}
}
}
then the requirement is as follow
public class MustHaveClaimRequirement : IAuthorizationRequirement
{
public string Type { get; set; }
public string Value { get; set; }
public MustHaveClaimRequirement(string type, string value)
{
Type = type;
Value = value;
}
}
But how can I call this requirement as an attribute? For example HasPermission("do something", "1")
It seems that my HasPermission class needs to implement the AuthorizeAttribute but not sure how would I call the handler from the attribute.
Your primary goal here is to get the requirement into a policy, and then use or create an attribute that can specify that policy with a string name. Once you do that, don't need to worry about calling the handler yourself, because ASP.NET Core will take care of that for you.
The simplest method of creating policies is to do it on app startup, as documented here. You create your policies, then use AuthorizeAttribute to specify which policy to attach to each endpoint.
However, doing it this way requires you to define all your policies up front. If that would result in you needing to create tons of different policies (because you are going to be checking lots of different claim types), and what you really want is to be able to have an attribute that specifies the claim information, there is a more dynamic way of doing it: see here. You need to create an AuthorizeAttribute implementation that stuffs your parameter values (name and type) into a string, and create and register an IAuthorizationPolicyProvider that can interpret that string and generate a policy with the appropriate requirement.
EDIT: It's also worth pointing out that ASP.NET Core already includes a requirement implementation for checking a claim: ClaimsAuthorizationRequirement. AuthorizationPolicyBuilder has a shortcut for it (RequireClaim) so you can quickly create policies that check claims:
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});
First, you need to register policy and their related claims:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
//Scenario 0: Policy requires Claim0 without care what the value is
options.AddPolicy("MyPolicy0", policy => policy.RequireClaim("Claim0"));
//Scenario 1: Policy requires Claim1 with value ClaimValue1_1 OR ClaimValue 1_2
options.AddPolicy("MyPolicy1", policy => policy.RequireClaim("Claim1", "ClaimValue1_1", "ClaimValue1_2"));
//Scenario 2: Policy requires Claims2 AND Claim3 with particular values
options.AddPolicy("MyPolicy2", policy => {
policy.RequireClaim("Claim2", "ClaimValue2");
policy.RequireClaim("Claim3", "ClaimValue3"));
}
//Scenario 3: Policy requires Claims4 OR Claim5 with particular values
options.AddPolicy("MyPolicy3", policy => {
policy.RequireAssertion(ctx =>
{
return ctx.User.HasClaim("Claim4", "ClaimValue4") ||
ctx.User.HasClaim("Claim5", "ClaimValue5");
})
}
});
}
Then apply these checks whenever you need (can be applied at controller or action level):
[Authorize(Policy = "Policy1")]
public class HomeController : Controller
{
[Authorize(Policy = "Policy2")]
public ActionResult MyAction()
{
...
}
[Authorize(Policy = "Policy3")]
public ActionResult MyAnotherAction()
{
...
}
[AllowAnonymous]
public ActionResult NotSecuredAtAll()
{
...
}
}
Don't forget, if you apply multiple policies to a controller or action, then all policies must pass before access is granted (if only they don't have AllowAnonymous attribute).
More about claims-based authorization in ASP.NET Core 2.2
I managed to write up the attribute which allows checking for claim existence after taking the feedback from nlawalker and ivamax9
In conclusion, the HasPermissionAttribute class takes claimType and an optional claimValue then creates a policy name. The attribute class looks like this
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HasPermissionAttribute : AuthorizeAttribute
{
public const string Policy_Prefix = "HasClaim";
public const string Policy_Glue = ".";
public HasPermissionAttribute(string type, string value = null)
{
Policy = GetPolicyValue(type, value);
}
private string GetPolicyValue(string type, string value)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new ArgumentNullException($"{type} cannot be null.");
}
List<string> parts = new List<string> { type.Replace(Policy_Glue, "_").Trim() };
if (!string.IsNullOrWhiteSpace(value))
{
parts.Add(value.Replace(Policy_Glue, "_"));
}
string policy = $"{Policy_Prefix}{Policy_Glue}{string.Join(Policy_Glue, parts)}";
return policy;
}
}
Now that we have a policy being applied via the HasPermissionAttribute we now need to take the applied policy and register it using AuthorizationPolicyBuilder which check if the given claim exists or not. That said, I added a class called ClaimCheckerPolicyProvider which takes the provided claim and processing the check as follow
internal class ClaimCheckerPolicyProvider : IAuthorizationPolicyProvider
{
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (IsClaimBasePolicy(policyName))
{
string[] parts = GetParts(policyName);
if (parts.Length > 0)
{
AuthorizationPolicyBuilder policy = GetPolicyBuilder(parts);
return Task.FromResult(policy.Build());
}
}
return Task.FromResult<AuthorizationPolicy>(null);
}
private bool IsClaimBasePolicy(string policyName)
{
return !string.IsNullOrWhiteSpace(policyName)
&& policyName.StartsWith(HasPermissionAttribute.Policy_Prefix, StringComparison.OrdinalIgnoreCase);
}
private string[] GetParts(string policyName)
{
return policyName.Split(HasPermissionAttribute.Policy_Glue, StringSplitOptions.RemoveEmptyEntries)
.Where(x => !x.Equals(HasPermissionAttribute.Policy_Prefix))
.ToArray();
}
private AuthorizationPolicyBuilder GetPolicyBuilder(string[] parts)
{
if (parts == null)
{
throw new ArgumentNullException($"{nameof(parts)} cannot be null.");
}
var length = parts.Length;
if (length == 0)
{
throw new ArgumentOutOfRangeException($"{nameof(parts)} cannot cannot be empty.");
}
var policy = new AuthorizationPolicyBuilder();
if (length > 1)
{
return policy.RequireClaim(parts[0], parts[1]);
}
return policy.RequireClaim(parts[0]);
}
}
Finally, we need to register the provider as a service. In the ConfigureServices of the Startup class, we add the following
services.AddTransient<IAuthorizationPolicyProvider, ClaimCheckerPolicyProvider>();

how to implement this mvc filter attribute?

I need to integrate a new Security API into several existing applications in my organization. Some applications use ASP.NET MVC and use the .NET AuthorizeAttribute class to decorate classes with security.
For example:
[Authorize(Roles="MY_CORP\Group1,MY_CORP\Group2")]
public class MyClass
{
//
}
The code above is based on a Windows authentication configuration. I need to update this implementation to use the new Security API. The new Security API will retrieve a user like this:
var user = new SecurityApi().GetUser(userId);
var groups = user.Groups;
So ideally the updated decorator would look something like this, where GroupX and GroupY exist as user.Groups returned from the Security API:
[Authorize(Roles="GroupX, GroupY")]
public class MyClass
{
//
}
Any idea how I would go about implementing this?
I use something along the lines of this:
public class RequireAuthAttribute : TypeFilterAttribute
{
public RequireAuthAttribute(params Roles[] rolesRequirement)
: base(typeof(RequireAuthFilter))
{
Arguments = new object[] { rolesRequirement };
}
public enum Roles: ushort
{
CompanyOnly,
AuthenticatedCustomer,
AuthorizedCustomer,
AuthorizedOwnerManager
}
}
With:
public class RequireAuthFilter : IAsyncActionFilter
{
private readonly Roles[] _rolesToAllow;
public RequireAuthFilter(Roles[] rolesRequirement = default(Roles[]))
{
_rolesToAllow = rolesRequirement;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next )
{
// Verify is Authenticated
if (context.HttpContext.User.Identity.IsAuthenticated != true)
{
context.HttpContext.SetResponse(401, "User is not Authenticated");
return;
}
var isCompanyAdmin = context.HttpContext.IsCompanyAdmin();
// ^ HttpContext Extension method that looks at our JWT Token
// and determines if has required Cliams/Roles.
if (isCompanyAdmin == true)
{
await next();
return;
} else {
context.HttpContext.SetResponse(401, "Restricted to Company");
return;
}
// Other custom logic for each role.
// You will want to decide if comma represents AND or an OR
// when specifying roles.
}
}
And use like this:
[RequireAuth(Roles.CompanyOnly, Roles.AuthorizedOwnerManager)]
public class MyClass
{
//
}

DI into a Requirement/Policy in ASP.NET MVC 6

I'm looking for a way to program a custom authorization filter in ASP.NET 5 as the current implementation relies in Policies/Requirements which in turn rely solely in the use of Claims, thus on the umpteenth and ever-changing Identity System of which I'm really tired of (I've tried all it's flavors).
I have a large set of permissions (over 200) which I don't want to code as Claims as I have my own repository for them and a lot faster way to be check against it than comparing hundreds of strings (that is what claims are in the end).
I need to pass a parameter in each attribute that should be checked against my custom repository of permissions:
[Authorize(Requires = enumPermission.DeleteCustomer)]
I know this is not the most frequent scenario, but I think it isn't an edge case. I've tried implementing it in the way described by #leastprivilege on his magnificent post "The State of Security in ASP.NET 5 and MVC 6: Authorization", but I've hit the same walls as the author, who has even opened an issue on the ASP.NET 5 github repo, which has been closed in a not too much clarifying manner: link
Any idea of how to achieve this? Maybe using other kind of filter? In that case, how?
Following is an example of how you can achieve this scenario:
Let's assume you have a service called IPermissionStore which validates if a given user has the required permissions specified on the attribute.
public class MyCustomAuthorizationFilterAttribute : Attribute, IFilterFactory, IOrderedFilter
{
private readonly Permision[] _permissions;
public MyCustomAuthorizationFilterAttribute(params Permision[] permissions)
{
_permissions = permissions;
}
public int Order { get; set; }
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var store = serviceProvider.GetRequiredService<IPermissionStore>();
return new MyCustomAuthorizationFilter(store, _permissions)
{
Order = Order
};
}
}
public class MyCustomAuthorizationFilter : IAuthorizationFilter, IOrderedFilter
{
private readonly IPermissionStore _store;
private readonly Permision[] _permissions;
public int Order { get; set; }
public MyCustomAuthorizationFilter(IPermissionStore store, params Permision[] permissions)
{
_store = store;
_permissions = permissions;
}
public void OnAuthorization(AuthorizationContext context)
{
// Check if the action has an AllowAnonymous filter
if (!HasAllowAnonymous(context))
{
var user = context.HttpContext.User;
var userIsAnonymous =
user == null ||
user.Identity == null ||
!user.Identity.IsAuthenticated;
if (userIsAnonymous)
{
Fail(context);
}
else
{
// check the store for permissions for the current user
}
}
}
private bool HasAllowAnonymous(AuthorizationContext context)
{
return context.Filters.Any(item => item is Microsoft.AspNet.Authorization.IAllowAnonymous);
}
private void Fail(AuthorizationContext context)
{
context.Result = new HttpUnauthorizedResult();
}
}
// Your action
[HttpGet]
[MyCustomAuthorizationFilter(Permision.CreateCustomer)]
public IEnumerable<string> Get()
{
//blah
}

Unable to access custom property in a custom principal

I'm using windows authentication and custom roles. I've extended the WindowsPrincipal because I want to include additional information about the user based on a User class I've added. When I run the application, it sees the CustomPrincipal assigned to the built-in User, but not the additional "user" property I've added. I'm sure I'm doing something really dumb, but this is my first run into the C# ASP world and could really appreciate some help. Here is my custom principal and global.asax
Custom principal:
public class CustomPrincipal : WindowsPrincipal
{
List<string> _roles;
private User thisUser = new User();
public CustomPrincipal(WindowsIdentity identity)
: base(identity)
{
_roles = new List<string>(Roles.GetRolesForUser());
user = thisUser.getDarUser(identity.Name);
}
public User user { get; private set; }
public override bool IsInRole(string role)
{
if (base.IsInRole(role) || _roles.Contains(role) || _roles.Contains("Admin") || _roles.Contains("Dev"))
{
return true;
}
else
{
return false;
}
}
}
And Global.asax:
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
IIdentity thisId = User.Identity;
WindowsIdentity wi = (WindowsIdentity)thisId;
CustomPrincipal cp = new CustomPrincipal(wi);
HttpContext.Current.User = cp;
Thread.CurrentPrincipal = cp;
}
}
Thanks again for any direction.
Looking into it a little more I found the fail was in how I was trying to access the principal in my views. Thanks to ASP.NET MVC - Set custom IIdentity or IPrincipal, while this was not related to my Windows Auth type project, it did lead me to the correct usage of my principal.
What I was doing WRONG:
#User.user.myproperty
Change to:
#((User as CustomPrinicpal).user.myproperty
Hopefully this helps another newb not make the same bonehead mistake

How can I make accessing my custom IPrincipal easier in ASP.NET MVC?

I've written a custom principal object which contains a few additional fields (email and userid in addition to the username).
In order to access these properties I have to cast the Context.User object as my custom principal.
#Html.GetGravitarImage((User as CustomPrincipal).Email)
This custom principal is created / deserialized via the Application_AuthenticateRequest in my global.ascx. You can see this question I asked here for more information.
private void Application_AuthenticateRequest(Object source, EventArgs e)
{
var application = (HttpApplication)source;
var context = application.Context;
// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = context.Request.Cookies[cookieName];
if (authCookie == null)
return;
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
context.User = CustomPrincipal.CreatePrincipalFromCookieData(authTicket.UserData);
}
However, if a user isn't authenticated, then my cast to CustomPrincipal will fail (because it won't be injected in the method above) and the result of the (User as CustomPrincipal) will return null, thus giving me a null reference exception when my method above attempts to get the email.
What would be a clean solution to this problem? I want to make accessing my custom principal easy and having to do the following seems cumbersome:
#Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")
Is this the only way to handle this situation?
I whipped something together quickly. One possible way of easily introducing a custom IPrincipal in ASP.NET MVC is the following:
1) Create your own descendant of the IPrincipal interface.
public interface IMyPrincipal : IPrincipal
{
Guid UserId { get; }
string EmailAddress { get; }
}
2) Let's assume you are using the ASP.NET Membership provider to authenticate your users. Let's quickly build an IMyPrincipal implementation which utilizes the membership API.
public class MyPrincipal : IMyPrincipal
{
private MembershipUser _user;
public MyPrincipal()
{
this._user = Membership.GetUser();
var userName = this._user != null ? this._user.UserName : String.Empty;
this.Identity = new GenericIdentity(userName);
}
public Guid UserId
{
get
{
return this._user != null ? (Guid) this._user.ProviderUserKey :
default(Guid);
}
}
public string EmailAddress
{
get
{
return this._user != null ? this._user.Email : null;
}
}
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
}
3) Create your own base class type for your controllers. Hide the inherited User member and introduce your own IPrincipal descendant.
public class BaseController : Controller
{
protected virtual new MyPrincipal User
{
get { return HttpContext.User as MyPrincipal; }
}
}
4) Have all your controllers descend from this new BaseController type.
public class HomeController : BaseController
{
//...
}
5) Create your own controller factory to make sure your principal is introduced on the HttpContext / Thread.
public class MyControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance
(RequestContext requestContext, Type controllerType)
{
try
{
var controller = base.GetControllerInstance(requestContext, controllerType);
requestContext.HttpContext.User = Thread.CurrentPrincipal = new
MyPrincipal();
return controller;
}
catch (Exception)
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
}
6) Register the controller factory in the Global.asax's Application_Start() event handler.
var controllerFactory = new MyControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
Voila, now you can use the new User (IMyPrincipal) anywhere in your controllers.
For example:
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewBag.UserId = User.UserId;
ViewBag.UserName = User.EmailAddress;
return View();
}
You could either create a base class and override the "User" property using the "new" keyword or create an extension method like this:
public static class ControllerExtensions
{
public static CustomPrincipal CustomPrincipal(this Controller controller)
{
if(controller.User is CustomPrincipal)
{
return controller.User as CustomPrincipal;
}
return null; // maybe return an empty object instead to get around null reference...
}
}
The best way to make your IPrincipal implementation accessible in your Razor pages using ASP.NET MVC, is doing the following:
Implement the System.Security.Principal.IPrincipal interface.
Implement the System.Security.Principal.IIdentity interface.
In Global.asax define a method for: void Application_AuthenticateRequest(Object, EventArgs) that persists your both implementations of IPrincipal and IIdentity.
Create an extension method for IPrincipal to expose your implementation of IIdentity.
Finally, add the namespace for the previous extension method in your web.config file in <system.web.webPages.razor>.
At the end, you will be able to access your custom implementation of IIdentity instead of type casting. You now can access your custom implementation like this:
Hello #User.CustomIdentity().FirstName #User.CustomerIdentity().LastName!
These steps are a concise and brief description of a well detailed article written here: http://rizzo7.blogspot.com/2012/04/mvc-30-razor-custom-principal-and.html
You could create some sort of utility method or add a method to one of your services that checks if it's your custom principal. Maybe something like:
public class UserService : IUserService
{
public CustomPrincipal CurrentUser
{
get
{
CustomPrincipal user = HttpContext.Current.User as CustomPrincipal;
if (user == null)
return GuestUser; // Just some default user object
return user;
}
}
}
You could also make extension methods for the Email and UserID in the same fashion as John Kalberer's answer:
public static class CustomPrincipalExtensions
{
public static string Email(this CustomPrincipal cUser)
{
return cUser != null ? cUser.Email : "Default Email"
}
public static string UserID(this CustomPrincipal cUser)
{
return cUser != null ? cUser.UserID : "Default ID"
}
}
When not authorized, you could set the user object to a specific instance of the custom principal with default values:
if (authCookie == null)
{
context.User = CustomPrincipal.Default; // Or CreateDefault()
return;
}

Categories