Skip to content
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;

namespace Unity.GrantManager.Assessments;
public class AssessmentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Assessment>, ISingletonDependency
Expand Down Expand Up @@ -81,7 +82,8 @@ protected virtual async Task<bool> CheckPolicyAsync(string permissionName, Autho
{
Check.NotNull(principal, nameof(principal));

var userIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == "UserId");
var userIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId)
?? principal.Claims?.FirstOrDefault(c => c.Type == "UserId");
if (userIdOrNull == null || userIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Unity.Flex.Permissions;
using Unity.GrantManager.Identity;
using Unity.Modules.Shared;
using Unity.Modules.Shared.Permissions;
using Unity.Notifications.Permissions;
using Unity.Payments.Permissions;
using Volo.Abp.Authorization.Permissions;
Expand Down Expand Up @@ -99,6 +100,12 @@ public PermissionGrantsDataSeeder(IPermissionDataSeeder permissionDataSeeder)

public async Task SeedAsync(DataSeedContext context)
{
if (context.TenantId == null)
{
await SeedHostPermissionsAsync();
return;
}

// Default permission grants based on role

// - Program Manager
Expand Down Expand Up @@ -332,6 +339,30 @@ await _permissionDataSeeder.SeedAsync(RolePermissionValueProvider.ProviderName,
], context.TenantId);

}

