From e40775428000f0957b1de9fba7e0f57a510ce186 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 14 Nov 2023 18:05:10 +0800 Subject: [PATCH] Refresh instead of returning dynamic claims. --- ...RemoteDynamicClaimsPrincipalContributor.cs | 19 ++++- ...eDynamicClaimsPrincipalContributorCache.cs | 72 +++++++------------ ...cClaimsPrincipalContributorCacheOptions.cs | 13 ---- .../Claims/AbpDynamicClaimsMiddleware.cs | 12 ++-- .../Abp/Security/Claims/AbpClaimCacheItem.cs | 22 ------ .../AbpClaimsPrincipalFactoryOptions.cs | 8 ++- .../Abp/Security/Claims/AbpDynamicClaim.cs | 17 +++++ .../Claims/AbpDynamicClaimCacheItem.cs | 25 +++++++ ...bpDynamicClaimsPrincipalContributorBase.cs | 4 +- ...micClaimsPrincipalContributorBase_Tests.cs | 24 +++---- .../Abp/Account/IDynamicClaimsAppService.cs | 2 +- .../Abp/Account/DynamicClaimsAppService.cs | 33 +++------ .../DynamicClaimsClientProxy.Generated.cs | 4 +- .../ClientProxies/account-generate-proxy.json | 20 +++--- .../Abp/Account/DynamicClaimsController.cs | 7 +- ...entityDynamicClaimsPrincipalContributor.cs | 6 +- ...yDynamicClaimsPrincipalContributorCache.cs | 16 ++--- .../MyProjectNameAuthServerModule.cs | 6 ++ .../MyProjectNameBlazorModule.cs | 3 +- .../MyProjectNameBlazorModule.cs | 5 ++ .../MyProjectNameBlazorModule.cs | 3 +- .../MyProjectNameHttpApiHostModule.cs | 6 ++ .../MyProjectNameHttpApiHostModule.cs | 5 ++ .../MyProjectNameWebModule.cs | 9 +-- .../MyProjectNameWebModule.cs | 5 ++ 25 files changed, 183 insertions(+), 163 deletions(-) delete mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCacheOptions.cs delete mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimCacheItem.cs create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaim.cs create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimCacheItem.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs index e1f545a995..d9339b57df 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs @@ -1,6 +1,9 @@ +using System; using System.Linq; +using System.Security.Claims; using System.Security.Principal; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Mvc.Client; @@ -22,8 +25,20 @@ public class RemoteDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincipal } var dynamicClaimsCache = context.GetRequiredService(); - var dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId()); + AbpDynamicClaimCacheItem dynamicClaims; + try + { + dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId()); + } + catch (Exception e) + { + // In case if failed refresh remote dynamic cache, We force to clear the claims principal. + context.ClaimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity()); + var logger = context.GetRequiredService>(); + logger.LogWarning(e, $"Failed to refresh remote dynamic claims cache for user: {userId.Value}"); + return; + } - await AddDynamicClaimsAsync(context, identity, dynamicClaims); + await AddDynamicClaimsAsync(context, identity, dynamicClaims.Claims); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs index 400d42349c..eb9618e6b1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -21,76 +19,58 @@ public class RemoteDynamicClaimsPrincipalContributorCache : ITransientDependency public const string HttpClientName = nameof(RemoteDynamicClaimsPrincipalContributorCache); public ILogger Logger { get; set; } - protected IDistributedCache> Cache { get; } + protected IDistributedCache Cache { get; } protected IHttpClientFactory HttpClientFactory { get; } protected IOptions AbpClaimsPrincipalFactoryOptions { get; } protected IJsonSerializer JsonSerializer { get; } protected IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } - protected IOptions CacheOptions { get; } public RemoteDynamicClaimsPrincipalContributorCache( - IDistributedCache> cache, + IDistributedCache cache, IHttpClientFactory httpClientFactory, IOptions abpClaimsPrincipalFactoryOptions, IJsonSerializer jsonSerializer, - IRemoteServiceHttpClientAuthenticator httpClientAuthenticator, - IOptions cacheOptions) + IRemoteServiceHttpClientAuthenticator httpClientAuthenticator) { Cache = cache; HttpClientFactory = httpClientFactory; AbpClaimsPrincipalFactoryOptions = abpClaimsPrincipalFactoryOptions; JsonSerializer = jsonSerializer; HttpClientAuthenticator = httpClientAuthenticator; - CacheOptions = cacheOptions; Logger = NullLogger.Instance; } - public virtual async Task> GetAsync(Guid userId, Guid? tenantId = null) + public virtual async Task GetAsync(Guid userId, Guid? tenantId = null) { Logger.LogDebug($"Get dynamic claims cache for user: {userId}"); - //The UI may use the same cache as AuthServer in the tiered application. - var claims = await Cache.GetAsync(AbpClaimCacheItem.CalculateCacheKey(userId, tenantId)); - if (!claims.IsNullOrEmpty()) + var claims = await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); + if (claims != null && !claims.Claims.IsNullOrEmpty()) { - return claims!; + return claims; } - Logger.LogDebug($"Get dynamic claims cache for user: {userId} from remote cache."); - // Use independent cache for remote claims. - return (await Cache.GetOrAddAsync($"{nameof(RemoteDynamicClaimsPrincipalContributorCache)}{AbpClaimCacheItem.CalculateCacheKey(userId, tenantId)}", async () => + Logger.LogDebug($"Refresh dynamic claims for user: {userId} from remote service."); + try { - var dynamicClaims = AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims.Select(claimType => new AbpClaimCacheItem(claimType, null)).ToList(); - Logger.LogDebug($"Get dynamic claims for user: {userId} from remote service."); - try - { - var client = HttpClientFactory.CreateClient(HttpClientName); - var requestMessage = new HttpRequestMessage(HttpMethod.Get, AbpClaimsPrincipalFactoryOptions.Value.RemoteUrl); - await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, requestMessage, new RemoteServiceConfiguration("/"), string.Empty)); - var response = await client.SendAsync(requestMessage); - var remoteClaims = JsonSerializer.Deserialize>(await response.Content.ReadAsStringAsync()); - foreach (var claim in dynamicClaims) - { - claim.Value = remoteClaims.FirstOrDefault(c => c.Type == claim.Type)?.Value; - } - Logger.LogDebug($"Successfully got {dynamicClaims.Count} remote claims for user: {userId}"); - } - catch (Exception e) - { - Logger.LogWarning(e, $"Failed to get remote claims for user: {userId}"); - } - return dynamicClaims; - }, () => new DistributedCacheEntryOptions + var client = HttpClientFactory.CreateClient(HttpClientName); + var requestMessage = new HttpRequestMessage(HttpMethod.Post, AbpClaimsPrincipalFactoryOptions.Value.RemoteRefreshUrl); + await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, requestMessage, new RemoteServiceConfiguration("/"), string.Empty)); + var response = await client.SendAsync(requestMessage); + response.EnsureSuccessStatusCode(); + } + catch (Exception e) { - AbsoluteExpirationRelativeToNow = CacheOptions.Value.CacheAbsoluteExpiration - }))!; - } + Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}"); + throw; + } - public virtual async Task ClearAsync(Guid userId, Guid? tenantId = null) - { - Logger.LogDebug($"Clear dynamic claims cache for user: {userId}"); - Logger.LogDebug($"Clear dynamic claims cache from remote cache for user: {userId}"); - await Cache.RemoveAsync(AbpClaimCacheItem.CalculateCacheKey(userId, tenantId)); - await Cache.RemoveAsync($"{nameof(RemoteDynamicClaimsPrincipalContributorCache)}{AbpClaimCacheItem.CalculateCacheKey(userId, tenantId)}"); + claims = await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); + if (claims == null || claims.Claims.IsNullOrEmpty()) + { + throw new AbpException($"Failed to refresh remote claims for user: {userId}"); + } + + return claims!; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCacheOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCacheOptions.cs deleted file mode 100644 index ea7afb5dcb..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCacheOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Volo.Abp.AspNetCore.Mvc.Client; - -public class RemoteDynamicClaimsPrincipalContributorCacheOptions -{ - public TimeSpan CacheAbsoluteExpiration { get; set; } - - public RemoteDynamicClaimsPrincipalContributorCacheOptions() - { - CacheAbsoluteExpiration = TimeSpan.FromSeconds(60); - } -} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs index 33b7b3f16e..777ca7744a 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.Security.Claims; -using Volo.Abp.Users; namespace Volo.Abp.AspNetCore.Security.Claims; @@ -11,11 +11,13 @@ public class AbpDynamicClaimsMiddleware : IMiddleware, ITransientDependency { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { - var currentUser = context.RequestServices.GetRequiredService(); - if (currentUser.IsAuthenticated) + if (context.User.Identity?.IsAuthenticated == true) { - var abpClaimsPrincipalFactory = context.RequestServices.GetRequiredService(); - context.User = await abpClaimsPrincipalFactory.CreateDynamicAsync(context.User); + if (context.RequestServices.GetRequiredService>().Value.IsDynamicClaimsEnabled) + { + var abpClaimsPrincipalFactory = context.RequestServices.GetRequiredService(); + context.User = await abpClaimsPrincipalFactory.CreateDynamicAsync(context.User); + } } await next(context); diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimCacheItem.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimCacheItem.cs deleted file mode 100644 index d87e8634b2..0000000000 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimCacheItem.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Volo.Abp.Security.Claims; - -[Serializable] -public class AbpClaimCacheItem -{ - public string Type { get; set; } - - public string? Value { get; set; } - - public AbpClaimCacheItem(string type, string? value) - { - Type = type; - Value = value; - } - - public static string CalculateCacheKey(Guid userId, Guid? tenantId) - { - return $"{tenantId}-{userId}"; - } -} diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs index 0f5b5cdbd6..75bf67bee6 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs @@ -12,10 +12,12 @@ public class AbpClaimsPrincipalFactoryOptions public List DynamicClaims { get; } - public string RemoteUrl { get; set; } + public string RemoteRefreshUrl { get; set; } public Dictionary> ClaimsMap { get; set; } + public bool IsDynamicClaimsEnabled { get; set; } + public AbpClaimsPrincipalFactoryOptions() { Contributors = new TypeList(); @@ -33,7 +35,7 @@ public class AbpClaimsPrincipalFactoryOptions AbpClaimTypes.PhoneNumberVerified }; - RemoteUrl = "/api/account/dynamic-claims"; + RemoteRefreshUrl = "/api/account/dynamic-claims/refresh"; ClaimsMap = new Dictionary>() { @@ -43,5 +45,7 @@ public class AbpClaimsPrincipalFactoryOptions { AbpClaimTypes.Role, new List { "role", "roles", ClaimTypes.Role }}, { AbpClaimTypes.Email, new List { "email", ClaimTypes.Email }}, }; + + IsDynamicClaimsEnabled = false; } } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaim.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaim.cs new file mode 100644 index 0000000000..ab8fc13f17 --- /dev/null +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaim.cs @@ -0,0 +1,17 @@ +using System; + +namespace Volo.Abp.Security.Claims; + +[Serializable] +public class AbpDynamicClaim +{ + public string Type { get; set; } + + public string? Value { get; set; } + + public AbpDynamicClaim(string type, string? value) + { + Type = type; + Value = value; + } +} diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimCacheItem.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimCacheItem.cs new file mode 100644 index 0000000000..8c25cb19e7 --- /dev/null +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimCacheItem.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Security.Claims; + +[Serializable] +public class AbpDynamicClaimCacheItem +{ + public List Claims { get; set; } + + public AbpDynamicClaimCacheItem() + { + Claims = new List(); + } + + public AbpDynamicClaimCacheItem(List claims) + { + Claims = claims; + } + + public static string CalculateCacheKey(Guid userId, Guid? tenantId) + { + return $"{tenantId}-{userId}"; + } +} diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase.cs index 2982018650..2e607ace93 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase.cs @@ -12,7 +12,7 @@ public abstract class AbpDynamicClaimsPrincipalContributorBase : IAbpDynamicClai { public abstract Task ContributeAsync(AbpClaimsPrincipalContributorContext context); - protected virtual async Task AddDynamicClaimsAsync(AbpClaimsPrincipalContributorContext context, ClaimsIdentity identity, List dynamicClaims) + protected virtual async Task AddDynamicClaimsAsync(AbpClaimsPrincipalContributorContext context, ClaimsIdentity identity, List dynamicClaims) { var options = context.GetRequiredService>().Value; foreach (var map in options.ClaimsMap) @@ -27,7 +27,7 @@ public abstract class AbpDynamicClaimsPrincipalContributorBase : IAbpDynamicClai } } - protected virtual Task MapClaimAsync(ClaimsIdentity identity, List dynamicClaims, string targetClaimType, params string[] sourceClaimTypes) + protected virtual Task MapClaimAsync(ClaimsIdentity identity, List dynamicClaims, string targetClaimType, params string[] sourceClaimTypes) { var claims = dynamicClaims.Where(c => sourceClaimTypes.Contains(c.Type)).ToList(); if (claims.IsNullOrEmpty()) diff --git a/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase_Tests.cs b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase_Tests.cs index d82ae2803a..7054319dfa 100644 --- a/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase_Tests.cs +++ b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase_Tests.cs @@ -16,7 +16,7 @@ class TestAbpDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincipalContri var identity = context.ClaimsPrincipal.Identities.FirstOrDefault(); Check.NotNull(identity, nameof(identity)); - await AddDynamicClaimsAsync(context, identity, AbpDynamicClaimsPrincipalContributorBase_Tests.DynamicClaims); + await AddDynamicClaimsAsync(context, identity, AbpDynamicClaimsPrincipalContributorBase_Tests.DynamicClaims.Claims); } } @@ -24,7 +24,7 @@ public class AbpDynamicClaimsPrincipalContributorBase_Tests : AbpIntegratedTest< { private readonly TestAbpDynamicClaimsPrincipalContributor _dynamicClaimsPrincipalContributorBase = new TestAbpDynamicClaimsPrincipalContributor(); - public readonly static List DynamicClaims = new List(); + public readonly static AbpDynamicClaimCacheItem DynamicClaims = new AbpDynamicClaimCacheItem(); protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) { @@ -46,17 +46,17 @@ public class AbpDynamicClaimsPrincipalContributorBase_Tests : AbpIntegratedTest< claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.PhoneNumberVerified, "test-source-phoneNumberVerified")); claimsPrincipal.Identities.First().AddClaim(new Claim("my-claim", "test-source-my-claim")); - DynamicClaims.AddRange(new [] + DynamicClaims.Claims.AddRange(new [] { - new AbpClaimCacheItem("preferred_username", "test-preferred_username"), - new AbpClaimCacheItem(ClaimTypes.GivenName, "test-given_name"), - new AbpClaimCacheItem("family_name", "test-family_name"), - new AbpClaimCacheItem("role", "test-role1"), - new AbpClaimCacheItem("roles", "test-role2"), - new AbpClaimCacheItem(ClaimTypes.Role, "test-role3"), - new AbpClaimCacheItem("email", "test-email"), - new AbpClaimCacheItem(AbpClaimTypes.EmailVerified, "test-email-verified"), - new AbpClaimCacheItem(AbpClaimTypes.PhoneNumberVerified, null), + new AbpDynamicClaim("preferred_username", "test-preferred_username"), + new AbpDynamicClaim(ClaimTypes.GivenName, "test-given_name"), + new AbpDynamicClaim("family_name", "test-family_name"), + new AbpDynamicClaim("role", "test-role1"), + new AbpDynamicClaim("roles", "test-role2"), + new AbpDynamicClaim(ClaimTypes.Role, "test-role3"), + new AbpDynamicClaim("email", "test-email"), + new AbpDynamicClaim(AbpClaimTypes.EmailVerified, "test-email-verified"), + new AbpDynamicClaim(AbpClaimTypes.PhoneNumberVerified, null), }); await _dynamicClaimsPrincipalContributorBase.ContributeAsync(new AbpClaimsPrincipalContributorContext(claimsPrincipal, GetRequiredService())); diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IDynamicClaimsAppService.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IDynamicClaimsAppService.cs index 8702ef5fcd..36a91b2155 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IDynamicClaimsAppService.cs +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IDynamicClaimsAppService.cs @@ -6,5 +6,5 @@ namespace Volo.Abp.Account; public interface IDynamicClaimsAppService : IApplicationService { - Task> GetAsync(); + Task RefreshAsync(); } diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/DynamicClaimsAppService.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/DynamicClaimsAppService.cs index d32d265d4a..e26dbfc979 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/DynamicClaimsAppService.cs +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/DynamicClaimsAppService.cs @@ -1,48 +1,31 @@ -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.Options; using Volo.Abp.Identity; using Volo.Abp.Security.Claims; +using Volo.Abp.Users; namespace Volo.Abp.Account; [Authorize] public class DynamicClaimsAppService : IdentityAppServiceBase, IDynamicClaimsAppService { + protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; } protected IAbpClaimsPrincipalFactory AbpClaimsPrincipalFactory { get; } protected ICurrentPrincipalAccessor PrincipalAccessor { get; } - protected IOptions AbpClaimsPrincipalFactoryOptions { get; } public DynamicClaimsAppService( + IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache, IAbpClaimsPrincipalFactory abpClaimsPrincipalFactory, - ICurrentPrincipalAccessor principalAccessor, - IOptions abpClaimsPrincipalFactoryOptions) + ICurrentPrincipalAccessor principalAccessor) { + IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache; AbpClaimsPrincipalFactory = abpClaimsPrincipalFactory; PrincipalAccessor = principalAccessor; - AbpClaimsPrincipalFactoryOptions = abpClaimsPrincipalFactoryOptions; } - public virtual async Task> GetAsync() + public virtual async Task RefreshAsync() { - var principal = await AbpClaimsPrincipalFactory.CreateAsync(PrincipalAccessor.Principal); - - var dynamicClaims = new List(); - foreach (var claimType in AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims) - { - var claims = principal.Claims.Where(x => x.Type == claimType).ToList(); - if (claims.Any()) - { - dynamicClaims.AddRange(claims.Select(claim => new DynamicClaimDto(claimType, claim.Value))); - } - else - { - dynamicClaims.Add(new DynamicClaimDto(claimType, null)); - } - } - - return dynamicClaims; + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(CurrentUser.GetId(), CurrentUser.TenantId); + await AbpClaimsPrincipalFactory.CreateDynamicAsync(PrincipalAccessor.Principal); } } diff --git a/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.Generated.cs b/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.Generated.cs index d460483a8d..7bb80a95cb 100644 --- a/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.Generated.cs +++ b/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.Generated.cs @@ -17,8 +17,8 @@ namespace Volo.Abp.Account; [ExposeServices(typeof(IDynamicClaimsAppService), typeof(DynamicClaimsClientProxy))] public partial class DynamicClaimsClientProxy : ClientProxyBase, IDynamicClaimsAppService { - public virtual async Task> GetAsync() + public virtual async Task RefreshAsync() { - return await RequestAsync>(nameof(GetAsync)); + await RequestAsync(nameof(RefreshAsync)); } } diff --git a/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/account-generate-proxy.json b/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/account-generate-proxy.json index 857c27a3cd..9857f97c9a 100644 --- a/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/account-generate-proxy.json +++ b/modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/account-generate-proxy.json @@ -351,28 +351,28 @@ "name": "IDynamicClaimsAppService", "methods": [ { - "name": "GetAsync", + "name": "RefreshAsync", "parametersOnMethod": [], "returnValue": { - "type": "System.Collections.Generic.List", - "typeSimple": "[Volo.Abp.Account.DynamicClaimDto]" + "type": "System.Void", + "typeSimple": "System.Void" } } ] } ], "actions": { - "GetAsync": { - "uniqueName": "GetAsync", - "name": "GetAsync", - "httpMethod": "GET", - "url": "api/account/dynamic-claims", + "RefreshAsync": { + "uniqueName": "RefreshAsync", + "name": "RefreshAsync", + "httpMethod": "POST", + "url": "api/account/dynamic-claims/refresh", "supportedVersions": [], "parametersOnMethod": [], "parameters": [], "returnValue": { - "type": "System.Collections.Generic.List", - "typeSimple": "[Volo.Abp.Account.DynamicClaimDto]" + "type": "System.Void", + "typeSimple": "System.Void" }, "allowAnonymous": null, "implementFrom": "Volo.Abp.Account.IDynamicClaimsAppService" diff --git a/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/DynamicClaimsController.cs b/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/DynamicClaimsController.cs index 85587478ed..f3546e6607 100644 --- a/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/DynamicClaimsController.cs +++ b/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/DynamicClaimsController.cs @@ -18,9 +18,10 @@ public class DynamicClaimsController : AbpControllerBase, IDynamicClaimsAppServi DynamicClaimsAppService = dynamicClaimsAppService; } - [HttpGet] - public virtual Task> GetAsync() + [HttpPost] + [Route("refresh")] + public virtual Task RefreshAsync() { - return DynamicClaimsAppService.GetAsync(); + return DynamicClaimsAppService.RefreshAsync(); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor.cs index 45093591b6..7d56a3b184 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Security.Principal; @@ -22,7 +20,7 @@ public class IdentityDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincip } var dynamicClaimsCache = context.GetRequiredService(); - List dynamicClaims; + AbpDynamicClaimCacheItem dynamicClaims; try { dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId()); @@ -36,6 +34,6 @@ public class IdentityDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincip return; } - await AddDynamicClaimsAsync(context, identity, dynamicClaims); + await AddDynamicClaimsAsync(context, identity, dynamicClaims.Claims); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs index f3014adfce..b0ab885e03 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs @@ -18,7 +18,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen { public ILogger Logger { get; set; } - protected IDistributedCache> Cache { get; } + protected IDistributedCache Cache { get; } protected ICurrentTenant CurrentTenant { get; } protected IdentityUserManager UserManager { get; } protected IUserClaimsPrincipalFactory UserClaimsPrincipalFactory { get; } @@ -26,7 +26,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen protected IOptions CacheOptions { get; } public IdentityDynamicClaimsPrincipalContributorCache( - IDistributedCache> cache, + IDistributedCache cache, ICurrentTenant currentTenant, IdentityUserManager userManager, IUserClaimsPrincipalFactory userClaimsPrincipalFactory, @@ -43,11 +43,11 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen Logger = NullLogger.Instance; } - public virtual async Task> GetAsync(Guid userId, Guid? tenantId = null) + public virtual async Task GetAsync(Guid userId, Guid? tenantId = null) { Logger.LogDebug($"Get dynamic claims cache for user: {userId}"); - return await Cache.GetOrAddAsync(AbpClaimCacheItem.CalculateCacheKey(userId, tenantId), async () => + return await Cache.GetOrAddAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), async () => { using (CurrentTenant.Change(tenantId)) { @@ -56,17 +56,17 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen var user = await UserManager.GetByIdAsync(userId); var principal = await UserClaimsPrincipalFactory.CreateAsync(user); - var dynamicClaims = new List(); + var dynamicClaims = new AbpDynamicClaimCacheItem(); foreach (var claimType in AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims) { var claims = principal.Claims.Where(x => x.Type == claimType).ToList(); if (claims.Any()) { - dynamicClaims.AddRange(claims.Select(claim => new AbpClaimCacheItem(claimType, claim.Value))); + dynamicClaims.Claims.AddRange(claims.Select(claim => new AbpDynamicClaim(claimType, claim.Value))); } else { - dynamicClaims.Add(new AbpClaimCacheItem(claimType, null)); + dynamicClaims.Claims.Add(new AbpDynamicClaim(claimType, null)); } } @@ -81,6 +81,6 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen public virtual async Task ClearAsync(Guid userId, Guid? tenantId = null) { Logger.LogDebug($"Clearing dynamic claims cache for user: {userId}"); - await Cache.RemoveAsync(AbpClaimCacheItem.CalculateCacheKey(userId, tenantId)); + await Cache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs index 95b2563438..4c191579f8 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs @@ -32,6 +32,7 @@ using Volo.Abp.DistributedLocking; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.OpenIddict; +using Volo.Abp.Security.Claims; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.UI; using Volo.Abp.VirtualFileSystem; @@ -180,6 +181,11 @@ public class MyProjectNameAuthServerModule : AbpModule .AllowCredentials(); }); }); + + context.Services.Configure(options => + { + options.IsDynamicClaimsEnabled = true; + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs index dd890d4dc6..c41ed749be 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs @@ -231,7 +231,8 @@ public class MyProjectNameBlazorModule : AbpModule context.Services.Configure(options => { - options.RemoteUrl = configuration["AuthServer:Authority"] + options.RemoteUrl; + options.IsDynamicClaimsEnabled = true; + options.RemoteRefreshUrl = configuration["AuthServer:Authority"] + options.RemoteRefreshUrl; }); } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs index 0bb20fa36e..8c93eeca33 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs @@ -34,6 +34,7 @@ using Volo.Abp.AutoMapper; using Volo.Abp.Identity.Blazor.Server; using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.Security.Claims; using Volo.Abp.SettingManagement.Blazor.Server; using Volo.Abp.Swashbuckle; using Volo.Abp.TenantManagement.Blazor.Server; @@ -122,6 +123,10 @@ public class MyProjectNameBlazorModule : AbpModule private void ConfigureAuthentication(ServiceConfigurationContext context) { context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + context.Services.Configure(options => + { + options.IsDynamicClaimsEnabled = true; + }); } private void ConfigureUrls(IConfiguration configuration) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs index a323ff8dcd..833ae909f5 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs @@ -84,7 +84,8 @@ public class MyProjectNameBlazorModule : AbpModule builder.Services.Configure(options => { - options.RemoteUrl = builder.Configuration["AuthServer:Authority"] + options.RemoteUrl; + options.IsDynamicClaimsEnabled = true; + options.RemoteRefreshUrl = builder.Configuration["AuthServer:Authority"] + options.RemoteRefreshUrl; }); } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs index d277aa4039..e785db843a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -27,6 +27,7 @@ using Volo.Abp.DistributedLocking; using Volo.Abp.Identity; using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.Security.Claims; using Volo.Abp.Swashbuckle; using Volo.Abp.VirtualFileSystem; @@ -106,6 +107,11 @@ public class MyProjectNameHttpApiHostModule : AbpModule options.RequireHttpsMetadata = configuration.GetValue("AuthServer:RequireHttpsMetadata"); options.Audience = "MyProjectName"; }); + + context.Services.Configure(options => + { + options.IsDynamicClaimsEnabled = true; + }); } private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs index 238339b1cf..45d3c605e7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs @@ -25,6 +25,7 @@ using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.Security.Claims; using Volo.Abp.Swashbuckle; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; @@ -74,6 +75,10 @@ public class MyProjectNameHttpApiHostModule : AbpModule private void ConfigureAuthentication(ServiceConfigurationContext context) { context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + context.Services.Configure(options => + { + options.IsDynamicClaimsEnabled = true; + }); } private void ConfigureBundles() diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs index 01ed6034f8..70e98a64eb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs @@ -211,10 +211,11 @@ public class MyProjectNameWebModule : AbpModule }); } - context.Services.Configure(options => - { - options.RemoteUrl = configuration["AuthServer:Authority"] + options.RemoteUrl; - }); + context.Services.Configure(options => + { + options.IsDynamicClaimsEnabled = true; + options.RemoteRefreshUrl = configuration["AuthServer:Authority"] + options.RemoteRefreshUrl; + }); } private void ConfigureAutoMapper() diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index a259540829..1b61db27c0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -30,6 +30,7 @@ using Volo.Abp.Identity.Web; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.Web; +using Volo.Abp.Security.Claims; using Volo.Abp.SettingManagement.Web; using Volo.Abp.Swashbuckle; using Volo.Abp.TenantManagement.Web; @@ -115,6 +116,10 @@ public class MyProjectNameWebModule : AbpModule private void ConfigureAuthentication(ServiceConfigurationContext context) { context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + context.Services.Configure(options => + { + options.IsDynamicClaimsEnabled = true; + }); } private void ConfigureUrls(IConfiguration configuration)