Your code is no problem, that strange. Can you share a test project?
Unfortunately I cannot. I only can follow some recommendations. So from what you say I can deduce, that some of the custom store or all of them are not invoked, right? Where should I put the breakpoint in debug?
using AbxEps.MyApp.AuthServer.Grants;
using AbxEps.MyApp.AuthServer.Stores;
using AbxEps.MyApp.Cryptography;
using AbxEps.MyApp.EntityFrameworkCore;
using AbxEps.MyApp.Filters;
using AbxEps.MyApp.Localization;
using AbxEps.MyApp.Middlewares;
using AbxEps.MyApp.MultiTenancy;
using AbxEps.LanguageCodeConvertor.Extensions;
using AbxEps.Middleware.AbxRequestContext.Extensions;
using AbxEps.Middleware.CookieAccessToken.Extensions;
using Localization.Resources.AbpUi;
using Medallion.Threading;
using Medallion.Threading.Oracle;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Net.Http.Headers;
using OpenIddict.Server.AspNetCore;
using OpenIddict.Validation.AspNetCore;
using Serilog;
using StackExchange.Redis;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Public.Web;
using Volo.Abp.Account.Public.Web.Impersonation;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DistributedLocking;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.OpenIddict.Applications;
using Volo.Abp.OpenIddict.Authorizations;
using Volo.Abp.OpenIddict.ExtensionGrantTypes;
using Volo.Abp.OpenIddict.Tokens;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Ui.LayoutHooks;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Volo.Saas.Host;
using AbxEps.CentralTools.AuthServer.Grants;
using AbxEps.CentralTools.AuthServer.Stores;
using AbxEps.CentralTools.Cryptography;
using AbxEps.CentralTools.Filters;
using AbxEps.CentralTools.Middlewares;
using Twilio.Rest.Microvisor.V1;
namespace AbxEps.MyApp;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AbpDistributedLockingModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpAccountPublicWebOpenIddictModule),
typeof(AbpAccountPublicHttpApiModule),
typeof(AbpAspNetCoreMvcUiLeptonThemeModule),
typeof(AbpAccountPublicApplicationModule),
typeof(AbpAccountPublicWebImpersonationModule),
typeof(SaasHostApplicationContractsModule),
typeof(MyAppEntityFrameworkCoreModule)
)]
public class MyAppAuthServerModule : AbpModule
{
private const string DefaultCorsPolicyName = "Default";
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
//var logger = context.Services.GetInitLogger<MyAppAuthServerModule>();
Log.Information($"*** {nameof(MyAppAuthServerModule)}.{nameof(PreConfigureServices)}: HostingEnvironment: {hostingEnvironment.EnvironmentName}");
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
// https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
if (!hostingEnvironment.IsDevelopment())
{
options.AddDevelopmentEncryptionAndSigningCertificate = false;
}
});
PreConfigure<OpenIddictServerBuilder>(builder =>
{
// https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
// not needed because default of AddDevelopmentEncryptionAndSigningCertificate is true in Development...
/*if (hostingEnvironment.IsDevelopment())
{
builder
.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
}*/
if (!hostingEnvironment.IsDevelopment())
{
// Encryption
string encryptionCertThumbPrint = configuration["Certificates:Encryption:ThumbPrint"];
string encryptionCertFile = configuration["Certificates:Encryption:File"];
string encryptionCertPassword = configuration["Certificates:Encryption:Password"];
if (string.IsNullOrEmpty(encryptionCertThumbPrint) && string.IsNullOrEmpty(encryptionCertFile))
{
string message = "Neither Certificates:Encryption:ThumbPrint nor Certificates:Encryption:File is set!";
Log.Error($"*** {message}!");
throw new NotSupportedException(message);
}
if (!string.IsNullOrEmpty(encryptionCertFile) && !File.Exists(encryptionCertFile))
{
string message = $"Cannot find encryption certificate '{encryptionCertFile}'!";
Log.Error($"*** {message}!");
throw new NotSupportedException(message);
}
X509Certificate2 certificate = X509Helper.GetCertificate(encryptionCertThumbPrint, encryptionCertFile, encryptionCertPassword, Log.Logger);
if (certificate == null)
{
// throw exception, stop service...
string cert = string.IsNullOrEmpty(encryptionCertThumbPrint) ? encryptionCertFile : encryptionCertThumbPrint;
throw new NotSupportedException($"Encryption Certificate '{cert}' not found!");
}
else
{
Log.Debug($"Encryption Certificate: Issuer is '{certificate.Issuer}'");
builder.AddEncryptionCertificate(certificate);
}
// Signing Certificate
string signingCertThumbPrint = configuration["Certificates:Signing:ThumbPrint"];
string signingCertFile = configuration["Certificates:Signing:file"];
string signingCertPassword = configuration["Certificates:Signing:Password"];
if (string.IsNullOrEmpty(signingCertThumbPrint) && string.IsNullOrEmpty(signingCertFile))
{
string message = "Neither Certificates:Signing:ThumbPrint nor Certificates:Signing:File is set!";
Log.Error($"*** {message}!");
throw new NotSupportedException(message);
}
if (!string.IsNullOrEmpty(signingCertFile) && !File.Exists(signingCertFile))
{
string message = $"Cannot find signing certificate '{signingCertFile}'!";
Log.Error($"*** {message}!");
throw new NotSupportedException(message);
}
certificate = X509Helper.GetCertificate(signingCertThumbPrint, signingCertFile, signingCertPassword, Log.Logger);
if (certificate == null)
{
// throw exception, stop service...
string cert = string.IsNullOrEmpty(signingCertThumbPrint) ? signingCertFile : signingCertThumbPrint;
throw new NotSupportedException($"Signing Certificate '{cert}' not found!");
}
else
{
Log.Debug($"Signing Certificate: Issuer is '{certificate.Issuer}'");
builder.AddSigningCertificate(certificate);
}
}
builder.Configure(options =>
{
// adobu TODO
// https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption
// DisableAccessTokenEncryption is set to true in Volo.Abp.OpenIddict.AbpOpenIddictAspNetCoreModule.AddOpenIddictServer
// options.DisableAccessTokenEncryption = false;
//options.GrantTypes.Add(AbxLoginGrant.ExtensionGrantName);
options.GrantTypes.Add(SwitchToTenantGrant.ExtensionGrantName);
});
});
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddValidation(options =>
{
options.AddAudiences("MyApp");
options.UseLocalServer();
options.UseAspNetCore();
});
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
if (!Convert.ToBoolean(configuration["App:DisablePII"]))
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
}
if (!Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]))
{
Configure<OpenIddictServerAspNetCoreOptions>(options =>
{
options.DisableTransportSecurityRequirement = true;
});
}
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
// This is added for a correct root path in js files
Configure<AbpLayoutHookOptions>(options =>
{
options.Add(LayoutHooks.Head.Last,
typeof(AbpApplicationPathViewComponent));
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<MyAppResource>()
.AddBaseTypes(
typeof(AbpUiResource)
);
});
Configure<AbpBundlingOptions>(options =>
{
options.StyleBundles.Configure(
LeptonThemeBundles.Styles.Global,
bundle =>
{
bundle.AddFiles("/global-styles.css");
}
);
});
Configure<AbpAuditingOptions>(options =>
{
//options.IsEnabledForGetRequests = true;
options.ApplicationName = "AuthServer";
});
if (hostingEnvironment.IsDevelopment())
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical<MyAppDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}AbxEps.MyApp.Domain.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<MyAppDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}AbxEps.MyApp.Domain", Path.DirectorySeparatorChar)));
});
}
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));
options.Applications["Angular"].RootUrl = configuration["App:AngularUrl"];
options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password";
options.Applications["Angular"].Urls[AccountUrlNames.EmailConfirmation] = "account/email-confirmation";
});
Configure<AbpBackgroundJobOptions>(options =>
{
options.IsJobExecutionEnabled = false;
});
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "Abx:";
});
Configure<MvcOptions>(options =>
{
options.Filters.Add(typeof(TokenExpiredExceptionFilter));
});
var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("MyApp");
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "AuthServer-Protection-Keys");
}
context.Services.AddSingleton<IDistributedLockProvider>(sp =>
{
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
//return new RedisDistributedSynchronizationProvider(connection.GetDatabase());
return new OracleDistributedSynchronizationProvider(configuration["ConnectionStrings:Default"]);
});
ConfigureCors(context, configuration);
ConfigureAuth(context, configuration);
ConfigureOpenIdDict(context, configuration);
ConfigureStores(context, configuration);
context.Services.Configure<AbpAccountOptions>(options =>
{
options.TenantAdminUserName = "admin";
options.ImpersonationTenantPermission = SaasHostPermissions.Tenants.Impersonation;
options.ImpersonationUserPermission = IdentityPermissions.Users.Impersonation;
});
Configure<PermissionManagementOptions>(options =>
{
options.IsDynamicPermissionStoreEnabled = true;
});
Configure<FeatureManagementOptions>(options =>
{
options.IsDynamicFeatureStoreEnabled = true;
});
}
private static void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.Trim().RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
private static void ConfigureAuth(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication();
}
private static void ConfigureStores(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddOpenIddict()
.AddCore(builder =>
{
builder
.AddApplicationStore<AbxAbpOpenIddictApplicationStore>()
.AddAuthorizationStore<AbxAbpOpenIddictAuthorizationStore>()
.AddTokenStore<AbxAbpOpenIddictTokenStore>();
});
}
private void ConfigureOpenIdDict(ServiceConfigurationContext context, IConfiguration configuration)
{
// Configure<TokenCleanupOptions>(options => { });
Configure<AbpOpenIddictExtensionGrantsOptions>(options =>
{
//options.Grants.Add(AbxLoginGrant.ExtensionGrantName, new AbxLoginGrant());
options.Grants.Add(SwitchToTenantGrant.ExtensionGrantName, new SwitchToTenantGrant());
});
// This is required for working of AbxRequestContext
context.Services.AddHttpContextAccessor();
context.Services.InitLanguageCodeConvertor(configuration);
context.Services.InitAbxRequestContext();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAbpRequestLocalization();
if (!env.IsDevelopment())
{
app.UseErrorPage();
}
app.UseCorrelationId();
app.UseAbpSecurityHeaders();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(DefaultCorsPolicyName);
app.UseAuthentication();
//app.UseAbpOpenIddictValidation(); // UseIdentityServer
app.UseCookieAccessTokenDelete(HeaderNames.Authorization);
/* https://docs.abp.io/en/abp/7.0/Migration-Guides/IdentityServer_To_OpenIddict
app.UseJwtTokenMiddleware();*/
if (MultiTenancyConsts.IsEnabled)
{
app.UseMultiTenancy();
}
app.UseUnitOfWork();
app.UseMiddleware<SessionEndAuditingMiddleware>();
app.UseMiddleware<TenantSwitchMiddleware>();
app.UseAbpOpenIddictValidation(); // UseIdentityServer
app.UseAuthorization();
app.UseAuditing();
app.UseAbpSerilogEnrichers();
app.UseConfiguredEndpoints();
}
}
So while the initial exception reproducing still pending, I received this again:
> [09:26:33 INF] Lock is acquired for TokenCleanupBackgroundWorker
> [09:26:33 INF] Start cleanup.
> [09:26:33 INF] Start cleanup tokens.
> fail: Microsoft.EntityFrameworkCore.Infrastructure[0]
> 2024-08-19 09:26:34.569195 ThreadID:500 (ERROR) OracleExecutionStrategy.ExecuteAsync() : System.ArgumentException: IsolationLevel must be ReadCommitted or Serializable (Parameter 'isolationLevel')
> at Oracle.ManagedDataAccess.Client.OracleConnection.BeginTransaction(IsolationLevel isolationLevel)
> at Oracle.ManagedDataAccess.Client.OracleConnection.BeginDbTransaction(IsolationLevel isolationLevel)
> at System.Data.Common.DbConnection.BeginDbTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken)
> --- End of stack trace from previous location ---
> at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.BeginTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken)
> at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
> [09:26:34 ERR] IsolationLevel must be ReadCommitted or Serializable (Parameter 'isolationLevel')
> System.ArgumentException: IsolationLevel must be ReadCommitted or Serializable (Parameter 'isolationLevel')
> at Oracle.ManagedDataAccess.Client.OracleConnection.BeginTransaction(IsolationLevel isolationLevel)
> at Oracle.ManagedDataAccess.Client.OracleConnection.BeginDbTransaction(IsolationLevel isolationLevel)
> at System.Data.Common.DbConnection.BeginDbTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken)
> --- End of stack trace from previous location ---
> at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.BeginTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken)
> at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
> at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
> at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.CreateDbContextAsync(IUnitOfWork unitOfWork)
> at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.CreateDbContextAsync(IUnitOfWork unitOfWork, String connectionStringName, String connectionString)
> at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.GetDbContextAsync()
> at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository`2.GetDbSetAsync()
> at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository`2.GetQueryableAsync()
> at Volo.Abp.OpenIddict.Tokens.EfCoreOpenIddictTokenRepository.PruneAsync(DateTime date, CancellationToken cancellationToken)
> at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
> at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
> at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
> at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
> at Volo.Abp.OpenIddict.Tokens.AbpOpenIddictTokenStore.PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
> at Volo.Abp.OpenIddict.Tokens.TokenCleanupService.CleanAsync()
And it is happening after I replaced all the stores as you suggested. The code of the stores was copy-pasted from yours:
context.Services.AddOpenIddict()
.AddCore(builder =>
{
builder
.AddApplicationStore<AbxAbpOpenIddictApplicationStore>()
.AddAuthorizationStore<AbxAbpOpenIddictAuthorizationStore>()
.AddTokenStore<AbxAbpOpenIddictTokenStore>();
});
Maybe I am adding this code too late inside ConfigureServices
if the order matters at all here? What is it supposed to follow in OpenIDServer module?
Please share full request and response logs.
Sorry - my bad (due to very late time here). I've updated the message above. So it is seen the time is spent here. But there is no more information:
2024-08-16 04:25:17.610 -05:00 [DBG] Found in the cache: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:55.009 -05:00 [DBG] Added 0 entity changes to the current audit log
Please check the log. The log will contain the time taken to read and write redis.
The situation is interesting. The log does not show a huge timing:
2024-08-16 04:25:13.454 -05:00 [INF] Request starting HTTP/1.1 GET https://localhost:44328/api/module-permission/roles/module/as-permissions?sorting=displayName%20asc&skipCount=0&maxResultCount=12000&isModuleRole=true&isGranted=true&id=d941cfc0-74b7-06b1-cb98-39fefb5fa915 - null null 2024-08-16 04:25:13.454 -05:00 [INF] CORS policy execution successful. 2024-08-16 04:25:14.725 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:U,pk:f6bfc02f-4400-1256-f3db-39fed7dc74b6,n:AbpIdentity.Roles 2024-08-16 04:25:14.728 -05:00 [DBG] Found in the cache: pn:U,pk:f6bfc02f-4400-1256-f3db-39fed7dc74b6,n:AbpIdentity.Roles 2024-08-16 04:25:14.733 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:14.735 -05:00 [DBG] Found in the cache: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:14.736 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:14.737 -05:00 [DBG] Found in the cache: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:14.738 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:C,pk:CentralTools_App,n:AbpIdentity.Roles 2024-08-16 04:25:14.739 -05:00 [DBG] Found in the cache: pn:C,pk:CentralTools_App,n:AbpIdentity.Roles 2024-08-16 04:25:14.803 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:14.804 -05:00 [DBG] Found in the cache: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:14.804 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:14.806 -05:00 [DBG] Found in the cache: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:17.423 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:17.424 -05:00 [INF] Executing endpoint 'AbxEps.CentralTools.Controllers.ModulePermissions.ModulePermissionController.GetModuleRolesAsPermissionsAsync (AbxEps.CentralTools.HttpApi)' 2024-08-16 04:25:17.434 -05:00 [INF] Route matched with {area = "app", controller = "ModulePermission", action = "GetModuleRolesAsPermissions", page = ""}. Executing controller action with signature System.Threading.Tasks.Task
1[Volo.Abp.Application.Dtos.PagedResultDto
1[Volo.Abp.PermissionManagement.PermissionGrantInfoDto]] GetModuleRolesAsPermissionsAsync(AbxEps.CT.ModulePermission.Roles.GetRolePermissionsInput) on controller AbxEps.CentralTools.Controllers.ModulePermissions.ModulePermissionController (AbxEps.CentralTools.HttpApi). 2024-08-16 04:25:17.601 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:U,pk:f6bfc02f-4400-1256-f3db-39fed7dc74b6,n:AbpIdentity.Roles 2024-08-16 04:25:17.603 -05:00 [DBG] Found in the cache: pn:U,pk:f6bfc02f-4400-1256-f3db-39fed7dc74b6,n:AbpIdentity.Roles 2024-08-16 04:25:17.603 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:17.604 -05:00 [DBG] Found in the cache: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:17.604 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:17.605 -05:00 [DBG] Found in the cache: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:17.605 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:C,pk:CentralTools_App,n:AbpIdentity.Roles 2024-08-16 04:25:17.606 -05:00 [DBG] Found in the cache: pn:C,pk:CentralTools_App,n:AbpIdentity.Roles 2024-08-16 04:25:17.608 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:17.609 -05:00 [DBG] Found in the cache: pn:R,pk:Role 1,n:AbpIdentity.Roles 2024-08-16 04:25:17.609 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:17.610 -05:00 [DBG] Found in the cache: pn:R,pk:admin,n:AbpIdentity.Roles 2024-08-16 04:25:55.009 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:55.017 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:55.025 -05:00 [INF] Executing ObjectResult, writing value of type 'Volo.Abp.Application.Dtos.PagedResultDto`1[[Volo.Abp.PermissionManagement.PermissionGrantInfoDto, Volo.Abp.PermissionManagement.Application.Contracts, Version=8.1.3.0, Culture=neutral, PublicKeyToken=null]]'. 2024-08-16 04:25:55.036 -05:00 [INF] Executed action AbxEps.CentralTools.Controllers.ModulePermissions.ModulePermissionController.GetModuleRolesAsPermissionsAsync (AbxEps.CentralTools.HttpApi) in 37601.7587ms 2024-08-16 04:25:55.036 -05:00 [INF] Executed endpoint 'AbxEps.CentralTools.Controllers.ModulePermissions.ModulePermissionController.GetModuleRolesAsPermissionsAsync (AbxEps.CentralTools.HttpApi)' 2024-08-16 04:25:55.037 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:55.040 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:55.569 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:55.570 -05:00 [DBG] Added 0 entity changes to the current audit log 2024-08-16 04:25:55.571 -05:00 [INF] Request finished HTTP/1.1 GET https://localhost:44328/api/module-permission/roles/module/as-permissions?sorting=displayName%20asc&skipCount=0&maxResultCount=12000&isModuleRole=true&isGranted=true&id=d941cfc0-74b7-06b1-cb98-39fefb5fa915 - 200 24713 application/json; charset=utf-8 42117.6325ms
The DB is remote, everything the rest is localhost. But when there was no Redis - the timing was much better.
I did not transfer these lines to the project which now is responsible for Role Management:
Configure<PermissionManagementOptions>(options =>
{
options.ManagementProviders.Add<ModulePermissionManagementProvider>();
options.ProviderPolicies[ModulePermissionRoleValueProvider.ProviderName] = ModulePermissionConsts.RoleSubGroupName;
});
After I added this, the permissions are properly reflected.
But I have noticed that now after moving to dynamic permissions and Redis, selecting the permissions (basically, invoking the method above) is very slow. Can it only be related to Redis? All Redis settings are default:
I have one more idea, please hold on.
hi
BTW, List<PermissionValueProviderInfo> Providers contains no elements which also looks weird to me, i think logically it always would have to contain at least 'MR' according to the DB and the corresponding screenshot above.
Can you debug the app to inject the
IOptions<PermissionManagementOptions>
to check itsManagementProviders
property.
So I want to get the list, where input.IsGranted
is true
for let's say "Role 1". But now it returns me no such data (multipleGrantInfo.Result
does not contain data where IsGranted
is true
). It used to return data properly before and as you were able to see from the screen above, there is data for "Role 1", the tenant is correct, the provider name, ModulePermissionRoleValueProvider.ProviderName
, is correct ("MR"):
public virtual async Task<PagedResultDto<PermissionGrantInfoDto>> GetModuleRolesAsPermissionsAsync(GetRolePermissionsInput input)
{
var parentRole = await _identityRoleRepository.FindAsync(input.Id, false);
if (parentRole == null)
{
throw new BusinessException(DomainErrorCodes.NotFound, _stringLocalizer.GetString("Roles:RoleNotFound"));
}
var permissionDefinitionGroups = await _permissionDefinitionManager.GetGroupsAsync();
var permissionDefinitionModuleRoleGroup = permissionDefinitionGroups.FirstOrDefault(x => x.Name == ModulePermissionConsts.GroupName);
if (permissionDefinitionModuleRoleGroup == null)
{
throw new ConfigurationErrorsException("Missing Module Role Permission Definition Group");
}
var groupDto = CreatePermissionGroupDto(permissionDefinitionModuleRoleGroup);
var neededCheckPermissions = new List<PermissionDefinition>();
foreach (var topLevelPermission in permissionDefinitionModuleRoleGroup.GetPermissionsWithChildren()
.Where(x => x.Name != ModulePermissionConsts.RoleSubGroupName && x.IsEnabled && x.Providers.Contains(ModulePermissionRoleValueProvider.ProviderName)))
{
if (await _simpleStateCheckerManager.IsEnabledAsync(topLevelPermission))
{
neededCheckPermissions.Add(topLevelPermission);
}
}
if (!neededCheckPermissions.Any())
{
return new PagedResultDto<PermissionGrantInfoDto>
{
TotalCount = 0,
Items = new List<PermissionGrantInfoDto>()
};
}
var grantInfoDtos = neededCheckPermissions.Select(CreatePermissionGrantInfoDto);
var multipleGrantInfo = await _permissionManager.GetAsync
(
neededCheckPermissions.Select(x => x.Name).ToArray(), ModulePermissionRoleValueProvider.ProviderName, parentRole.Name
);
foreach (var grantInfo in multipleGrantInfo.Result.WhereIf(input.IsGranted.HasValue, x => x.IsGranted == input.IsGranted.Value))
{
var grantInfoDto = grantInfoDtos.First(x => x.Name == grantInfo.Name);
grantInfoDto.IsGranted = grantInfo.IsGranted;
foreach (var provider in grantInfo.Providers)
{
grantInfoDto.GrantedProviders.Add(new ProviderInfoDto
{
ProviderName = provider.Name,
ProviderKey = provider.Key,
});
}
groupDto.Permissions.Add(grantInfoDto);
}
return new PagedResultDto<PermissionGrantInfoDto>
{
TotalCount = groupDto.Permissions.Count,
Items = groupDto.Permissions.ComplexOrderBy(input.Sorting).Skip(input.SkipCount).Take(input.MaxResultCount).ToList()
};
}
It looks to me that the method
var multipleGrantInfo = await _permissionManager.GetAsync
(
neededCheckPermissions.Select(x => x.Name).ToArray(), ModulePermissionRoleValueProvider.ProviderName, parentRole.Name
);
just ignores the value from DB now and so IsGranted
is always false, because the input parameters here are all correct (neededCheckPermissions contains correct names, ProviderName is 'MR' and role name is 'Role 1'). BTW, List<PermissionValueProviderInfo> Providers
contains no elements which also looks weird to me, i think logically it always would have to contain at least 'MR' according to the DB and the corresponding screenshot above.
Unfortunately, I cannot share our code. So I will try to cope with this myself and just share new details when I find our what's happening.