From 2933e4d45f3cf52281f16a1498dba6f46113c163 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Sat, 29 Oct 2022 18:55:57 +0300 Subject: [PATCH] RED-5482: wip new permissions --- apps/red-ui/src/app/app-routing.module.ts | 38 +++++++++-- apps/red-ui/src/app/app.module.ts | 2 + .../screens/audit/audit-screen.component.ts | 1 - apps/red-ui/src/app/users/red-role.guard.ts | 6 +- apps/red-ui/src/app/users/roles.ts | 65 ++++++++++++------- apps/red-ui/src/app/users/user.service.ts | 23 +++++-- .../app/utils/configuration.initializer.ts | 6 +- libs/common-ui | 2 +- 8 files changed, 104 insertions(+), 39 deletions(-) diff --git a/apps/red-ui/src/app/app-routing.module.ts b/apps/red-ui/src/app/app-routing.module.ts index 6686dc25f..0de22ee9c 100644 --- a/apps/red-ui/src/app/app-routing.module.ts +++ b/apps/red-ui/src/app/app-routing.module.ts @@ -1,8 +1,8 @@ import { AuthErrorComponent } from '@components/auth-error/auth-error.component'; -import { CompositeRouteGuard, CustomRouteReuseStrategy, IqserAuthGuard } from '@iqser/common-ui'; +import { CompositeRouteGuard, CustomRouteReuseStrategy, IqserAuthGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui'; import { RedRoleGuard } from '@users/red-role.guard'; import { BaseScreenComponent } from '@components/base-screen/base-screen.component'; -import { RouteReuseStrategy, RouterModule, Routes } from '@angular/router'; +import { RouteReuseStrategy, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component'; import { DossiersGuard } from '@guards/dossiers.guard'; @@ -15,8 +15,9 @@ import { TrashGuard } from '@guards/trash.guard'; import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain'; import { DossierFilesGuard } from '@guards/dossier-files-guard'; import { WebViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard'; +import { ROLES } from '@users/roles'; -const routes: Routes = [ +const routes: IqserRoutes = [ { path: '', redirectTo: 'main', @@ -42,10 +43,21 @@ const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule), - canActivate: [CompositeRouteGuard], + canActivate: [CompositeRouteGuard, IqserPermissionsGuard], data: { routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard], - requiredRoles: ['RED_USER'], + permissions: { + allow: [ + ROLES.templates.read, + ROLES.fileAttributes.readConfig, + ROLES.watermarks.read, + ROLES.dictionaryTypes.read, + ROLES.colors.read, + ROLES.states.read, + ROLES.notifications.read, + ], + redirectTo: '/auth-error', + }, }, }, { @@ -134,10 +146,22 @@ const routes: Routes = [ pathMatch: 'full', }, ], - canActivate: [CompositeRouteGuard], + canActivate: [CompositeRouteGuard, IqserPermissionsGuard], data: { routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard, DossierTemplateExistsGuard], - requiredRoles: ['RED_USER'], + permissions: { + allow: [ + ROLES.templates.read, + ROLES.fileAttributes.readConfig, + ROLES.watermarks.read, + ROLES.dictionaryTypes.read, + ROLES.colors.read, + ROLES.states.read, + ROLES.notifications.read, + ROLES.dossiers.read, + ], + redirectTo: '/', + }, }, }, ], diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index d97fba504..7e9e22bdb 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -13,6 +13,7 @@ import { IqserHelpModeModule, IqserLoadingModule, IqserPermissionsModule, + IqserPermissionsService, IqserTranslateModule, IqserUsersModule, LanguageService, @@ -164,6 +165,7 @@ export const appModuleFactory = (config: AppConfig) => { UserService, UserPreferenceService, LicenseService, + IqserPermissionsService, ], }, { diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts index 739d3a0ab..3e476542b 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts @@ -14,7 +14,6 @@ import { RouterHistoryService } from '@services/router-history.service'; const PAGE_SIZE = 50; @Component({ - selector: 'redaction-audit-screen', templateUrl: './audit-screen.component.html', styleUrls: ['./audit-screen.component.scss'], providers: listingProvidersFactory(AuditScreenComponent), diff --git a/apps/red-ui/src/app/users/red-role.guard.ts b/apps/red-ui/src/app/users/red-role.guard.ts index f41df9c69..5fc4db398 100644 --- a/apps/red-ui/src/app/users/red-role.guard.ts +++ b/apps/red-ui/src/app/users/red-role.guard.ts @@ -1,18 +1,20 @@ import { inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { UserService } from './user.service'; -import { IqserRoleGuard } from '@iqser/common-ui'; +import { IqserPermissionsService, IqserRoleGuard } from '@iqser/common-ui'; +import { ROLES } from '@users/roles'; @Injectable({ providedIn: 'root', }) export class RedRoleGuard extends IqserRoleGuard { protected readonly _userService = inject(UserService); + protected readonly _permissionsService = inject(IqserPermissionsService); async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { const currentUser = this._userService.currentUser; - if (!currentUser.hasAnyREDRoles) { + if (!this._permissionsService.has(ROLES.any)) { await this._router.navigate(['/auth-error']); this._loadingService.stop(); return false; diff --git a/apps/red-ui/src/app/users/roles.ts b/apps/red-ui/src/app/users/roles.ts index 975cd9a3a..5920fb42f 100644 --- a/apps/red-ui/src/app/users/roles.ts +++ b/apps/red-ui/src/app/users/roles.ts @@ -1,17 +1,9 @@ export const ROLES = { RED_ADD_COMMENT: 'red-add-comment', - RED_ADD_DICTIONARY_ENTRY: 'red-add-dictionary-entry', - RED_ADD_DOSSIER_DICTIONARY_ENTRY: 'red-add-dossier-dictionary-entry', RED_ADD_REDACTION: 'red-add-redaction', - RED_ADD_UPDATE_DICTIONARY_TYPE: 'red-add-update-dictionary-type', - RED_ADD_UPDATE_DOSSIER_DICTIONARY_TYPE: 'red-add-update-dossier-dictionary-type', RED_CONVERT_HIGHLIGHTS: 'red-convert-highlights', RED_CREATE_TENANT: 'red-create-tenant', RED_DELETE_COMMENT: 'red-delete-comment', - RED_DELETE_DICTIONARY_ENTRY: 'red-delete-dictionary-entry', - RED_DELETE_DICTIONARY_TYPE: 'red-delete-dictionary-type', - RED_DELETE_DOSSIER_DICTIONARY_ENTRY: 'red-delete-dossier-dictionary-entry', - RED_DELETE_DOSSIER_DICTIONARY_TYPE: 'red-delete-dossier-dictionary-type', RED_DELETE_FILE: 'red-delete-file', RED_DELETE_HIGHLIGHTS: 'red-delete-highlights', RED_DELETE_IMPORTED_REDACTIONS: 'red-delete-imported-redactions', @@ -35,22 +27,15 @@ export const ROLES = { RED_PROCESS_DOWNLOAD: 'red-process-download', RED_PROCESS_MANUAL_REDACTION_REQUEST: 'red-process-manual-redaction-request', RED_PROCESS_TEXTHIGHLIGHTS: 'red-process-texthighlights', - RED_READ_APP_CONFIGURATION: 'red-read-app-configuration', - RED_READ_COLORS: 'red-read-colors', - RED_READ_DICTIONARY_TYPES: 'red-read-dictionary-types', RED_READ_DIGITAL_SIGNATURE: 'red-read-digital-signature', RED_READ_DOSSIER_ATTRIBUTES: 'red-read-dossier-attributes', RED_READ_DOSSIER_ATTRIBUTES_CONFIG: 'red-read-dossier-attributes-config', - RED_READ_DOSSIER_TEMPLATES: 'red-read-dossier-templates', RED_READ_DOWNLOAD_STATUS: 'red-read-download-status', - RED_READ_FILE_ATTRIBUTES_CONFIG: 'red-read-file-attributes-config', RED_READ_FILE_STATUS: 'red-read-file-status', - RED_READ_GENERAL_CONFIGURATION: 'red-read-general-configuration', RED_READ_LEGAL_BASIS: 'red-read-legal-basis', RED_READ_LICENSE: 'red-read-license', RED_READ_LICENSE_REPORT: 'red-read-license-report', RED_READ_MANUAL_REDACTIONS: 'red-read-manual-redactions', - RED_READ_NOTIFICATION: 'red-read-notification', RED_READ_REDACTION_LOG: 'red-read-redaction-log', RED_READ_RULES: 'red-read-rules', RED_READ_SMTP_CONFIGURATION: 'red-read-smtp-configuration', @@ -65,18 +50,11 @@ export const ROLES = { RED_SET_STATUS_APPROVED: 'red-set-status-approved', RED_SET_STATUS_UNDER_APPROVAL: 'red-set-status-under-approval', RED_UPDATE_MY_PROFILE: 'red-update-my-profile', - RED_UPDATE_NOTIFICATION: 'red-update-notification', RED_UPLOAD_FILE: 'red-upload-file', RED_UPLOAD_REPORT_TEMPLATE: 'red-upload-report-template', - RED_WRITE_APP_CONFIGURATION: 'red-write-app-configuration', - RED_WRITE_COLORS: 'red-write-colors', RED_WRITE_DIGITAL_SIGNATURE: 'red-write-digital-signature', RED_WRITE_DOSSIER_ATTRIBUTES: 'red-write-dossier-attributes', RED_WRITE_DOSSIER_ATTRIBUTES_CONFIG: 'red-write-dossier-attributes-config', - RED_WRITE_DOSSIER_TEMPLATES: 'red-write-dossier-templates', - RED_WRITE_FILE_ATTRIBUTES: 'red-write-file-attributes', - RED_WRITE_FILE_ATTRIBUTES_CONFIG: 'red-write-file-attributes-config', - RED_WRITE_GENERAL_CONFIGURATION: 'red-write-general-configuration', RED_WRITE_LEGAL_BASIS: 'red-write-legal-basis', RED_WRITE_RULES: 'red-write-rules', RED_WRITE_SMTP_CONFIGURATION: 'red-write-smtp-configuration', @@ -96,9 +74,52 @@ export const ROLES = { delete: 'red-delete-dossier', archived: 'red-archived-dossier', reanalyze: 'red-reanalyze-dossier', + dictionaryTypes: { + write: 'red-add-update-dossier-dictionary-type', + delete: 'red-delete-dossier-dictionary-type', + }, + dictionaryEntries: { + write: 'red-add-dossier-dictionary-entry', + delete: 'red-delete-dossier-dictionary-entry', + }, + }, + templates: { + read: 'red-read-dossier-templates', + write: 'red-write-dossier-templates', }, states: { read: 'red-read-dossier-status', write: 'red-write-dossier-status', }, + fileAttributes: { + write: 'red-write-file-attributes', + readConfig: 'red-read-file-attributes-config', + writeConfig: 'red-write-file-attributes-config', + }, + dictionaryEntries: { + write: 'red-add-dictionary-entry', + delete: 'red-delete-dictionary-entry', + }, + dictionaryTypes: { + read: 'red-read-dictionary-types', + write: 'red-add-update-dictionary-type', + delete: 'red-delete-dictionary-type', + }, + colors: { + read: 'red-read-colors', + write: 'red-write-colors', + }, + notifications: { + read: 'red-read-notification', + write: 'red-update-notification', + }, + appConfiguration: { + read: 'red-read-app-configuration', + write: 'red-write-app-configuration', + }, + generalConfiguration: { + read: 'red-read-general-configuration', + write: 'red-write-general-configuration', + }, + any: 'any', } as const; diff --git a/apps/red-ui/src/app/users/user.service.ts b/apps/red-ui/src/app/users/user.service.ts index 2640e248e..a95eef3fc 100644 --- a/apps/red-ui/src/app/users/user.service.ts +++ b/apps/red-ui/src/app/users/user.service.ts @@ -1,6 +1,8 @@ import { inject, Injectable } from '@angular/core'; import { User } from '@red/domain'; import { IIqserUser, IqserUserService, List, QueryParam } from '@iqser/common-ui'; +import { ROLES } from '@users/roles'; +import { of } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -17,14 +19,27 @@ export class UserService extends IqserUserService { return this.all.filter(user => user.isUser || user.isManager); } + async loadCurrentUser(): Promise { + const currentUser = await super.loadCurrentUser(); + + this._permissionsService.add({ + [ROLES.any]: (permission, all) => { + console.log('all roles: ', Object.keys(all)); + return Object.keys(all).some(key => key.startsWith('red-')); + }, + }); + + return currentUser; + } + loadAll() { - if (this.currentUser.isUserAdmin || this.currentUser.isUser || this.currentUser.isAdmin) { - return super.loadAll(); - } + const canReadUsers = this._permissionsService.has(ROLES.users.read); + return canReadUsers ? super.loadAll() : of([]); } getAll() { - const url = this.currentUser.isUserAdmin ? this._defaultModelPath : `${this._defaultModelPath}/red`; + const canReadAllUsers = this._permissionsService.has(ROLES.users.readAll); + const url = canReadAllUsers ? this._defaultModelPath : `${this._defaultModelPath}/red`; return super.getAll(url); } diff --git a/apps/red-ui/src/app/utils/configuration.initializer.ts b/apps/red-ui/src/app/utils/configuration.initializer.ts index b46d52307..7a2f34965 100644 --- a/apps/red-ui/src/app/utils/configuration.initializer.ts +++ b/apps/red-ui/src/app/utils/configuration.initializer.ts @@ -3,12 +3,13 @@ import { ConfigService } from '@services/config.service'; import { firstValueFrom, map, of, throwError } from 'rxjs'; import { KeycloakEventType, KeycloakService } from 'keycloak-angular'; import { GeneralSettingsService } from '@services/general-settings.service'; -import { LanguageService } from '@iqser/common-ui'; +import { IqserPermissionsService, LanguageService } from '@iqser/common-ui'; import { UserPreferenceService } from '@users/user-preference.service'; import { UserService } from '@users/user.service'; import { FeaturesService } from '@services/features.service'; import { SystemPreferencesService } from '@services/system-preferences.service'; import { LicenseService } from '@services/license.service'; +import { ROLES } from '@users/roles'; function lastDossierTemplateRedirect(baseHref: string, userPreferenceService: UserPreferenceService) { const url = window.location.href.split('/').filter(s => s.length > 0); @@ -29,6 +30,7 @@ export function configurationInitializer( userService: UserService, userPreferenceService: UserPreferenceService, licenseService: LicenseService, + permissionsService: IqserPermissionsService, ) { const setup = keycloakService.keycloakEvents$.pipe( filter(event => event.type === KeycloakEventType.OnReady), @@ -36,7 +38,7 @@ export function configurationInitializer( switchMap(() => keycloakService.isLoggedIn()), switchMap(loggedIn => (!loggedIn ? throwError(() => 'Not Logged In') : of({}))), switchMap(() => userService.initialize()), - switchMap(() => (!userService.currentUser.hasAnyREDRoles ? throwError(() => 'Not user has no red roles') : of({}))), + switchMap(() => (!permissionsService.has(ROLES.any) ? throwError(() => 'User has no red roles') : of({}))), switchMap(() => generalSettingsService.getGeneralConfigurations()), tap(configuration => configService.updateDisplayName(configuration.displayName)), switchMap(() => systemPreferencesService.loadPreferences()), diff --git a/libs/common-ui b/libs/common-ui index 7367c31d3..45627ed33 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 7367c31d37b19c2e3a73c886cc743b12dcccc71f +Subproject commit 45627ed3338011c64d1b328e12bdcac107a19280