Activities of "alexander.nikonov"

4.3.0 / Angular / separate IdentityServer

I have custom implementation of IAuthorizationMiddlewareResultHandler (it checks missing permissions for a user) and I want to provide additional exception info from it to Angular client in pop-up window. It works in some scenarios - like the page is accessed from navigation menu and it misses some permission. But if I try to access the page which is not accessible via typing its address in a browser - I ALWAYS get the following page without details:

Answer

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?)

Answer

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?"

Answer

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?

Answer

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...

Answer

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?

I am sorry, but I am now allowed to share the project. What I can do is just to show some informational screendumps, send logs and separate code files... Please let me know if I can measure some additional timings somewhere, etc. Now - just some thoughts: when I use Postman to send API requests - first TTFB is large, that's right, second and further - 10 and more times decreased. That would explain DI initialization. But in real situation, when I refresh the same page in Angular app - I am getting almost the same big timings. For instance, first run ~5-6sec per lookup API request. Next run ~1.5-3sec. Next run - about the same time... Next run - attention - ~4sec again!! Even retrieving refresh tokens at IdentityServer takes ages:

UPDATE: I've updated my controllers - removing all appservices instantiation at all. There is no DB access to retrieve data now anywhere, empty data is returned:

    [HttpGet]
    [Route("values/owners/{languageCode?}")]
    public virtual async Task&lt;PagedResultDto&lt;LookupItemDto&lt;int&gt;>> GetOwnerAsync([FromRoute] string languageCode = null)
    {
        return new PagedResultDto&lt;LookupItemDto&lt;int&gt;> { Items = new List&lt;LookupItemDto&lt;int&gt;>(), TotalCount = 0 };
    }

Also, made all API calls anonymous and without audit logging. And TTFB is still unsasisfactory. Any ideas?

DB resides on Azure cloud. Everything the rest - on localhost. Just in case: I won't be able to create local copy of DB if it is needed for a deeper testing. But as I already mentioned, sometimes there are unexplained lags even if I don't access DB.

I returned from vacation and keep on testing. I am not sure now it's a DB thing. I run API GET request from DB via appservice and TTFB was about 3-4s. Then I replaced the AbpController code which accesses DB via appservice with already mentioned above List<int> data:

        [HttpGet]
        [Route("values/module-types/{languageCode?}")]
        public virtual async Task<List<int>> GetModuleTypeAsync([FromRoute] string languageCode = null)
        {
            return new List<int>
            {
                0, 1, 2, 3, 4, 5, 6, 7, 999, 10000
            };
            // AppService DB request is commented!!!
        }

And TTFB still is 3s!

What do you suggest? Probably it has something to do with appservice DI instantiation in AbpControllers? I tried to run it in several mins when writing to you - and the time is about 50-60ms! Another API request - with appservice engaged, run straight after this, is 200-300ms...

So in other words - sometimes TTFS is more or less OK. But sometimes it is inacceptibly slow. I need to find a root cause. Emphasizing - everything is tested on localhost, VS 2020 / 2022.

I took a look at the log, trying to find information for the case when TTFB was about 3s. Please have a look at this:

2021-09-07 18:01:35.693 +03:00 [INF] Route matched with {area = "app", controller = "FixCodeValue", action = "GetModuleType", page = ""}. Executing controller action with signature System.Threading.Tasks.Task1[Volo.Abp.Application.Dtos.PagedResultDto1[AbxEps.CT.Core.Shared.LookupItemDto`1[System.Int32]]] GetModuleTypeAsync(System.String) on controller AbxEps.CentralTools.Controllers.FixCodeValues.FixCodeValueController (AbxEps.CentralTools.HttpApi). 2021-09-07 18:01:38.816 +03:00 [INF] Executing action method AbxEps.CentralTools.Controllers.FixCodeValues.FixCodeValueController.GetModuleTypeAsync (AbxEps.CentralTools.HttpApi) - Validation state: "Valid" 2021-09-07 18:01:38.996 +03:00 [INF] Executed action method AbxEps.CentralTools.Controllers.FixCodeValues.FixCodeValueController.GetModuleTypeAsync (AbxEps.CentralTools.HttpApi), returned result Microsoft.AspNetCore.Mvc.ObjectResult in 180.5069ms.

  • about 3s between "executing controller action" and "executing action method"... What those 3s in-between were spent for? Indeed, the method execution itself took only 180ms...

UPDATE: for better understanding, I'm attaching zipped log file, where logging level was set to "Debug". This time I tried to run my problematic Details page again. Could you please have a look at the log, probably it will give you some idea?

https://1drv.ms/u/s!AhWdpZddvifTtxGBMcuy35NOzw31?e=fSMRYs

On some next run, those times could be 2-3 times less (about 1.5-2sec), which is still pretty much...

UPDATE 2: cold run today, audit and authorization is turned off - debug log displays the gap between those 2 entries:

2021-09-08 11:31:11.624 +03:00 [DBG] Executing controller factory for controller AbxEps.CentralTools.Controllers.FreeCodeValues.FreeCodeValueController (AbxEps.CentralTools.HttpApi) 2021-09-08 11:31:13.063 +03:00 [DBG] Executed controller factory for controller AbxEps.CentralTools.Controllers.FreeCodeValues.FreeCodeValueController (AbxEps.CentralTools.HttpApi)

What is happening in-between? Again, next run of this request takes 255ms, so could look like a DB stuff (and now the data is cached)... But yesterday I saw big time without DB query involved.

_identityUserAppService.GetListAsync makes 2 DB request. one for count one for the list. and I think this duration is OK. 1.44second is acceptable with a fully featured request.
Do you say it adds 300ms when you derive from AbpController? See the AbpController base class which uses Lazy properties.

probably i was not precise enough. what i wanted to say is that response time is approx 300ms when not using appservice - just forming test data in controller directly as shown in the code above. if i access real DB data from identityUserService (this exact ABP service is taken just in test purpose, because you know it well) - it is already 4sec which i suppose is too much, because usually it is said one request needs to take less than 1 sec. so what would you suggest?

Showing 31 to 40 of 141 entries
Made with ❤️ on ABP v9.1.0-rc.1. Updated on January 17, 2025, 14:13