Open Closed

Permission checker #627


User avatar
0
alexander.nikonov created

3.3.1 / Angular

Hi ABP team.

I created solution as an ABP module. At this moment i would like to check for permissions which were granted for a user's roles. I'm using IsGrantedAsync method of IPermissionStore interface. But this method returns negative result every time. I'm using "* .HttpApi.Host" project to run and test my solution.

Also I've found out that information about user isn't complete: the user's roles are absent in CurrentUser member of ApplicationService object, but access token contains this data.

Could you please suggest what I did wrong and how it can be fixed? I would like to add a custom provider name like "Q", what am I supposed to do in this case and how to make IPermissionStore interface methods work with a new provider name?


41 Answer(s)
  • User Avatar
    0
    ServiceBot created
    Support Team Automatic process manager

    This question has been automatically marked as stale because it has not had recent activity.

  • User Avatar
    0
    alexander.nikonov created

    I am running two Angular apps: https://localhost:4200 (Permissions are assigned and consumed here) and https://localhost:4201 (Permissions are only consumed here). Each of these two Angular apps have own HTTP API host (and own full-fledged ABP-based solution) and they share the same Identity Server. To control full set of permission in app Angular 1, I have Nuget package of Application.Contracts project from Solution 2 in Application.Contracts project of Solution 1:

    So far so good. I can see and change role permissions:

    The problem is that the changes made in Angular 1 are not reflected in Angular 2 - I still can see or not see the protected pages after page reload or re-login:

    I've tried to override and play with IDistributedCache<PermissionGrantCacheItem>, but it does not work - most likely because both hosts have separate caches. But how to resolve the described issue then?

  • User Avatar
    0
    alexander.nikonov created

    UPDATE: i've used RabbitMq to notify Angular 2 about permission changes in Angular 1. I am now trying to modify Angular 2 permission cache via SetManyAsync - it adds the values to the cache in Angular 2, but it still does not reflect the changes on page refresh...

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Will it work if you logout and log in Angular 2?

  • User Avatar
    0
    alexander.nikonov created

    I understood my mistake in the code: I am missing setting current tenant before changing cache. I am going to send tenant GUID from RabbitMq sender.

    Anyway, here's the override of PermissionAppServicein back-end 1:

        using Volo.Abp.PermissionManagement;
        using System.Threading.Tasks;
        using Volo.Abp.Authorization.Permissions;
        using Microsoft.Extensions.Options;
        using System.Linq;
        using Volo.Abp.DependencyInjection;
        using AbxEps.RabbitMq.Client;
        using AbxEps.RabbitMq.Client.Messages;
        using AbxEps.Fines;
    
        namespace AbxEps.CentralTools
        {
            [Dependency(ReplaceServices = true)]
            [ExposeServices(typeof(IPermissionAppService))]
            public class CentralToolsPermissionAppService : PermissionAppService
            {
                private readonly IRabbitMqManager _rabbitMqManager;
    
                public CentralToolsPermissionAppService(
                    IPermissionManager permissionManager,
                    IPermissionDefinitionManager permissionDefinitionManager,
                    IOptions<PermissionManagementOptions> options,
                    IPermissionStateManager permissionStateManager,
                    IRabbitMqManager rabbitMqManager)
    
                    : base(permissionManager, permissionDefinitionManager, options, permissionStateManager)
                {
                    _rabbitMqManager = rabbitMqManager;
                }
    
                public override async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input)
                {
                    await base.UpdateAsync(providerName, providerKey, input);
    
                    var cacheRabbitMqInput = new CacheRabbitMqInput(input.Permissions.Select(x => x)
                        .ToDictionary(x => PermissionGrantCacheItem.CalculateCacheKey(x.Name, providerName, providerKey), x => x.IsGranted));
    
                    await _rabbitMqManager.CreateSender().SendAsync(new RabbitMqMessage<CacheRabbitMqInput>
                    {
                        RoutingKey = "AbxEps-Abp-Caching",
                        Body = cacheRabbitMqInput
                    });
                }
            }
        }
    

    Here's the triggered RabbitMq subscription in back-end 2:

        using Volo.Abp.PermissionManagement;
        using Volo.Abp.DependencyInjection;
        using Volo.Abp.EventBus.Distributed;
        using System.Threading.Tasks;
        using Volo.Abp.Caching;
        using System.Collections.Generic;
        using AbxEps.RabbitMq.Client.Receivers;
        using RabbitMQ.Client.Events;
        using System;
        using Microsoft.Extensions.DependencyInjection;
        using AbxEps.RabbitMq.Client.Extensions;
        using System.Linq;
    
        namespace AbxEps.Fines
        {
            public class FinesRabbitMqReceiver : RabbitMqReceiverBase
            {
                private readonly IServiceProvider _serviceProvider;
    
                public FinesRabbitMqReceiver(IServiceProvider serviceProvider) : base("AbxEps-Abp-Caching")
                {
                    _serviceProvider = serviceProvider;
                }
    
                public override async Task<object> Received(BasicDeliverEventArgs @event)
                {
                    using var scope = _serviceProvider.CreateScope();
                    
                    var permissionGrantCache = scope.ServiceProvider.GetService<IDistributedCache<PermissionGrantCacheItem>>();
    
                    var permissions = @event.ToDataObject<Dictionary<string, bool>>();
    
                    await permissionGrantCache.SetManyAsync(
                        permissions.Select(permission =>
                            new KeyValuePair<string, PermissionGrantCacheItem>(permission.Key, new PermissionGrantCacheItem(permission.Value))));
    
                    return Task.FromResult<object>(null);
                }
            }            
        }
    

    Does this approach to refresh permissions in another app looks OK or I'm missing a better ABP approach?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You have not follow abp design to develop, so there is no good approach for abp, you can continue to test your design.

  • User Avatar
    0
    alexander.nikonov created

    Please specify where I broke ABP design: both applications 1 and applications 2 - are ABP-framework-based. Permissions are set in application 1, since it's a "main" application. The issue was that the permissions set in application 1 were not applied to application 2. I found "workaround", but my question in the first place would be: "Why they are not applied to application 2? They are not applied even if a distributed cache key is not set in both applications. Is it because the cache in both applications is separate? Is using Redis cache a way to go then?"

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    I mean the previous: https://support.abp.io/QA/Questions/627?_se=bGltaW5nLm1hQHZvbG9zb2Z0LmNvbQ==#answer-85980db3-c7b4-3bf5-3859-39fd01fd03c5

  • User Avatar
    0
    alexander.nikonov created

    Ah, OK. What about ABP Permission Grant Cache? Should I use Redis to share one cache between two apps (hosted as separate IIS apps eventually) (or my approach - exchanging messages via bus and refreshing the cache in dependent apps?)

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Yes, We also use the Redis and recommend that you use it.

  • User Avatar
    0
    alexander.nikonov created

    Hi. Could you please let me know why this does not work anymore? Maybe it has something to do with the ABP version upgrade? If I remember it right, it was done for 4.x. Now we have ABP 5.1.3 and even though RabbitMq data seems to be correct, the permission update happens RANDOMLY, i.e. sometimes the permission with "false" value are not removed in fact from cache. Sometimes permissions with "true" value are not added... The question is related to SetManyAsync. I've tried to use sync method instead, tried to use RefreshManyAsync after SetManyAsync, it's all in vain:

    public class MyPermissionCacheRabbitMqReceiver : RabbitMqReceiverBase
    {
        private readonly IServiceProvider _serviceProvider;
    
        public MyPermissionCacheRabbitMqReceiver(IServiceProvider serviceProvider, string queueName) : base(queueName)
        {
            _serviceProvider = serviceProvider;
        }
    
        public override async Task&lt;object&gt; Received(BasicDeliverEventArgs @event)
        {
            var permissionCacheRabbitMqEto = @event.ToDataObject&lt;PermissionCacheRabbitMqEto&gt;();
    
            var permissionGrantCache = _serviceProvider.GetService&lt;IDistributedCache&lt;PermissionGrantCacheItem&gt;>();
    
            var currentTenant = _serviceProvider.GetService&lt;ICurrentTenant&gt;();
    
            using (currentTenant.Change(permissionCacheRabbitMqEto.AbpTenantId))
            {
                await permissionGrantCache.SetManyAsync(
                    permissionCacheRabbitMqEto.Permissions.Select(permission =>
                        new KeyValuePair&lt;string, PermissionGrantCacheItem&gt;(permission.Key, new PermissionGrantCacheItem(permission.Value))));
            }
    
            return Task.FromResult&lt;object&gt;(null);
        }
    }            
    

    The service which sends the message resides on the different host and looks like this:

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IPermissionAppService))]
    public class ApiPermissionAppService : PermissionAppService
    {
        private readonly IRabbitMqManager _rabbitMqManager;
    
        public ApiPermissionAppService
        (
            IPermissionManager permissionManager,
            IPermissionDefinitionManager permissionDefinitionManager,
            IOptions&lt;PermissionManagementOptions&gt; options,
            ISimpleStateCheckerManager&lt;PermissionDefinition&gt; permissionStateManager,
            IRabbitMqManager rabbitMqManager
        )
            : base(permissionManager, permissionDefinitionManager, options, permissionStateManager)
        {
            _rabbitMqManager = rabbitMqManager;
        }
    
        public override async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input)
        {
            await base.UpdateAsync(providerName, providerKey, input);
    
            var permissions = input.Permissions.Select(x => x)
                .ToDictionary(x => PermissionGrantCacheItem.CalculateCacheKey(x.Name, providerName, providerKey), x => x.IsGranted);
    
            await _rabbitMqManager.SendPermissionCacheChangeAsync("AbxEps-Abp-Caching", CurrentTenant.Id, permissions);
        }
    }
    

    Sorry, we cannot share our code. Creating a test project would be too complex too.

    UPDATE: I've tried to use Redis server cache instead of the built-in cache. And the problem remains. Do I ever need to make something special if I change role permission on separate app server A and want them to get applied on server B, server C...?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Could you please let me know why this does not work anymore? Maybe it has something to do with the ABP version upgrade? If I remember it right, it was done for 4.x.

    Does everything work fine if you don't upgrade?

  • User Avatar
    0
    alexander.nikonov created

    Hi.

    It's not possible to check out. Because we've already upgraded.

    In my opinion there shouldn't be any special custom code if using Redis: all permissions from all applications use the same permission cache, but they have own cache prefix, so the permissions are not mixed. And once I am done editing the roles, the cache has to be automatically updated, so when a user refreshes a webpage in app B, app C - he sees only the pages which are available according to the updated permissions. Unfortunately, it doesn't work...

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Is it possible to reproduce the problem using the brand new template project?

  • User Avatar
    0
    alexander.nikonov created

    Recreating our structure having several projects using a brand new ABP project is not a trivial and time consuming task and I'm afraid I have no project time for that. For this reason I'm asking you if there's something wrong with the code above. This is all I can provide. If this code makes no sense - I'd like to know why permission cache is not properly updated across the applications sharing the same cache - no matter if I use default in-memory cache or Redis server. All the apps are using the same Identity server and now hosted on localhost.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I don't see the problem from the code. You can simply try your idea in the template project and if you find a problem, we can fix it.

Made with ❤️ on ABP v9.1.0-preview. Updated on November 19, 2024, 10:33