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.
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.
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;
}
}
});
}
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.
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)
?
After upgrading from ABP 5.1.2 to 7.0.1 I cannot add a claim to "Owned" When I'm trying to move any claim from right to left:
I am getting JS exception (because the given property is "undefined"):
The same goes for Identity Resources (and maybe other sections like this).
FYI: we keep using Identity Server, not Open ID Dict - probably it has something to do with this.
Please lead me in the right direction to fix this - I cannot share the code and we need to make this setup working ASAP, since the newly added solution cannot be run.
We would like to implement the logic which implies assigning permissions to a custom role, while such custom role in turn is assigned to a standard role of the system. Could you please advice how can we fulfil such task by means of ABP framework?
I have a couple of issues / questions with the migration.
Server side: probably because of absent Devart Oracle driver which supports .NET 7.0+ the Add-Migration
causes the following error (please note that we have set target=NET7 for all our projects):
Return type in method 'Devart.Common.Entity.c5.FindMapping(System.Type)' on type 'Devart.Common.Entity.c5' from assembly 'Devart.Data.Oracle.Entity.EFCore, Version=9.16.1434.0, Culture=neutral, PublicKeyToken=09af7300eec23701' is not compatible with base type method 'Microsoft.EntityFrameworkCore.Storage.RelationalTypeMappingSource.FindMapping(System.Type)'.
The Add-Migration
itself with the standard driver completes successfully, but when I run Update-Database
- very soon I end up with the errors related to Upper / Lower case in Table/Column names + quotes around those. We did not have such problems with Devart thanks to these options:
var config = Devart.Data.Oracle.Entity.Configuration.OracleEntityProviderConfig.Instance;
config.Workarounds.DisableQuoting = true;
config.CodeFirstOptions.UseNonLobStrings = true;
config.CodeFirstOptions.UseNonUnicodeStrings = true;
How to make a successful migration? Sorry - I cannot share the source code.
Client side: it is not clear which version of Angular should we use now, with ABP 7.0.1. We have had Angular 13 so far...I've tried to upgrade Angular after using abp update
, but faced the compilation problem for our Angular packages, similar to these, but related to ng-bootstrap and other material components. So what Angular version should we use? Should we upgrade from version 13 and how to make it correct?