private async Task SeedHostPermissionsAsync()
{
// ITAdministrator host-level permissions (previously stamped at login)
await _permissionDataSeeder.SeedAsync(RolePermissionValueProvider.ProviderName, IdentityConsts.ITAdminRoleName,
[
"UnityTenantManagement.Tenants",
"UnityTenantManagement.Tenants.Create",
"UnityTenantManagement.Tenants.Update",
"UnityTenantManagement.Tenants.Delete",
"AbpTenantManagement.Tenants.ManageFeatures",
"UnityTenantManagement.Tenants.ManageConnectionStrings",
Comment on lines +348 to +353
IdentitySeedPermissions.Users.Create,
IdentitySeedPermissions.UserLookup.Default,
IdentityConsts.ITAdminPermissionName
], tenantId: null);

// ITOperations host-level permissions (previously stamped at login)
await _permissionDataSeeder.SeedAsync(RolePermissionValueProvider.ProviderName, IdentityConsts.ITOperationsRoleName,
[
GrantManagerPermissions.Endpoints.ManageEndpoints,
IdentityConsts.ITOperationsPermissionName
], tenantId: null);
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;

namespace Unity.GrantManager.Web.Identity.Authorization;

public class PermissionOrRequirement : IAuthorizationRequirement
{
public string[] Permissions { get; }

public PermissionOrRequirement(params string[] permissions)
{
Permissions = permissions;
}
}

public class PermissionOrAuthorizationHandler : AuthorizationHandler<PermissionOrRequirement>, ITransientDependency
{
private readonly IPermissionChecker _permissionChecker;

public PermissionOrAuthorizationHandler(IPermissionChecker permissionChecker)
{
_permissionChecker = permissionChecker;
}

protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionOrRequirement requirement)
{
var result = await _permissionChecker.IsGrantedAsync(context.User, requirement.Permissions);

if (result.Result.Any(r => r.Value == PermissionGrantResult.Granted))
{
context.Succeed(requirement);
}
Comment on lines +28 to +37
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;

namespace Unity.GrantManager.Web.Identity.Authorization;

public class RoleOrPermissionRequirement : IAuthorizationRequirement
{
public string RoleName { get; }
public string PermissionName { get; }

public RoleOrPermissionRequirement(string roleName, string permissionName)
{
RoleName = roleName;
PermissionName = permissionName;
}
}

public class RoleOrPermissionAuthorizationHandler : AuthorizationHandler<RoleOrPermissionRequirement>, ITransientDependency
{
private readonly IPermissionChecker _permissionChecker;

public RoleOrPermissionAuthorizationHandler(IPermissionChecker permissionChecker)
{
_permissionChecker = permissionChecker;
}

protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RoleOrPermissionRequirement requirement)
{
if (context.User.IsInRole(requirement.RoleName))
{
context.Succeed(requirement);
return;
}

if (await _permissionChecker.IsGrantedAsync(context.User, requirement.PermissionName))
{
context.Succeed(requirement);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,22 @@ public virtual bool IsInRole(string roleName)
var userClaims = _principalAccessor.Principal?.Claims;
if (userClaims != null && userClaims.Any())
{
var userId = userClaims.FirstOrDefault(s => s.Type == "UserId");
// First try the IDIR-specific GUID claim
var idirGuid = userClaims.FirstOrDefault(s => s.Type == UnityClaimsTypes.IDirUserGuid);
if (idirGuid != null && Guid.TryParse(idirGuid.Value, out var guid))
{
return guid;
}

// Fallback to UserId claim (strip @azureidir suffix if present)
var userId = userClaims.FirstOrDefault(s => s.Type == AbpClaimTypes.UserId);
if (userId != null)
{
return Guid.Parse(userId.Value);
var value = userId.Value.Split('@')[0]; // Remove @azureidir suffix
if (Guid.TryParse(value, out guid))
{
return guid;
}
}
}
return null;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,20 @@
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Unity.GrantManager.Identity;
using Unity.Modules.Shared.Permissions;
using Unity.TenantManagement;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;

namespace Unity.GrantManager.Web.Identity.LoginHandlers
{
internal class IdentityProfileLoginAdminHandler : IdentityProfileLoginBase
{
internal readonly ImmutableArray<string> _adminPermissions = ImmutableArray.Create(
TenantManagementPermissions.Tenants.Default,
TenantManagementPermissions.Tenants.Create,
TenantManagementPermissions.Tenants.Update,
TenantManagementPermissions.Tenants.Delete,
TenantManagementPermissions.Tenants.ManageFeatures,
TenantManagementPermissions.Tenants.ManageConnectionStrings,
IdentityPermissions.Users.Create,
IdentityPermissions.UserLookup.Default,
IdentityConsts.ITAdminPermissionName
);

internal async Task<UserTenantAccountDto> Handle(TokenValidatedContext validatedTokenContext,
IList<UserTenantAccountDto> userTenantAccounts,
string? idp)
Expand All @@ -43,8 +30,8 @@ internal async Task<UserTenantAccountDto> Handle(TokenValidatedContext validated
userTenantAccount = userTenantAccounts.First(s => s.TenantId == null);
}

AssignAdminHostPermissions(validatedTokenContext.Principal!);
AssignDefaultClaims(validatedTokenContext.Principal!, userTenantAccount.DisplayName ?? string.Empty, userTenantAccount.Id);
(validatedTokenContext.Principal!.Identity as ClaimsIdentity)?.AddClaim(new Claim(AbpClaimTypes.Role, IdentityConsts.ITAdminRoleName));
return userTenantAccount;
}

Expand All @@ -63,11 +50,6 @@ private static bool AdminHasUserAccount(IList<UserTenantAccountDto>? userTenantA
return false;
}

private void AssignAdminHostPermissions(ClaimsPrincipal claimsPrincipal)
{
claimsPrincipal.AddPermissions(_adminPermissions);
}

private async Task<UserTenantAccountDto> CreateAdminAccountAsync(TokenValidatedContext validatedTokenContext, string? idp)
{
var token = validatedTokenContext.SecurityToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Volo.Abp.MultiTenancy;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Security.Claims;
using Microsoft.Extensions.Configuration;
using Volo.Abp.TenantManagement;

Expand All @@ -19,7 +19,6 @@ internal abstract class IdentityProfileLoginBase : ITransientDependency
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IdentityUserManager IdentityUserManager => LazyServiceProvider.LazyGetRequiredService<IdentityUserManager>();
protected IdentityRoleManager IdentityRoleManager => LazyServiceProvider.LazyGetRequiredService<IdentityRoleManager>();
protected PermissionManager PermissionManager => LazyServiceProvider.LazyGetRequiredService<PermissionManager>();
protected IIdentityUserRepository IdentityUserRepository => LazyServiceProvider.LazyGetRequiredService<IIdentityUserRepository>();
protected IConfiguration Configuration => LazyServiceProvider.LazyGetRequiredService<IConfiguration>();
protected IUserImportAppService UserImportAppService => LazyServiceProvider.LazyGetRequiredService<IUserImportAppService>();
Expand All @@ -29,7 +28,8 @@ internal abstract class IdentityProfileLoginBase : ITransientDependency
protected static void AssignDefaultClaims(ClaimsPrincipal claimsPrinicipal, string displayName, Guid userId)
{
claimsPrinicipal.AddClaim("DisplayName", displayName);
claimsPrinicipal.AddClaim("UserId", userId.ToString());
claimsPrinicipal.AddClaim(AbpClaimTypes.UserId, userId.ToString());
claimsPrinicipal.AddClaim("UserId", userId.ToString()); // Legacy claim for backward compatibility
claimsPrinicipal.AddClaim("Badge", Utils.CreateUserBadge(displayName));
}
Comment on lines 28 to 34

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,18 @@
using OpenIddict.Abstractions;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Unity.GrantManager.Identity;
using Unity.GrantManager.Permissions;
using Unity.GrantManager.Web.Exceptions;
using Unity.Modules.Shared.Permissions;
using Volo.Abp.Identity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Security.Claims;

namespace Unity.GrantManager.Web.Identity.LoginHandlers
{
internal class IdentityProfileLoginUserHandler : IdentityProfileLoginBase
{
internal readonly ImmutableArray<string> _userPermissions = [
GrantManagerPermissions.Default,
IdentityPermissions.UserLookup.Default
];

internal readonly ImmutableArray<string> _itOperationsPermissions = [
GrantManagerPermissions.Endpoints.ManageEndpoints,
IdentityConsts.ITOperationsPermissionName
];

internal async Task<UserTenantAccountDto> Handle(TokenValidatedContext validatedTokenContext,
IList<UserTenantAccountDto>? userTenantAccounts,
string? idp)
Expand Down Expand Up @@ -57,11 +43,6 @@ internal async Task<UserTenantAccountDto> Handle(TokenValidatedContext validated
}
}

if (validatedTokenContext.Principal != null && validatedTokenContext.Principal.IsInRole(IdentityConsts.ITOperationsRoleName))
{
AssignITOperationsPermissions(validatedTokenContext.Principal);
}

UserTenantAccountDto? userTenantAccount = null;
var setTenant = validatedTokenContext.Request.Cookies["set_tenant"];
if (setTenant != null && setTenant != Guid.Empty.ToString())
Expand All @@ -84,32 +65,18 @@ internal async Task<UserTenantAccountDto> Handle(TokenValidatedContext validated
{
var dbRole = await IdentityRoleManager.GetByIdAsync(role.Id);
principal.AddClaim(UnityClaimsTypes.Role, dbRole.Name);
principal.AddClaim(AbpClaimTypes.Role, dbRole.Name);
}
}

var userPermissions = (await PermissionManager.GetAllForUserAsync(userTenantAccount.Id)).Where(s => s.IsGranted);

foreach (var permissionName in userPermissions
.Select(s => s.Name)
.Where(permissionName => !principal.HasClaim(UnityClaimsTypes.Permission, permissionName)))
{
principal.AddClaim(UnityClaimsTypes.Permission, permissionName);
}
}

AssignDefaultPermissions(validatedTokenContext.Principal!);
AssignDefaultClaims(validatedTokenContext.Principal!, userTenantAccount.DisplayName ?? string.Empty, userTenantAccount.Id);

validatedTokenContext.Principal!.AddClaim(AbpClaimTypes.TenantId, userTenantAccount.TenantId?.ToString() ?? Guid.Empty.ToString());

return userTenantAccount;
}

private void AssignITOperationsPermissions(ClaimsPrincipal claimsPrincipal)
{
claimsPrincipal.AddPermissions(_itOperationsPermissions);
}

private async Task<IList<UserTenantAccountDto>> AutoRegisterUserWithDefaultAsync(string userIdentifier,
string username,
string firstName,
Expand Down Expand Up @@ -151,10 +118,5 @@ private bool IsAutoRegisterFlagSet()
{
return Configuration.GetValue<bool>("IdentityProfileLogin:AutoCreateUser");
}

private void AssignDefaultPermissions(ClaimsPrincipal claimsPrincipal)
{
claimsPrincipal.AddPermissions(_userPermissions);
}
}
}
Loading
Loading