load permissions when user is loaded
This commit is contained in:
parent
7d4e0a851a
commit
843415d9fd
@ -12,11 +12,11 @@
|
||||
|
||||
<ng-container *ngIf="searchPosition === searchPositions.beforeFilters" [ngTemplateOutlet]="searchBar"></ng-container>
|
||||
|
||||
<ng-container *ngFor="let config of filters; trackBy: trackByLabel">
|
||||
<ng-container *ngFor="let filter of filters; trackBy: trackByLabel">
|
||||
<iqser-popup-filter
|
||||
*ngIf="!config.hide"
|
||||
*ngIf="!filter.hide"
|
||||
[iqserHelpMode]="filterHelpModeKey"
|
||||
[primaryFiltersSlug]="config.slug"
|
||||
[primaryFiltersSlug]="filter.slug"
|
||||
></iqser-popup-filter>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Directive,
|
||||
EmbeddedViewRef,
|
||||
EventEmitter,
|
||||
@ -18,8 +19,6 @@ import { IqserRolesService } from './services/roles.service';
|
||||
import { IqserPermissionsService } from './services/permissions.service';
|
||||
import { List } from '../utils';
|
||||
|
||||
type NgTemplate = TemplateRef<unknown>;
|
||||
|
||||
@Directive({
|
||||
selector: '[allow]',
|
||||
})
|
||||
@ -51,6 +50,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
private readonly _permissionsService: IqserPermissionsService,
|
||||
private readonly _rolesService: IqserRolesService,
|
||||
private readonly _viewContainer: ViewContainerRef,
|
||||
private readonly _changeDetector: ChangeDetectorRef,
|
||||
templateRef: TemplateRef<unknown>,
|
||||
) {
|
||||
this.#thenTemplateRef = templateRef;
|
||||
@ -64,7 +64,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowThen(template: NgTemplate) {
|
||||
set allowThen(template: TemplateRef<unknown>) {
|
||||
assertTemplate('allowThen', template);
|
||||
this.#thenTemplateRef = template;
|
||||
this.#thenViewRef = false;
|
||||
@ -72,7 +72,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowElse(template: NgTemplate) {
|
||||
set allowElse(template: TemplateRef<unknown>) {
|
||||
assertTemplate('allowElse', template);
|
||||
this.#elseTemplateRef = template;
|
||||
this.#elseViewRef = false;
|
||||
@ -111,7 +111,8 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
|
||||
this.permissionsUnauthorized.emit();
|
||||
this.#thenViewRef = false;
|
||||
this.#elseViewRef = this.#showTemplate(this.#elseTemplateRef) ?? true;
|
||||
this.#elseViewRef = this.#showTemplate(this.#elseTemplateRef);
|
||||
this._changeDetector.markForCheck();
|
||||
}
|
||||
|
||||
#showThenBlock() {
|
||||
@ -121,12 +122,13 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
|
||||
this.permissionsAuthorized.emit();
|
||||
this.#elseViewRef = false;
|
||||
this.#thenViewRef = this.#showTemplate(this.#thenTemplateRef) ?? true;
|
||||
this.#thenViewRef = this.#showTemplate(this.#thenTemplateRef);
|
||||
this._changeDetector.markForCheck();
|
||||
}
|
||||
|
||||
#showTemplate(template?: NgTemplate) {
|
||||
#showTemplate(template?: TemplateRef<unknown>) {
|
||||
this._viewContainer.clear();
|
||||
return template ? this._viewContainer.createEmbeddedView(template) : undefined;
|
||||
return template ? this._viewContainer.createEmbeddedView(template) : true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
CanActivateChild,
|
||||
CanLoad,
|
||||
NavigationExtras,
|
||||
Route,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, CanLoad, Route, Router, RouterStateSnapshot } from '@angular/router';
|
||||
|
||||
import { firstValueFrom, forkJoin, from, of } from 'rxjs';
|
||||
import { first, mergeMap, tap } from 'rxjs/operators';
|
||||
@ -52,71 +43,53 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
return this.#checkPermissions(route);
|
||||
}
|
||||
|
||||
#validate(permissions: IqserPermissionsData, route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) {
|
||||
if (isFunction<RedirectToFn>(permissions.redirectTo) || !isRedirectWithParameters(permissions.redirectTo)) {
|
||||
return this.#checkRedirect(permissions, route, state);
|
||||
}
|
||||
|
||||
return this.#validatePermissions(permissions, route, state);
|
||||
}
|
||||
|
||||
#checkPermissions(route: IqserActivatedRouteSnapshot | IqserRoute, state?: RouterStateSnapshot) {
|
||||
const routePermissions = route.data?.permissions;
|
||||
if (!routePermissions) {
|
||||
return Promise.resolve(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
const permissions = transformPermission(routePermissions, route, state);
|
||||
|
||||
if (permissions.allow?.length > 0) {
|
||||
return this.#validate(permissions, route, state);
|
||||
if (isFunction<RedirectToFn>(permissions.redirectTo) || !isRedirectWithParameters(permissions.redirectTo)) {
|
||||
return this.#checkRedirect(permissions, route, state);
|
||||
}
|
||||
|
||||
return this.#validatePermissions(permissions, route, state);
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
#redirectToAnotherRoute(
|
||||
permissionRedirectTo: RedirectTo | RedirectToFn,
|
||||
redirectTo: RedirectTo | RedirectToFn,
|
||||
route: ActivatedRouteSnapshot | Route,
|
||||
failedPermissionName: string,
|
||||
state?: RouterStateSnapshot,
|
||||
) {
|
||||
const redirectTo = isFunction<RedirectToFn>(permissionRedirectTo)
|
||||
? permissionRedirectTo(failedPermissionName, route, state)
|
||||
: permissionRedirectTo;
|
||||
const _redirectTo = isFunction<RedirectToFn>(redirectTo) ? redirectTo(failedPermissionName, route, state) : redirectTo;
|
||||
|
||||
if (isRedirectWithParameters(redirectTo)) {
|
||||
redirectTo.navigationCommands = this.#transformNavigationCommands(redirectTo.navigationCommands, route, state);
|
||||
redirectTo.navigationExtras = this.#transformNavigationExtras(redirectTo.navigationExtras ?? {}, route, state);
|
||||
return this._router.navigate(redirectTo.navigationCommands, redirectTo.navigationExtras);
|
||||
if (isRedirectWithParameters(_redirectTo)) {
|
||||
let navigationCommands = _redirectTo.navigationCommands;
|
||||
navigationCommands = isFunction<NavigationCommandsFn>(navigationCommands)
|
||||
? navigationCommands(route, state)
|
||||
: navigationCommands;
|
||||
let navigationExtras = _redirectTo.navigationExtras ?? {};
|
||||
navigationExtras = isFunction<NavigationExtrasFn>(navigationExtras) ? navigationExtras(route, state) : navigationExtras;
|
||||
return this._router.navigate(navigationCommands, navigationExtras);
|
||||
}
|
||||
|
||||
if (Array.isArray(redirectTo)) {
|
||||
return this._router.navigate(redirectTo);
|
||||
if (Array.isArray(_redirectTo)) {
|
||||
return this._router.navigate(_redirectTo);
|
||||
}
|
||||
|
||||
return this._router.navigate([redirectTo]);
|
||||
}
|
||||
|
||||
#transformNavigationCommands(
|
||||
navigationCommands: any[] | NavigationCommandsFn,
|
||||
route: ActivatedRouteSnapshot | Route,
|
||||
state?: RouterStateSnapshot,
|
||||
) {
|
||||
return isFunction<NavigationCommandsFn>(navigationCommands) ? navigationCommands(route, state) : navigationCommands;
|
||||
}
|
||||
|
||||
#transformNavigationExtras(
|
||||
navigationExtras: NavigationExtras | NavigationExtrasFn,
|
||||
route: ActivatedRouteSnapshot | Route,
|
||||
state?: RouterStateSnapshot,
|
||||
): NavigationExtras {
|
||||
return isFunction<NavigationExtrasFn>(navigationExtras) ? navigationExtras(route, state) : navigationExtras;
|
||||
return this._router.navigate([_redirectTo]);
|
||||
}
|
||||
|
||||
#checkRedirect(permissions: IqserPermissionsData, route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) {
|
||||
if (!permissions.allow || permissions.allow.length === 0) {
|
||||
return Promise.resolve(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
let failedPermission = '';
|
||||
@ -205,27 +178,21 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
}
|
||||
|
||||
#validatePermissions(
|
||||
purePermissions: IqserPermissionsData,
|
||||
permissions: IqserPermissionsData,
|
||||
route: ActivatedRouteSnapshot | Route,
|
||||
state?: RouterStateSnapshot,
|
||||
): Promise<boolean> {
|
||||
const permissions: IqserPermissionsData = {
|
||||
...purePermissions,
|
||||
};
|
||||
const results = Promise.all([this._permissionsService.has(permissions.allow), this._rolesService.has(permissions.allow)]);
|
||||
|
||||
return Promise.all([this._permissionsService.has(permissions.allow), this._rolesService.has(permissions.allow)]).then(
|
||||
([hasPermission, hasRole]) => {
|
||||
if (hasPermission || hasRole) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (permissions.redirectTo) {
|
||||
return results
|
||||
.then(([hasPermission, hasRole]) => hasPermission || hasRole)
|
||||
.then(isAllowed => {
|
||||
if (!isAllowed && permissions.redirectTo) {
|
||||
const redirect = this.#redirectToAnotherRoute(permissions.redirectTo, route, permissions.allow[0], state);
|
||||
return redirect.then(() => false);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
return isAllowed;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, Observable, of, switchMap } from 'rxjs';
|
||||
|
||||
import { isArray, isString, toArray } from '../utils';
|
||||
import { IqserPermissions, PermissionValidationFn } from '../types';
|
||||
import { List } from '../../utils';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class IqserPermissionsService {
|
||||
@ -22,9 +23,8 @@ export class IqserPermissionsService {
|
||||
has(permission: string): Promise<boolean>;
|
||||
has(permissions: List): Promise<boolean>;
|
||||
has(permissions: string | List): Promise<boolean>;
|
||||
has(value: string | List): Promise<boolean> {
|
||||
const isEmpty = !value || value.length === 0;
|
||||
return isEmpty ? Promise.resolve(true) : this.#has(toArray(value));
|
||||
has(permissions: string | List): Promise<boolean> {
|
||||
return firstValueFrom(this.has$(permissions));
|
||||
}
|
||||
|
||||
load(permissions: IqserPermissions | List) {
|
||||
@ -43,11 +43,29 @@ export class IqserPermissionsService {
|
||||
}
|
||||
|
||||
get(): IqserPermissions;
|
||||
|
||||
get(permission: string): PermissionValidationFn | undefined;
|
||||
|
||||
get(permission?: string): IqserPermissions | PermissionValidationFn | undefined {
|
||||
return permission ? this.#permissions$.value[permission] : this.#permissions$.value;
|
||||
}
|
||||
|
||||
has$(permission: string): Observable<boolean>;
|
||||
has$(permissions: List): Observable<boolean>;
|
||||
has$(permissions: string | List): Observable<boolean>;
|
||||
has$(permissions: string | List) {
|
||||
const isEmpty = !permissions || permissions.length === 0;
|
||||
if (isEmpty) {
|
||||
return of(true);
|
||||
}
|
||||
|
||||
return this.permissions$.pipe(
|
||||
map(all => toArray(permissions).map(permission => all[permission]?.(permission, all) ?? false)),
|
||||
switchMap(promises => Promise.all(promises)),
|
||||
map(results => results.every(result => result)),
|
||||
);
|
||||
}
|
||||
|
||||
#reduce(permissions: IqserPermissions | List, initialValue = {} as IqserPermissions) {
|
||||
if (isArray(permissions)) {
|
||||
return this.#permissions$.next(this.#reduceList(permissions, initialValue));
|
||||
@ -67,13 +85,4 @@ export class IqserPermissionsService {
|
||||
return { ...acc, [permission]: () => true };
|
||||
}, initialValue);
|
||||
}
|
||||
|
||||
#has(permissions: List): Promise<boolean> {
|
||||
const promises = permissions.map(permission => {
|
||||
const validationFn = this.#permissions$.value[permission];
|
||||
return validationFn?.(permission, this.#permissions$.value) ?? false;
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(results => results.every(result => result));
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,12 @@ import { IMyProfileUpdateRequest } from '../types/my-profile-update.request';
|
||||
import { IProfileUpdateRequest } from '../types/profile-update.request';
|
||||
import { KeycloakProfile } from 'keycloak-js';
|
||||
import { IqserUser } from '../iqser-user.model';
|
||||
import { IqserPermissionsService, IqserRolesService } from '../../permissions';
|
||||
|
||||
interface IToken {
|
||||
sub: string;
|
||||
resource_access: { account: { roles: List }; redaction: { roles: List } };
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export abstract class IqserUserService<
|
||||
@ -28,6 +34,8 @@ export abstract class IqserUserService<
|
||||
protected readonly _baseHref = inject(BASE_HREF);
|
||||
protected readonly _keycloakService = inject(KeycloakService);
|
||||
protected readonly _cacheApiService = inject(CacheApiService);
|
||||
protected readonly _permissionsService = inject(IqserPermissionsService);
|
||||
protected readonly _rolesService = inject(IqserRolesService);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -65,11 +73,6 @@ export abstract class IqserUserService<
|
||||
}
|
||||
|
||||
async loadCurrentUser(): Promise<Class | undefined> {
|
||||
const token = await this._keycloakService.getToken();
|
||||
const decoded = jwt_decode(token);
|
||||
const userId = (<{ sub: string }>decoded).sub;
|
||||
|
||||
const roles = this._keycloakService.getUserRoles(true).filter(role => this._rolesFilter(role));
|
||||
let profile;
|
||||
try {
|
||||
profile = await this._keycloakService.loadUserProfile(true);
|
||||
@ -79,6 +82,13 @@ export abstract class IqserUserService<
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await this._keycloakService.getToken();
|
||||
const decoded: IToken = jwt_decode(token);
|
||||
const userId = decoded.sub;
|
||||
this._permissionsService.load(decoded.resource_access.redaction.roles);
|
||||
|
||||
const roles = this._keycloakService.getUserRoles(true).filter(role => this._rolesFilter(role));
|
||||
this._rolesService.load(roles);
|
||||
const user = new this._entityClass(profile, roles, userId);
|
||||
this.replace(user);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user