Activities of "alexander.nikonov"

  • ABP Framework version: v7.0.1
  • UI Type: Angular
  • Database System: Oracle

I am observing a very weird behavior of permissions.

Let's say I have Tenant1... TenantX. I never have had any issues with accessing API resources protected with permissions by the users of these tenants. For simplicity, let's take the role "admin" - this is particularly where the issue is reproduced.

So, now I have received the complaint from TenantY. His users - which have the "admin" role assigned - cannot access specific resources (getting error 403) whereas - what is even more confusing - the other resources (and corresponding permissions) do not have such an issue.

"admin" role for TenantY does not differ from "admin" role in other tenants (at least, in UI).

I checked the DB and tenant settings thoroughly, but cannot see anything unusual.

Also, the data in AbpPermissionGrants looks the same for all tenants "admin" roles, i.e. all relevant tenants "admin" role have the complained permission assigned.

Another important note: now when I create a new tenant - I observe the same issue with error 403.

Do you have the idea what could be wrong, where should I check which settings?

  • ABP Framework version: v7.0.1.
  • UI Type: Angular
  • Database System: EF Core (Oracle)
  • Auth Server Separated

I'm trying to insert about 400 entries to the table, the table is simple. The operation is very slow (maybe it's due to the communication between localhost where the app server is and Azure DB server, but I just want to find out before jumping to the conclusions).

Here is the code (chunkSize is 100):

    private async Task TryCreateChunkAsync(List<GeoPoint> geoPoints, int chunkSize)
    {
        var dbContext = await _geoPointRepository.GetDbContextAsync();
        dbContext.ChangeTracker.AutoDetectChangesEnabled = false;
        for (var skip = 0; skip < geoPoints.Count; skip += chunkSize)
        {
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
            {
                await _geoPointRepository.InsertManyAsync(geoPoints.Skip(skip).Take(chunkSize), autoSave: false);
                await uow.CompleteAsync();
            }
        }
        dbContext.ChangeTracker.AutoDetectChangesEnabled = true;
    }

I don't know if this approach makes sense - I also tried a different approach, using DbContext, prior to that set BatchSize to 100 for our database:

var dbContext = await _geoPointRepository.GetDbContextAsync();
((MyDbContext)dbContext).AddRange(geoPoints);
await dbContext.SaveChangesAsync();

No difference. It is still very slow. There is no third-party tools for bulk operations for Oracle, so this is not an option in any case. Besides, according to some article online, the AddRange approach on 100 entries per DB round-trip is only 1.5 times slower than using bulk insert on such amount of data.

Another thing - I am not sure that ABP logging makes the insertion much slower, but I was unable to turn off these things:

despite applying [DisableAuditing] attribute to Controller or AppService method. Please note, that I don't want to use IgnoreUrls, since the Url might change in future - only the method body is relevant. I also don't want to apply [DisableAuditing] to the entity, because in some other methods I DO want the logging as usual.

What puzzles me is: >

Executed DbCommand (XXXms)

all the DbCommand take more or less appropriate amount of time (each DbCommand takes less than 1 second).

But look at this:

Executed action YYY.Controllers.GeoPoints.GeoPointController.UploadAsync (YYY.HttpApi) in 53197.6736ms

I can't explain this. I turned off the auditing by putting the attribute [DisableAuditing] on the entity class.The time for all the operations does not sum up. Why the total time is so huge, what most of this total time is spent for?? It is somehow related to the fact I'm inserting many records. Because when I do some elementary DB operation - the API request time is normal.

ABP Framework version: v7.0.1 UI Type: Angular Database System: EF Core Auth Server Separated

I observe strange behavior of ABP memory cache (DistributedCache). But I suspect it has something to do with the relevant services DI registrations.

On Application level, I register two DI instances as a singleton: a cache and a cache manager which reads or update this cache (and yes - I DO have this on Domain level - DependsOn[(...typeof(AbpCachingModule)...)]):

    public class MyApplicationModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            ...
            context.Services.AddSingleton<IDistributedCache<CockpitCacheItem>, DistributedCache<CockpitCacheItem>>();
            context.Services.AddSingleton<CockpitCacheManager>((IServiceProvider provider) => new CockpitCacheManager(provider));
            ...
        }
    }

So I have a single cache instance of this type per solution. It is identified by a single name - so there is only ONE cache ITEM per solution.

