DotNet Core Custom Authorize Attribute using Route Params - c#

I googled this and nothing was found, so I would like to know from those who use DotNet Core for a longer time than myself.
I am now to DotNet core. I am currently creating an App just for the sake of practicing. I noticed that in most of my Apis' Actions, I am verifying the passed in UserId against claims NameIdentifier (that is the logged in user id).
I am doing like so:
if (userId != int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value))
{
return Unauthorized();
}
But now thinking, it is getting a bit too repetitive. Is there a way of using an Attribute instead?
Something like:
[AuthorizeUser(UserId = userid)]
[HttpGet]
public async Task<IActionResult> GetSomething(int userId)
{
//Custom code ...
}
And then create my Authorize Attribute:
public class AuthorizeUser : AuthorizeAttribute, IAuthorizationFilter
{
public AuthorizeUser(params string[] args)
{
Args = args;
}
public string[] Args { get; }
public void OnAuthorization(AuthorizationFilterContext context)
{
//Custom code ...
}
}
This way I would check for the user id passed in the "api/user/{userId}" in a single place for all my Actions.
Or there is another way of making my code look nicer and with less copy and paste?
Thank you in advance.

I was able to solve my problem by doing the following:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class UserAuthorizationAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// Here I can get userId from my params.
var userId = context.RouteData.Values["userId"].ToString();
// It is then being checked against current user claims.
// The user is only authorized if the userId is equals to ClaimsType.Value and claims Type is equals to NameIdentifier.
var isUserAuthorized = context.HttpContext.User.Claims.Any(c => c.Type == ClaimTypes.NameIdentifier && c.Value == userId);
if (!isUserAuthorized)
{
context.Result = new UnauthorizedResult();
}
}
}

Related

Custom parameter in Authorization c#

I have a problem, I need to do something like this in order to protect my API based on permission code.
Into the "OnAuthorization" i check the jwtToken, db and soon.
[AuthorizePermissionByCode("fr")]
[HttpGet]
public int Get()
{
DO STUFF
}
I have write a class in order to manage AuthorizePermissionByCode
namespace CustomAuthorizeAttribute
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizePermissionByCode : AuthorizeAttribute, IAuthorizationFilter
{
public string Permissions { get; set; } //Permission string to get from controller
public AuthorizePermissionByCode(string Permissions)
{
this.Permissions = Permissions;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
I have no error, build success and soon, but never enter into "OnAuthorization".
I need to edit in some way my startup?
I put:
services.AddAuthentication();
services.AddAuthorization();
Not particularly sure of your intentions but i haven done something close in time pass.
Take a look at this code and refactor where appropriate.
https://www.c-sharpcorner.com/UploadFile/56fb14/custom-authorization-in-mvc/

Custom Authorize attribute role is not working web api authentication

I'm facing an issue while working with web api azure ad authentication
I'm having controller like below, the one which having giving proper response, But the one which having customauthorization roles throwing error as "Authentication has been for this request".
[RoutePrefix("api/hospitals")]
public class hospitals : ApiController
{
[Route("GetAll")]
[HttpGet]
[Authorize]
public async Task<IEnumerable<Hospitals>> GetAll()
{
// return ok;
}
[Route("Getbeds")]
[HttpGet]
[SmAuthorize(Constants.Roles.Admin,
Constants.Roles.HotSpitalAdmin,
Constants.Roles.QA)]
public async Task<IEnumerable<Hospitals>> Getbeds()
{
// return ok;
}
}
The Getbeds method is throwing an error as "Authorization has been request".
Please find me Custom attribute class as well
public class SmAuthorizeAttribute : AuthorizeAttribute
{
public SmAuthorizeAttribute(params string[] roles)
{
this.Roles = string.Join(",", roles.Select(s => s.Trim()).ToArray());
}
}
Can anyone help on this ?
You can refer to this SO question's answer by Derek Greer for Dot Net core, additionally I will reiterate the answer below -
The approach recommended by the ASP.Net Core team is to use the new policy design which is fully documented here. The basic idea behind the new approach is to use the new [Authorize] attribute to designate a "policy" (e.g. [Authorize( Policy = "YouNeedToBe18ToDoThis")] where the policy is registered in the application's Startup.cs to execute some block of code (i.e. ensure the user has an age claim where the age is 18 or older).
The policy design is a great addition to the framework and the ASP.Net Security Core team should be commended for its introduction. That said, it isn't well-suited for all cases. The shortcoming of this approach is that it fails to provide a convenient solution for the most common need of simply asserting that a given controller or action requires a given claim type. In the case where an application may have hundreds of discrete permissions governing CRUD operations on individual REST resources ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder", etc.), the new approach either requires repetitive one-to-one mappings between a policy name and a claim name (e.g. options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));), or writing some code to perform these registrations at run time (e.g. read all claim types from a database and perform the aforementioned call in a loop). The problem with this approach for the majority of cases is that it's unnecessary overhead.
While the ASP.Net Core Security team recommends never creating your own solution, in some cases this may be the most prudent option with which to start.
The following is an implementation which uses the IAuthorizationFilter to provide a simple way to express a claim requirement for a given controller or action:
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
if (!hasClaim)
{
context.Result = new ForbidResult();
}
}
}
[Route("api/resource")]
public class MyController : Controller
{
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[HttpGet]
public IActionResult GetResource()
{
return Ok();
}
}
Part of this answer for .NET Framework-
Recommended Custom Attribute class:
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
private readonly PermissionAction[] permissionActions;
public CustomAuthorize(PermissionItem item, params PermissionAction[] permissionActions)
{
this.permissionActions = permissionActions;
}
protected override Boolean IsAuthorized(HttpActionContext actionContext)
{
var currentIdentity = actionContext.RequestContext.Principal.Identity;
if (!currentIdentity.IsAuthenticated)
return false;
var userName = currentIdentity.Name;
using (var context = new DataContext())
{
var userStore = new UserStore<AppUser>(context);
var userManager = new UserManager<AppUser>(userStore);
var user = userManager.FindByName(userName);
if (user == null)
return false;
foreach (var role in permissionActions)
if (!userManager.IsInRole(user.Id, Convert.ToString(role)))
return false;
return true;
}
}
}

