Open Closed

How to manage the list of the permissions from all the apps in one place using Redis server cache? #7706


User avatar
0
alexander.nikonov created
  • ABP Framework version: v8.1.3
  • UI Type: Angular
  • Database System: EF Core (Oracle)
  • Auth Server Separated

We used a built-in ABP cache in our ABP framework-based solutions plus Ocelot gateway project which aggregated the permissions from different sites plus RabbitMQ synchronization for updating the permissions cache on all the sites once these permissions are updated on the single Permission Management page.

We now decided to abandon this structure in favor of Redis server cache.

Still, it is not clear for me, how to get the list of ALL the permissions (for all the sites) having now Redis server cache at hand?

I took a look at the keys of the running Redis server for the started applications, but the naming is a mess and it does not look like it contains the permissions in principle... Maybe I need to add some code to manually place ALL the permissions in Redis cache after the application has started?? Which looks a bit weird...


32 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You should use the services related to ABP permission management instead of directly reading/writing to the Redis cache.

  • User Avatar
    0
    alexander.nikonov created

    hi

    You should use the services related to ABP permission management instead of directly reading/writing to the Redis cache.

    I didn't intend to use some non-standard methods. I started from usual IPermissionManager methods. But it only returns me "per-application" permissions, not all. Please give me a real example of the code which would return the permissions for ALL the running applications.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can use IPermissionDefinitionManager to get all permission definitions.

  • User Avatar
    0
    alexander.nikonov created

    hi

    You can use IPermissionDefinitionManager to get all permission definitions.

    It does not return "all" permission definitions. It only returns the definitions of the current application plus the definitions from the modules which I consumed as Nuget packages (including ABP permissions, of course).

    But I was talking about ALL the permission definitions from ALL running applications. Otherwise what is the point of using Redis cache, which supposedly has to store ALL such permissions?

    I need to manage ALL the permissions from ALL running applications using Permission Management page on one of my portals, but those changes need to be spread across all the running applications straight after.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Have you enabled the Dynamic Permission feature?

    https://abp.io/support/questions/5671/Generate-Permission-Automatically-from-external-service#answer-3a0d4bfb-8d49-dcad-8e23-50b633870090

  • User Avatar
    0
    alexander.nikonov created

    hi

    Have you enabled the Dynamic Permission feature?

    https://abp.io/support/questions/5671/Generate-Permission-Automatically-from-external-service#answer-3a0d4bfb-8d49-dcad-8e23-50b633870090

    No. I will. Do i have to add this inside HttpApiHostModule? And also inside OpenIdServerModule?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You need to enable IsDynamicPermissionStoreEnabled & SaveStaticPermissionsToDatabase in every project.

  • User Avatar
    0
    alexander.nikonov created

    I now started getting the exception in my custom service, but inside your code. Have no clue why. Did I miss some additional steps when moving to dynamic permissions? This step - "You should add a dynamic permission group and add sub-permissions to it" or it is not relevant?

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IAbpApplicationConfigurationAppService))]
    public class ModulePermissionApplicationConfigurationAppService : AbpApplicationConfigurationAppService, IAbpApplicationConfigurationAppService
    {
        public override async Task<ApplicationConfigurationDto> GetAsync(ApplicationConfigurationRequestOptions options)
        {
            var result = await base.GetAsync(options); //HERE
            ...
        }
        ...
    }
    

    The exception is:

    [01:23:05 ERR] Volo.Abp.AbpException: Undefined feature: LeptonManagement.Enable at Volo.Abp.Features.FeatureDefinitionManager.GetAsync(String name) at Volo.Abp.Features.FeatureChecker.GetOrNullAsync(String name) at Volo.Abp.Features.FeatureCheckerBase.IsEnabledAsync(String name) at Volo.Abp.Features.FeatureCheckerExtensions.IsEnabledAsync(IFeatureChecker featureChecker, Boolean requiresAll, String[] featureNames) at Volo.Abp.Features.RequireFeaturesSimpleStateChecker1.IsEnabledAsync(SimpleStateCheckerContext1 context) at Volo.Abp.SimpleStateChecking.SimpleStateCheckerManager1.InternalIsEnabledAsync(TState state, Boolean useBatchChecker) at Volo.Abp.SimpleStateChecking.SimpleStateCheckerManager1.IsEnabledAsync(TState state) at Volo.Abp.Authorization.Permissions.PermissionChecker.IsGrantedAsync(ClaimsPrincipal claimsPrincipal, String[] names) at Volo.Abp.Authorization.Permissions.PermissionChecker.IsGrantedAsync(String[] names) at Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService.GetAuthConfigAsync() at Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService.GetAsync(ApplicationConfigurationRequestOptions options) at AbxEps.CT.ModulePermission.ModulePermissionApplicationConfigurationAppService.GetAsync(ApplicationConfigurationRequestOptions options) in C:\CT\AbxEps.AbpModules\AbxEps.CT.ModulePermission\src\AbxEps.CT.ModulePermission.Application\ModulePermissionApplicationConfigurationAppService.cs:line 77 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope) at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync(ApplicationConfigurationRequestOptions options) at lambda_method3395(Closure, Object) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Undefined feature: LeptonManagement.Enable

    Can you test your code with a brand new database and redis?

  • User Avatar
    0
    alexander.nikonov created

    hi

    Undefined feature: LeptonManagement.Enable

    Can you test your code with a brand new database and redis?

    Even if i tested it in a test solution using a clean DB (which is almost impossible), it would not help me to troubleshoot the error in our solution. I suggest to drilldown the error instead.

    So I took a look a the DB and can see that the mentioned feature is present. Why is it reported as undefined?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you try to enable the dynamic feature as well?

    It will also store the feature's definitions to the database.

  • User Avatar
    0
    alexander.nikonov created

    I did it and now everything seems to be in order. But now there is a long way to modify a custom mechanism of permissions we had in addition to the standard one and it was based on static permissions and built-in ABP cache.

    I can close this ticket. But I do not guarantee I won't have further questions along the way. Should I close the ticket and then reopen if needed?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    If you got a new problem, You can create a new one.

    Thanks.

  • User Avatar
    0
    alexander.nikonov created

    hi

    If you got a new problem, You can create a new one.

    Thanks.

    Still, I eventually found the thing which does not work now. So we have had an additional mechanism of permissions, with a different provider name (MR). And it worked properly - in the given example, a usual role "Role 1" has permissions assigned, as you can see. However these permissions are all displayed as non-granted by standard methods after changing to dynamic permissions + Redis. Any clues why?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Did you change the record in the database table directly?

    If you change the data in the database, you need to clear the cache. Otherwise, the framework will not know if the database has been changed.

  • User Avatar
    0
    alexander.nikonov created

    No, I did not change the data in DB. It is the same as it was before switching to Redis + dynamic permissions.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please share your code to reproduce this.

    Thanks.

  • User Avatar
    0
    alexander.nikonov created

    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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can share the code of the service call.

    For example, how do you grand permission to a role and then check the permissions code snippet?

  • User Avatar
    0
    alexander.nikonov created

    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&lt;PagedResultDto&lt;PermissionGrantInfoDto&gt;> 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&lt;PermissionDefinition&gt;();
            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&lt;PermissionGrantInfoDto&gt;
                {
                    TotalCount = 0,
                    Items = new List&lt;PermissionGrantInfoDto&gt;()
                };
            }
        
            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&lt;PermissionGrantInfoDto&gt;
            {
                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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    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 its ManagementProviders property.

  • User Avatar
    0
    alexander.nikonov created

    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 its ManagementProviders property.

  • User Avatar
    0
    alexander.nikonov created

    I have one more idea, please hold on.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    In which project did you add your ModulePermissionRoleValueProvider to ManagementProviders?

    Does the current debug project depend on your project?

  • User Avatar
    0
    alexander.nikonov created

    I did not transfer these lines to the project which now is responsible for Role Management:

        Configure&lt;PermissionManagementOptions&gt;(options =>
        {
            options.ManagementProviders.Add&lt;ModulePermissionManagementProvider&gt;();
            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:

Made with ❤️ on ABP v9.1.0-preview. Updated on December 05, 2024, 12:19