However, when I call the method cockpitCacheManager.UpdateAsync - which retrieves and updates the cache item - from HttpApi.Host project (class MyRabbitMqReceiver below), FOR THE FIRST TIME it ALWAYS reports that the cache item is not present in the cache (is null) - so it takes the data from DB. And beginning from the next invocation it's already OK (the item is in cache):

    public class MyHttpApiHostModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            ...
            context.Services.AddSingleton((IServiceProvider provider) => new MyRabbitMqReceiver(provider, "myQueueName"));
            ...
        }
    }
 
    public class MyRabbitMqReceiver: RabbitMqReceiverBase
    {
        ...
        private async Task HandleCacheItemAsync(...)
        {
            var cockpitCacheManager = _serviceProvider.GetService<CockpitCacheManager>();
            await cockpitCacheManager.UpdateAsync(cacheFieldValue1, cacheFieldValue2); //IT DOES NOT SEE THE CACHE ITEM WHEN INVOKED FOR THE FIRST TIME!
            ...
        }
    }        

I won't bore you with cockpitCacheManager.UpdateAsync implementation, because the part related to retrieving a cache item is standard (using your GetOrAddAsync method). The item is 100% in the cache, I fill it from DB prior to the requesting.

Interesting fact is that if I request this cache item from Application layer - it's always retrieved as expected.

  • ABP Framework version: v7.0.1
  • UI Type: Angular
  • Database System: EF Core (Oracle)
  • Auth Server Separated

I have custom Permission Definition Provider:

    using AbxEps.CT.Core.Extensions;
    using AbxEps.CT.Core.Localization;
    using AbxEps.CT.Core.PortalMenu;
    using Microsoft.Extensions.DependencyInjection;
    using System;
    using System.Threading.Tasks;
    using Volo.Abp.Authorization.Permissions;
    using Volo.Abp.Localization;
    using Volo.Abp.Threading;
    using Volo.Abp.Users;

    namespace AbxEps.CT.Core.Permissions
    {
        public class CorePermissionDefinitionProvider : PermissionDefinitionProvider
        {
            private readonly ICurrentUser _currentUser;
            private readonly IAbxPortalsMenuAppService _abxPortalsMenuAppService;
    
            public CorePermissionDefinitionProvider
            (
                IAbxPortalsMenuAppService abxPortalsMenuAppService,
                ICurrentUser currentUser
             )
            {
                _abxPortalsMenuAppService = abxPortalsMenuAppService;
                _currentUser = currentUser;
            }
    
            public override void Define(IPermissionDefinitionContext context)
            {
                var coreCtGroup = context.AddGroup(CorePermissions.GroupName, L("Permission:Core"));
                var fileMngPermission = coreCtGroup.AddPermission(CorePermissions.FileManager.Read, L("Permission:FileManager:Read"));
                fileMngPermission.AddChild(CorePermissions.FileManager.Modify, L("Permission:FileManager:Modify"));
                if (_currentUser.IsAuthenticated)
                {
                    AsyncHelper.RunSync(() => InitPortalAccessPermissionsAsync(context)); // Not called, because when the host is started the user is still not authenticated - so "if" condition is not invoked
                }
            }
    
            private async Task InitPortalAccessPermissionsAsync(IPermissionDefinitionContext context)
            {
                ...
            }
        }
    }
        

Whereas the permissions which are supposed to be added unconditionally (fileMngPermissions) are added successfully when host is running, the permissions which need to be added only after the user is authenticated (portalAccessPermissions) are obviously not added and not visible via IPermissionAppService:

    var allPermissionsForRole = await _permissionAppService.GetAsync("R", "Role 1");
    

How to add a whole CorePermissionDefinitionProvider or the part of its relevant permissions (portalAccessPermissions) conditionally - once the user got authenticated?

P.S. I'd prefer not to use Middleware, because its Invoke method is invoked on each request. Instead, I need to add my CorePermissionDefinitionProvider or its authentication-related permissions once after the user authentication was successful. I use external authentication via Identity Server. So this functionality needs to reside inside the application project, not Identity Server project.

Making PermissionDefinitionManager a ITransientDependency (or probably I can make CorePermissionDefinitionProvider transient too) does not sound good either: I dot not want to trigger the check each time. I just need to trigger it ONCE, but after the user has been authenticated.

  • ABP Framework version: v7.0.1.
  • UI Type: Angular
  • Database System: EF Core
  • Auth Server Separated

Hi. I can't say for sure in which moment it has begun, but now if I try to logout from the page OTHER than Home page (root page) - the logout does not happen. Here is the workflow of what is happening when I'm clicking "Logout" button in my Angular app: First connect/revocation request contains "access_token". Next "connect/revocation" request contains "refresh_token. Eventually I am just redirected to Home page. Also the whole process is very slow and not attractive visually: at first the navigation and top menu gets disappears, so I see only the current page instead of being instantly redirected to Identity Server Login box (where I do not get at all in my case). I don't know where to look at: is it a problem of front-end or back-end. Please point me in right direction.

