Reputation: 776
I've seen basically the same question answered for net core 2.2 on ASP Net Forum. When I tried to implement for my net core 3.1 application, I'm getting an error
System.InvalidCastException: Unable to cast object of type 'System.Security.Claims.ClaimsIdentity' to type 'System.Security.Principal.WindowsIdentity'
The error happens on the RoleAuthHandler.cs trying to cast Identity to WindowsIdentity.
I'm wondering if there has been change between core 2.2 and 3.1 that explains this or I'm doing something wrong. Any help would be great.
Startup.cs contains
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IAuthorizationHandler, RoleAuthHandler>();
services.AddAuthorization(options =>
options.AddPolicy(
Role.Admin,
policy => policy.AddRequirements(new RoleRequirement(Role.Admin))));
}
Role.cs
public static class Role
{
public const string Guest = "Guest";
public const string Admin = "Admin";
}
RoleRequirement.cs
public class RoleRequirement : IAuthorizationRequirement
{
public string[] Roles { get; }
public RoleRequirement(params string[] roles)
{
this.Roles = roles;
}
}
RoleAuthHandler.cs contains
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RoleRequirement requirement)
{
var wi = (WindowsIdentity)context.User.Identity;
var groupSet = new HashSet<string>();
if (wi.Groups != null)
{
foreach (var group in wi.Groups)
{
groupSet.Add(group.Translate(typeof(NTAccount)).ToString());
}
}
string[] userRoles = roleService.GetRolesForGroup(groupSet);
var intersectRoles = Enumerable.Intersect(userRoles, requirement.Roles, StringComparer.OrdinalIgnoreCase);
if (intersectRoles.Count() > 0)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
Controller class
[Authorize]
[ApiController]
[Route("[controller]")]
public class InterestingController : ControllerBase
{
[HttpGet]
public string Get()
{
return "Something Interesting";
}
}
Upvotes: 2
Views: 2530
Reputation: 776
@Sayah imad - Thanks for the reminder. I had seen this solution on a post somewhere and dismissed it because the information I was looking for was "what group does the user belong to". What I didn't realize was that the information was availabile in the ClaimsIdentity. Your mention, made me think twice about it and I found my answer:
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RoleRequirement requirement)
{
var claimsIdentity = (ClaimsIdentity)context?.User.Identity;
var userGroups = claimsIdentity.Claims
.Where(x => x.Type.Contains("groupsid", StringComparison.OrdinalIgnoreCase))
.ToList();
if (userGroups == null || !userGroups.Any())
{
return Task.CompletedTask;
}
var groupNames = new HashSet<string>();
foreach (var group in userGroups)
{
var groupName = new SecurityIdentifier(group.Value)
.Translate(typeof(NTAccount))
.ToString();
groupNames.Add(groupName);
}
var userRoles = this.authenticationService.GetRoles(groupNames.ToArray());
// If the user is an Admin, always allow
if (userRoles.Contains(Role.Admin))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
var intersectingRoles = Enumerable.Intersect(
userRoles,
requirement?.Roles,
StringComparer.OrdinalIgnoreCase);
if (intersectingRoles?.Count() > 0)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
Upvotes: 1