Role based Authorization without Entity Framework?

I love the way asp.net identity lets you neatly manage role based authorizations by just adding an annotation at the top of your controller method. But what if you are not using entity framework? What if you are using ADO.NET with ASP.NET Core API? How do you manage Role Based Authorization?
[Authorize(Roles = Role.Admin)]
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
Perhaps we could mimic this by creating ActionFilter and checking what is the value of global variable that holds user's role? What are my options?
This is a great case for policy-based authorization. You'll use a custom IAuthorizationHandler and IAuthorizationRequirement. See the documentation here: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.0
I've done this in a project recently. You create named policies, in your case something like "Admin". Your AuthorizationHandler will access the current user identity and can look up their current roles by injecting your services into the handler. Then you decorate your controller / actions with [Authorize(Policy = "Admin")].
In my project, my AuthorizationHandler and AuthorizationRequirement look like this:
public class CreateClientHandler : AuthorizationHandler<CreateClientRequirement, CreateClientRequest>, IAuthorizationRequirement
{
private readonly IStudioService _studioService;
public CreateClientHandler(
IStudioService studioService
)
{
_studioService = studioService;
}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CreateClientRequirement requirement,
CreateClientRequest resource
)
{
var userIdClaim = context.User.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Name);
if (userIdClaim == null)
{
context.Fail();
return Task.CompletedTask;
}
if (resource != null)
{
var userId = int.Parse(userIdClaim.Value);
if (!UserIsAdmin(userId))
{
context.Fail();
return Task.CompletedTask;
}
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class CreateClientRequirement : IAuthorizationRequirement {}
In my ConfigureServices, in Startup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("CreateClient", policy =>
policy.Requirements.Add(new CreateClientRequirement()));
});
Once everything is configured, I can decorate controller actions with [Authorize(Policy = "CreateClient")].

AuthorizationPolicy by UserName was not found

I'm trying to add a new policy based authorization for a certain user who needs to execute certain actions of a controller that requires rights. I'm new with Policy based authorization but I followed all the instructions on this post, and seems pretty simple.
In my Startup.cs and after the AddMvc() method I have:
services.AddAuthorization(options =>
{
options.AddPolicy("AgentsActivityReport ", policy => policy.RequireUserName("AnaR"));
});
Then, In my controller action I have:
[Authorize(Policy = "AgentsActivityReport")]
public ActionResult AgentsActivity()
{
//some code
}
However, when I launch the application, I receive the following error:
InvalidOperationException: The AuthorizationPolicy named:
'AgentsActivityReport' was not found.
I have also readed a few other threads/posts such as:
Claim Based And Policy-Based Authorization With ASP.NET Core 2.1
reported issue
And everything seems pretty much correct. Any thougts?
Based on this post, I was able to declare and allow my user to invoke an action of a controller by using a requirement. Since my condition has to be, "allow certain roles OR a certain user named AnaR", I had to put that logic into the AuthorizationHandler.cs
Startup.cs
services.AddAuthorization(options => {
options.AddPolicy("ReportActivityPolicy", policy =>
{
policy.AddRequirements(new UserNameRequirement("AnaR"));
});
});
services.AddSingleton<IAuthorizationHandler, AgentsActivityAuthorizationHandler>();
And then, in a separate file:
public class AgentsActivityAuthorizationHandler : AuthorizationHandler<UserNameRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserNameRequirement requirement)
{
if (context.User.IsInRole("Administrator") || context.User.IsInRole("Manager") || context.User.Identity.Name == requirement.UserName)
{
context.Succeed(requirement);
}
return Task.FromResult(0);
}
}
public class UserNameRequirement : IAuthorizationRequirement
{
public UserNameRequirement(string username)
{
this.UserName = username;
}
public string UserName { get; set; }
}
Then, in my controller, the following:
[Authorize(Policy = "ReportActivityPolicy")]
public ActionResult AgentsActivity()
{
//code of your controller.
}
Hope it helps!
It seems that you register a policy named "AgentsActivityReport " with a white space at the end in the middleware but annotate the controller without the whitespace "AgentsActivityReport".