Additionaly details: we DO have "AuthGuard" in app-routing.module.ts for each page we are trying to logout from.

Marked in red is yet another issue which is still not resolved: the subscribed API calls in the current Component: seems like at some point the user IS getting logged out (and thus is not authorized), so these requests are not allowed (but later the user is kind of logged-in again automatically).

Sorry I cannot share the source code with you. So I will be glad to follow your recommendations in troubleshooting the logout scenario.

ABP 7.0.1 / Angular

My home page shows some information to an authenticated user via API calls. If the user logs out - these methods need not to be invoked anymore. Seems like I've tried all possible ways - and it still DOES call those methods with "Not authorized (401)" error from server after I click "Logout" button. I also have tried to call Subscription$.unsubscribe() while logging out, but it still does not work.

Another question: I can logout from any page, not just Home page. There are plenty of API call subscriptions on each of them. How am I supposed to unsubscribe from all such calls with minimal code changes??

Here is the piece of the code of my Home page:

        ngOnInit() {
      
          this.oAuthService.events
            .pipe(
              filter(event => event?.type === 'logout'),
              tap(() => { 
                this.logout$.next(null); //those are called, but API calls are still invoked
                this.logout$.complete();
              }))
            .subscribe();
      
          this.homeService.getNewsForHomePage()
            .pipe(filter(() => this.configStateService.getDeep('currentUser.isAuthenticated')), takeUntil(this.destroy), takeUntil(this.logout$))
            .subscribe((newsResponse) => {
              ...
            });
      
          this.homeService.getUrlsForHomePage()
            .pipe(filter(() => this.configStateService.getDeep('currentUser.isAuthenticated')), takeUntil(this.destroy), takeUntil(this.logout$))
            .subscribe((newsUrlParameterResponse) => {
              ...
            });
        }
      
        ngOnDestroy(): void {
          this.destroy.next(null);
          this.destroy.complete();    
        }

Moreover - when I am already at this page (where this.configStateService.getDeep('currentUser.isAuthenticated') is supposed to be false, I guess): the API calls are still invoked.

  • ABP Framework version: v7.0.1
  • UI Type: Angular
  • Database System: EF Core (Oracle)
  • Auth Server Separated

After switching the tenant I need to reload navigation menu according to the current tenant's user permissions. I cannot manage to do this: when I switch to the tenant who does not have the proper permissions - the menu items are hidden. However, when I switch back - the menu items which need to be visible are not shown:

      switch(tenant: Models.Common.Lookup<string>) {
        this.oAuthService.configure(this.environment.oAuthConfig);
        let loadingToasterId = Number(this.toaster.info('::Tenants:PleaseWait', '::Tenants:Switching', { ...this.toasterOptions && { sticky: true } }));
        return from(this.oAuthService.loadDiscoveryDocument())
          .pipe
          (
            switchMap(() => from(this.oAuthService.fetchTokenUsingGrant('switch_tenant', { token: this.oAuthService.getAccessToken(), tenant: tenant.id }))),
            take(1)
          )
          .subscribe({
            complete: () => {
              this.toaster.remove(loadingToasterId);
              this.toaster.success('::Tenants:SwitchingSucceeded', '::Tenants:Switching', { ...this.toasterOptions && { life: 2000 } });
              this.sessionStateService.setTenant({ id: tenant.id, name: tenant.displayName, isAvailable: true } as CurrentTenantDto);
              this.configStateService.refreshAppState().subscribe(x => {
                this.router.navigate(['/'], { skipLocationChange: false, onSameUrlNavigation: 'reload' }).then(ready => {
                  if (ready) {
                    //TODO: is it really needed? What to do here?
                  }
                });
              });
            },
            error: () => {
              this.toaster.remove(loadingToasterId);
              this.toaster.error('::Tenants:SwitchingFailed', '::Tenants:Switching', { ...this.toasterOptions && { life: 2000 } });
            }
          });
      }
      

The method which is triggered after switching the tenant:

      init() {
        this.routesService.flat.filter(x => x.requiredPolicy && (x as any).data?.moduleId).forEach(x => x.invisible = true);
        this.routesService.refresh();
        combineLatest([this.configStateService.getDeep$('extraProperties.modulePermissionMap'), this.routesService.flat$.pipe(take(1))])
          .pipe
          (
            filter(([modulePermissionMap, route]) => Object.keys(modulePermissionMap).length > 0 && Object.keys(route).length > 0),
            takeUntil(this.destroy)
          )
          .subscribe(([modulePermissionMap, nonLazyLoadedRoute])  => {
            let permissionProhibitedPageIds: string[] = [];
            nonLazyLoadedRoute.filter(node => node.requiredPolicy).forEach((nonLazyRouteItem: ABP.Route) => {
              let moduleId = (nonLazyRouteItem as any).data?.moduleId;
              if (moduleId) {
                const moduleIdPolicyViolated = !modulePermissionMap[moduleId] || modulePermissionMap[moduleId] && !modulePermissionMap[moduleId].includes(nonLazyRouteItem.requiredPolicy as string);
                const ordinaryRolePolicyViolated = !modulePermissionMap['_ordinaryRole'] || modulePermissionMap['_ordinaryRole'] && !modulePermissionMap['_ordinaryRole'].includes(nonLazyRouteItem.requiredPolicy as string);
                if (moduleIdPolicyViolated && ordinaryRolePolicyViolated) {
                  permissionProhibitedPageIds.push(nonLazyRouteItem.name);
                }
                else {
                  nonLazyRouteItem.invisible = false;
                }
              }
            });
            this.routesService.remove(permissionProhibitedPageIds);
            this.routesService.refresh(); //tried this, but it does not help - nonLazyLoadedRoute seem to contain menu items, however they are not displayed as expected
          });
      }
      

app.component.ts:

      ngOnInit(): void {
        this.oAuthService.events
          .pipe(filter(event => event?.type === 'logout'))
          .subscribe(() => {
            this.modulePermissionHandler.deinit();
          });
    
        this.currentUser$.subscribe(currentUser => {
          if (currentUser?.isAuthenticated) {
            if (!this.appSettingInitialized) {
              this.abxTenantSwitcher.init(); // tenant switching functionality
              this.modulePermissionHandler.init(); // custom permission handling - init method shown previously
              this.appSettingInitialized = true;
            }
          }
        });
      }
  • ABP Framework version: v7.0.1
  • UI Type: Angular
  • Database System: Oracle
  • Auth Server Separated

I create a custom permission definition in DB. On next step I assign it to a user role. This next step is impossible until I restart the app (so the list of permission definitions is being actualized). I do not want app restart - I need a "hot-reload" for permission definitions. How can I implement this? No dramatic changes, no switching to dynamic permissions! - I just need a small "Refresh" method, which would trigger permission definition list reload - so they all would be available for role assignment. Please, show a piece of code.

  • ABP Framework version: v7.0.1.
  • UI type: Angular
  • DB provider: EF Core Identity Server Separated (Angular)

At some point the pages from "Identity Server" section became non-accessible - i.e. they are visible in the menu and corresponding permissions are present. But clicking on any of these pages gives error 404:

The app-routing.module.ts is quite typical:

app.component.ts contains some page replacement, but nothing related to "Identity Server" section:

constructor(
    ...
  ) {
      this.replaceableComponents.add({
        component: TenantsComponent,
        key: eSaasComponents.Tenants,
      }),
      this.replaceableComponents.add({
        component: UsersComponent,
        key: eIdentityComponents.Users
      }),
      this.replaceableComponents.add({
        component: RolesComponent,
        key: eIdentityComponents.Roles
      });
}

ngOnInit(): void {

  this.manageProfileTabs.patch(eAccountManageProfileTabNames.PersonalInfo, {
    component: AbxPersonalSettingsComponent,
  });
  this.routesService.remove([eIdentityRouteNames.OrganizationUnits]);
  this.routesService.remove([eIdentityRouteNames.Users]);
  this.routesService.remove([eIdentityRouteNames.Roles]);
  this.routesService.remove([eSaasRouteNames.Tenants]);
  ...
}

I cannot figure out, what could have caused the reported issue. Please, provide some suggestion - this code is all I can show, full project is not available.

I've implemented Module Role conception. Module Role is bound to a Module ID, It's like a unique identifier of Angular app page. A Module Role is filled with ordinary permissions. At the same time, a group of Module Roles can be assigned to an ordinary role. This conception works as expected for per-page access, when a page is accessed via menu, because Module ID is passed to back-end and I can check if there specific page has three requested permissions.

However, it doesn't work properly when I load the app and expect to see only those pages in the menu whose Module ID has the required permission for this page.

So the question is how to pass-through additional information of route (like this module ID) from Angular app to a back-end: when I load the specific page via URL: or just navigating to the app https://localhost:4200 (and expecting to see only specific module ID-related pages in the menu) to have it available in any possible way (via DI request object, etc.) inside your method:

public override async Task<MultiplePermissionGrantResult> CheckAsync(PermissionValuesCheckContext context)

?

Showing 1 to 10 of 37 entries
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11