How to implement Permission Based Access Control with Asp.Net Core

I am trying to implement permission based access control with aspnet core. For dynamically managing user roles and permissions(create_product, delete_product etc.), they are stored in the database. Data Model is like http://i.stack.imgur.com/CHMPE.png
Before aspnet core (in MVC 5) i was using custom AuthorizeAttribute like below to handle the issue:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private readonly string _permissionName { get; set; }
[Inject]
public IAccessControlService _accessControlService { get; set; }
public CustomAuthorizeAttribute(string permissionName = "")
{
_permissionName = permissionName;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var user = _accessControlService.GetUser();
if (PermissionName != "" && !user.HasPermission(_permissionName))
{
// set error result
filterContext.HttpContext.Response.StatusCode = 403;
return;
}
filterContext.HttpContext.Items["CUSTOM_USER"] = user;
}
}
Then i was using it in action method like below:
[HttpGet]
[CustomAuthorize(PermissionEnum.PERSON_LIST)]
public ActionResult Index(PersonListQuery query){ }
Additionally, i was using HttpContext.Items["CUSTOM_USER"] in views to show or hide html part:
#if (CurrentUser.HasPermission("<Permission Name>"))
{
}
When i decided to switch aspnet core, all my plan was failed. Because there was no virtual OnAuthorization method in the AuthorizeAttribute. I tried some ways to solve problem. Those are below:
Using new policy based authorization(i think it is not suitable for
my scenerio)
Using custom AuthorizeAttribute and AuthorizationFilter(i read this
post https://stackoverflow.com/a/35863514/5426333 but i couldn’t change it properly)
Using custom middleware(how to get AuthorizeAttribute of current
action?)
Using ActionFilter(is it correct for security purpose?)
I couldn’t decide which way is the best for my scenerio and how to implement it.
First question: Is MVC5 implementation bad practice?
Second question: Do you have any suggest to implement aspnet core?
Based on the comments, here an example on how to use the policy based authorization:
public class PermissionRequirement : IAuthorizationRequirement
{
public PermissionRequirement(PermissionEnum permission)
{
Permission = permission;
}
public PermissionEnum Permission { get; }
}
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
private readonly IUserPermissionsRepository permissionRepository;
public PermissionHandler(IUserPermissionsRepository permissionRepository)
{
if(permissionRepository == null)
throw new ArgumentNullException(nameof(permissionRepository));
this.permissionRepository = permissionRepository;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
if(context.User == null)
{
// no user authorizedd. Alternatively call context.Fail() to ensure a failure
// as another handler for this requirement may succeed
return null;
}
bool hasPermission = permissionRepository.CheckPermissionForUser(context.User, requirement.Permission);
if (hasPermission)
{
context.Succeed(requirement);
}
}
}
And register it in your Startup class:
services.AddAuthorization(options =>
{
UserDbContext context = ...;
foreach(var permission in context.Permissions)
{
// assuming .Permission is enum
options.AddPolicy(permission.Permission.ToString(),
policy => policy.Requirements.Add(new PermissionRequirement(permission.Permission)));
}
});
// Register it as scope, because it uses Repository that probably uses dbcontext
services.AddScope<IAuthorizationHandler, PermissionHandler>();
And finally in the controller
[HttpGet]
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]
public ActionResult Index(PersonListQuery query)
{
...
}
The advantage of this solution is that you can also have multiple handlers for a requirement, i.e. if first one succeed the second handler can determine it's a fail and you can use it with resource based authorization with little extra effort.
The policy based approach is the preferred way to do it by the ASP.NET Core team.
From blowdart:
We don't want you writing custom authorize attributes. If you need to do that we've done something wrong. Instead you should be writing authorization requirements.
I had same requirement and i have done it as below and it works fine for me. I am using .Net Core 2.0 Webapi
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Method
, AllowMultiple = true
, Inherited = true)]
public class CheckAccessAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private string[] _permission;
public CheckAccessAttribute(params string[] permission)
{
_permission = permission;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
return;
}
IRepository service =
(IRepositoryWrapper)context.HttpContext.RequestServices.GetService(typeof(IRepository));
var success = service.CheckAccess(userName, _permission.ToList());
if (!success)
{
context.Result = JsonFormatter.GetErrorJsonObject(
CommonResource.error_unauthorized,
StatusCodeEnum.Forbidden);
return;
}
return;
}
}
In Controller use it like below
[HttpPost]
[CheckAccess(Permission.CreateGroup)]
public JsonResult POST([FromBody]Group group)
{
// your code api code here.
}
For a solution that doesn't require you to add a policy for each permission see my answer for another question.
It lets you decorate your Controllers and Actions with any custom attributes you wish, and access them in your AuthorizationHandler.

Categories

Resources