diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index ecb53a6cd..57c95f918 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -9,7 +9,7 @@ import { ApiPathInterceptor } from '@utils/api-path-interceptor'; import { MissingTranslationHandler, TranslateCompiler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { BASE_HREF, - BaseConfigService, + BaseUserService, CachingModule, CommonUiModule, HELP_DOCS, @@ -24,7 +24,6 @@ import { import { ToastrModule } from 'ngx-toastr'; import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '@environments/environment'; -import { AuthModule } from './modules/auth/auth.module'; import { AuthErrorComponent } from '@components/auth-error/auth-error.component'; import { NotificationsComponent } from '@components/notifications/notifications.component'; import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component'; @@ -60,6 +59,7 @@ import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module'; import { LicenseService } from '@services/license.service'; import { TenantIdInterceptor } from '@utils/tenant-id-interceptor'; import { UI_CACHES } from '@utils/constants'; +import { AuthModule } from './modules/auth/auth.module'; export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader { return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`); @@ -78,14 +78,19 @@ export const appModuleFactory = (config: AppConfig) => { SharedModule, FileUploadDownloadModule, HttpClientModule, - AuthModule, AppRoutingModule, MonacoEditorModule, IqserHelpModeModule, CommonUiModule.forRoot({ existingUserPreferenceService: UserPreferenceService, - configServiceFactory: () => new BaseConfigService(config), + configService: ConfigService, + configServiceFactory: () => new ConfigService(config), }), + AuthModule, + // AuthModule.forRoot({ + // existingUserService: UserService, + // existingRoleGuard: RedRoleGuard, + // }), CachingModule.forRoot(UI_CACHES), PdfViewerModule, ToastrModule.forRoot({ @@ -155,6 +160,10 @@ export const appModuleFactory = (config: AppConfig) => { provide: ErrorHandler, useClass: GlobalErrorHandler, }, + { + provide: BaseUserService, + useExisting: UserService, + }, { provide: HTTP_INTERCEPTORS, multi: true, diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts index 311bc4b13..3b329464a 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { AdminDialogService } from '../../../services/admin-dialog.service'; -import { BaseFormComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui'; +import { BaseFormComponent, IconButtonTypes, IProfileUpdateRequest, LoadingService, Toaster } from '@iqser/common-ui'; import { rolesTranslations } from '@translations/roles-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { IProfileUpdateRequest, User } from '@red/domain'; +import { User } from '@red/domain'; import { UserService } from '@services/user.service'; import { HttpStatusCode } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; @@ -53,7 +53,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges ...prev, [role]: [ { - value: this.user && this.user.hasRole(role), + value: this.user && this.user.has(role), disabled: this.shouldBeDisabled(role), }, ], diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html index 09310f37b..ae0c959fd 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html @@ -52,7 +52,7 @@
diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts index a0a3a9882..4a83f45f0 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts @@ -109,7 +109,7 @@ export class UserListingScreenComponent extends ListingComponent implement async toggleActive(user: User) { this._loadingService.start(); - const requestBody = { ...user, roles: user.isActive ? [] : ['RED_USER'] }; + const requestBody = { ...user, roles: user.hasAnyRole ? [] : ['RED_USER'] }; await firstValueFrom(this._userService.updateProfile(requestBody, user.id)); await this.#loadData(); } diff --git a/apps/red-ui/src/app/modules/archive/services/config.service.ts b/apps/red-ui/src/app/modules/archive/services/config.service.ts index 17ef28130..7d6c3bf93 100644 --- a/apps/red-ui/src/app/modules/archive/services/config.service.ts +++ b/apps/red-ui/src/app/modules/archive/services/config.service.ts @@ -43,7 +43,7 @@ export class ConfigService { userId => new NestedFilter({ id: userId, - label: this._userService.getNameForId(userId), + label: this._userService.getName(userId), }), ); diff --git a/apps/red-ui/src/app/modules/auth/auth.module.ts b/apps/red-ui/src/app/modules/auth/auth.module.ts index f1529e645..de2840206 100644 --- a/apps/red-ui/src/app/modules/auth/auth.module.ts +++ b/apps/red-ui/src/app/modules/auth/auth.module.ts @@ -5,7 +5,6 @@ import { HttpClientModule } from '@angular/common/http'; import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular'; import { ConfigService } from '@services/config.service'; import { BASE_HREF } from '@iqser/common-ui'; -import { firstValueFrom } from 'rxjs'; function getKeycloakOptions(configService: ConfigService, baseUrl: string) { let url: string = configService.values.OAUTH_URL; @@ -37,11 +36,9 @@ function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakServic export function keycloakInitializer(keycloakService: KeycloakService, configService: ConfigService, baseUrl: string): () => Promise { return () => - firstValueFrom(configService.loadLocalConfig()).then(() => - keycloakService - .init(getKeycloakOptions(configService, baseUrl)) - .then(() => configureAutomaticRedirectToLoginScreen(keycloakService)), - ); + keycloakService + .init(getKeycloakOptions(configService, baseUrl)) + .then(() => configureAutomaticRedirectToLoginScreen(keycloakService)); } @NgModule({ diff --git a/apps/red-ui/src/app/modules/auth/red-role.guard.ts b/apps/red-ui/src/app/modules/auth/red-role.guard.ts index e9f33c587..25a9bab19 100644 --- a/apps/red-ui/src/app/modules/auth/red-role.guard.ts +++ b/apps/red-ui/src/app/modules/auth/red-role.guard.ts @@ -1,71 +1,51 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { inject, Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { UserService } from '@services/user.service'; -import { LoadingService } from '@iqser/common-ui'; -import { Observable } from 'rxjs'; +import { RoleGuard } from '@iqser/common-ui'; @Injectable({ providedIn: 'root', }) -export class RedRoleGuard implements CanActivate { - constructor( - protected readonly _router: Router, - private readonly _loadingService: LoadingService, - private readonly _userService: UserService, - ) {} +export class RedRoleGuard extends RoleGuard { + protected readonly _userService = inject(UserService); - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return new Observable(obs => { - if (!this._userService.currentUser.hasAnyREDRoles) { - this._router.navigate(['/auth-error']); - this._loadingService.stop(); - obs.next(false); - obs.complete(); - return; + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + const currentUser = this._userService.currentUser; + + if (!currentUser.hasAnyREDRoles) { + await this._router.navigate(['/auth-error']); + this._loadingService.stop(); + return false; + } + + // we have at least 1 RED Role -> if it's not user he must be an admin + if (currentUser.isUserAdmin && !currentUser.isAdmin && state.url.includes('admin') && !state.url.includes('users')) { + await this._router.navigate(['/main/admin/users']); + return false; + } + + if ( + currentUser.isUserAdmin && + !currentUser.isAdmin && + !currentUser.isUser && + !(state.url.startsWith('/main/admin/users') || state.url.startsWith('/main/my-profile')) + ) { + await this._router.navigate(['/main/admin/users']); + return false; + } + + if (route.data.requiredRoles) { + if (currentUser.hasAny(route.data.requiredRoles)) { + return true; } - // we have at least 1 RED Role -> if it's not user he must be admin - - if ( - this._userService.currentUser.isUserAdmin && - !this._userService.currentUser.isAdmin && - state.url.includes('admin') && - !state.url.includes('users') - ) { - this._router.navigate(['/main/admin/users']); - obs.next(false); - obs.complete(); - return; - } - - if ( - this._userService.currentUser.isUserAdmin && - !this._userService.currentUser.isAdmin && - !this._userService.currentUser.isUser && - !(state.url.startsWith('/main/admin/users') || state.url.startsWith('/main/my-profile')) - ) { - this._router.navigate(['/main/admin/users']); - obs.next(false); - obs.complete(); - return; - } - - if (route.data.requiredRoles) { - if (this._userService.hasAnyRole(route.data.requiredRoles)) { - obs.next(true); - obs.complete(); - } else { - if (!this._userService.currentUser.isUser) { - this._router.navigate(['/main/admin']); - } else { - this._router.navigate(['/']); - } - obs.next(false); - obs.complete(); - } + if (!currentUser.isUser) { + await this._router.navigate(['/main/admin']); } else { - obs.next(true); - obs.complete(); + await this._router.navigate(['/']); } - }); + return false; + } + + return true; } } diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.ts index 9ee3d253a..c3a30c1e7 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.ts @@ -65,7 +65,7 @@ export class DossierDetailsComponent { const dossierRequest: IDossierRequest = { ...dossier, ownerId: owner.id }; await firstValueFrom(this._dossiersService.createOrUpdate(dossierRequest)); - const ownerName = this._userService.getNameForId(owner.id); + const ownerName = this._userService.getName(owner.id); const dossierName = dossier.dossierName; this._toaster.info(_('assignment.owner'), { params: { ownerName, dossierName } }); } diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/screen-header/dossier-overview-screen-header.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/screen-header/dossier-overview-screen-header.component.ts index e5970d6f1..1efc8b4d0 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/screen-header/dossier-overview-screen-header.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/screen-header/dossier-overview-screen-header.component.ts @@ -68,7 +68,7 @@ export class DossierOverviewScreenHeaderComponent implements OnInit { const fileName = this.dossier.dossierName + '.export.csv'; const mapper = (file?: File) => ({ ...file, - assignee: this._userService.getNameForId(file.assignee) || '-', + assignee: this._userService.getName(file.assignee) || '-', primaryAttribute: this._primaryFileAttributeService.getPrimaryFileAttributeValue(file, this.dossier.dossierTemplateId), }); const fileFields = [ diff --git a/apps/red-ui/src/app/modules/dossier-overview/config.service.ts b/apps/red-ui/src/app/modules/dossier-overview/config.service.ts index 253daad6b..2c9753ba6 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/config.service.ts @@ -284,7 +284,7 @@ export class ConfigService { peopleFilters.push( new NestedFilter({ id: userId, - label: this._userService.getNameForId(userId), + label: this._userService.getName(userId), }), ); }); diff --git a/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts b/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts index fd79b73b6..71d9024ef 100644 --- a/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts +++ b/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts @@ -142,7 +142,7 @@ export class ConfigService { userId => new NestedFilter({ id: userId, - label: this._userService.getNameForId(userId), + label: this._userService.getName(userId), }), ); diff --git a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts index ee4db7c40..c28bbbc97 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts @@ -97,7 +97,7 @@ export class UserManagementComponent { async assignReviewer(file: File, user: User | string) { const assigneeId = typeof user === 'string' ? user : user?.id; - const reviewerName = this.userService.getNameForId(assigneeId); + const reviewerName = this.userService.getName(assigneeId); const { dossierId, filename } = file; this.loadingService.start(); diff --git a/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts index c6091a4e6..42e6c84a0 100644 --- a/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts +++ b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts @@ -196,7 +196,7 @@ export class SearchScreenComponent extends ListingComponent imp const assignee = this._routeAssignee; const assigneeToFilter = (userId: string) => { const checked = assignee === userId; - return new NestedFilter({ id: userId, label: this._userService.getNameForId(userId), checked }); + return new NestedFilter({ id: userId, label: this._userService.getName(userId), checked }); }; const assigneeFilter: IFilterGroup = { slug: 'assignee', diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts index 138b56f2c..86658c4a9 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { UserService } from '@services/user.service'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Dossier, IDossierRequest } from '@red/domain'; @@ -135,7 +135,7 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On setMembersSelectOptions(value = this.searchQuery): void { this.membersSelectOptions = this.userService.eligibleUsers - .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(value.toLowerCase())) + .filter(user => this.userService.getName(user.id).toLowerCase().includes(value.toLowerCase())) .filter(user => this.selectedOwnerId !== user.id) .map(user => user.id); } diff --git a/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts b/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts index 6c5ca2331..fdce00d6e 100644 --- a/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts @@ -39,7 +39,7 @@ export class InitialsAvatarComponent implements OnInit, OnChanges { } get disabled(): boolean { - return this._user && !this._isSystemUser && !this._user.isActive; + return this._user && !this._isSystemUser && !this._user.hasAnyRole; } get isCurrentUser(): boolean { diff --git a/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts b/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts index fd4c06bb3..b89bb5058 100644 --- a/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts +++ b/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts @@ -54,6 +54,6 @@ export class NamePipe implements PipeTransform { if (!user) { return undefined; } - return typeof user === 'string' ? this._userService.getNameForId(user) : user.name; + return typeof user === 'string' ? this._userService.getName(user) : user.name; } } diff --git a/apps/red-ui/src/app/services/config.service.ts b/apps/red-ui/src/app/services/config.service.ts index d4b568ce6..1441ea740 100644 --- a/apps/red-ui/src/app/services/config.service.ts +++ b/apps/red-ui/src/app/services/config.service.ts @@ -1,55 +1,6 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Title } from '@angular/platform-browser'; -import packageInfo from '../../../../../package.json'; -import envConfig from '../../assets/config/config.json'; -import { CacheApiService, wipeAllCaches } from '@iqser/common-ui'; -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { BaseConfigService } from '@iqser/common-ui'; import { AppConfig } from '@red/domain'; -const version = packageInfo.version; - -@Injectable({ - providedIn: 'root', -}) -export class ConfigService { - constructor( - private readonly _httpClient: HttpClient, - private readonly _cacheApiService: CacheApiService, - private readonly _titleService: Title, - ) { - this._checkFrontendVersion(); - } - - private _values: AppConfig = { ...envConfig, FRONTEND_APP_VERSION: version } as const; - - get values() { - return this._values; - } - - loadLocalConfig(): Observable { - return this._httpClient.get('/assets/config/config.json').pipe( - tap(config => { - console.log('[REDACTION] Started with local config: ', config); - this._values = config; - }), - ); - } - - updateDisplayName(name: string): void { - this._values = { ...this._values, APP_NAME: name } as const; - this._titleService.setTitle(this._values.APP_NAME || 'RedactManager'); - } - - private _checkFrontendVersion(): void { - this._cacheApiService.getCachedValue('FRONTEND_APP_VERSION').then(async lastVersion => { - console.log('[REDACTION] Last app version: ', lastVersion, ' current version ', version); - if (lastVersion !== version) { - console.warn('[REDACTION] Version-mismatch - wiping caches!'); - await wipeAllCaches(); - } - await this._cacheApiService.cacheValue('FRONTEND_APP_VERSION', version); - }); - } -} +@Injectable() +export class ConfigService extends BaseConfigService {} diff --git a/apps/red-ui/src/app/services/entity-services/trash.service.ts b/apps/red-ui/src/app/services/entity-services/trash.service.ts index f2379985a..d795b8806 100644 --- a/apps/red-ui/src/app/services/entity-services/trash.service.ts +++ b/apps/red-ui/src/app/services/entity-services/trash.service.ts @@ -71,7 +71,7 @@ export class TrashService extends EntitiesService { this._systemPreferencesService.values.softDeleteCleanupTime, this._permissionsService.canRestoreDossier(dossier), this._permissionsService.canHardDeleteDossier(dossier), - this._userService.getNameForId(dossier.ownerId), + this._userService.getName(dossier.ownerId), ), ), switchMap(dossiers => this._dossierStatsService.getFor(dossiers.map(d => d.id)).pipe(map(() => dossiers))), @@ -81,7 +81,7 @@ export class TrashService extends EntitiesService { getFiles(dossierIds = this._activeDossiersService.all.map(d => d.id)): Observable { return this._post>(dossierIds, 'status/softdeleted').pipe( map(res => flatMap(Object.values(res))), - mapEach(file => new File(file, this._userService.getNameForId(file.assignee))), + mapEach(file => new File(file, this._userService.getName(file.assignee))), mapEach(file => { const dossier = this._activeDossiersService.find(file.dossierId); return new TrashFile( @@ -90,7 +90,7 @@ export class TrashService extends EntitiesService { this._systemPreferencesService.values.softDeleteCleanupTime, this._permissionsService.canRestoreFile(file, dossier), this._permissionsService.canHardDeleteFile(file, dossier), - this._userService.getNameForId(file.assignee), + this._userService.getName(file.assignee), dossier.dossierName, ); }), diff --git a/apps/red-ui/src/app/services/files/files.service.ts b/apps/red-ui/src/app/services/files/files.service.ts index e00ab4c92..323ccee30 100644 --- a/apps/red-ui/src/app/services/files/files.service.ts +++ b/apps/red-ui/src/app/services/files/files.service.ts @@ -27,7 +27,7 @@ export class FilesService extends EntitiesService { /** Reload dossier files + stats. */ loadAll(dossierId: string) { const files$ = this.getFor(dossierId).pipe( - mapEach(file => new File(file, this._userService.getNameForId(file.assignee))), + mapEach(file => new File(file, this._userService.getName(file.assignee))), tap(() => this._logger.info('[FILE] Loaded')), ); const loadStats$ = files$.pipe(switchMap(files => this._dossierStatsService.getFor([dossierId]).pipe(map(() => files)))); @@ -36,7 +36,7 @@ export class FilesService extends EntitiesService { reload(dossierId: string, file: File): Observable { return super._getOne([dossierId, file.id]).pipe( - map(_file => new File(_file, this._userService.getNameForId(_file.assignee))), + map(_file => new File(_file, this._userService.getName(_file.assignee))), switchMap(_file => this._dossierStatsService.getFor([dossierId]).pipe(map(() => _file))), map(_file => this._filesMapService.replace([_file])), tap(() => this._logger.info('[FILE] Reloaded')), diff --git a/apps/red-ui/src/app/services/notifications.service.ts b/apps/red-ui/src/app/services/notifications.service.ts index bea947d0d..922a256b6 100644 --- a/apps/red-ui/src/app/services/notifications.service.ts +++ b/apps/red-ui/src/app/services/notifications.service.ts @@ -102,6 +102,6 @@ export class NotificationsService extends EntitiesService { - readonly currentUser$: Observable; +export class UserService extends BaseUserService { protected readonly _defaultModelPath = 'user'; protected readonly _entityClass = User; - readonly #currentUser$ = new BehaviorSubject(null); - - constructor( - @Inject(BASE_HREF) private readonly _baseHref: string, - private readonly _keycloakService: KeycloakService, - private readonly _cacheApiService: CacheApiService, - ) { - super(); - this.currentUser$ = this.#currentUser$.asObservable(); - } - - get currentUser(): User { - return this.#currentUser$.value; - } get managerUsers(): User[] { return this.all.filter(user => user.isManager); @@ -36,109 +17,18 @@ export class UserService extends EntitiesService { return this.all.filter(user => user.isUser || user.isManager); } - async initialize(): Promise { - console.log('[Redaction] Initializing users for: ', this.currentUser); - if (!this.currentUser) { - return; - } - - if (this.currentUser.isUserAdmin || this.currentUser.isUser || this.currentUser.isAdmin) { - await firstValueFrom(this.loadAll()); - } - } - - logout() { - this._cacheApiService.wipeCaches().then(); - this._keycloakService.logout().then(); - } - loadAll() { - const all$ = this.currentUser.isUserAdmin ? this.getUsers() : this.getUsers(true); - - return all$.pipe( - mapEach(user => new User(user, user.roles, user.userId)), - tap(users => this.setEntities(users)), - ); - } - - async loadCurrentUser(): Promise { - 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 => role.startsWith('RED_')); - let profile; - try { - profile = await this._keycloakService.loadUserProfile(true); - } catch (e) { - await this._keycloakService.logout(); - console.log(e); + if (this.currentUser.isUserAdmin || this.currentUser.isUser || this.currentUser.isAdmin) { + return super.loadAll(); } - - const user = new User(profile, roles, userId); - this.replace(user); - - this.#currentUser$.next(this.find(userId)); - return user; } - getNameForId(userId: string): string | undefined { - return this.find(userId)?.name; + getAll() { + const url = this.currentUser.isUserAdmin ? this._defaultModelPath : `${this._defaultModelPath}/red`; + return super.getAll(url); } - hasAnyRole(requiredRoles: string[], user = this.currentUser): boolean { - if (requiredRoles?.length > 0) { - for (const role of requiredRoles) { - if (user.hasRole(role)) { - return true; - } - } - - return false; - } - - return true; - } - - getUsers(onlyRed = false, refreshCache = true): Observable { - const url = onlyRed ? `${this._defaultModelPath}/red` : this._defaultModelPath; - return super.getAll(url, [{ key: 'refreshCache', value: refreshCache }]); - } - - @Validate() - updateProfile(@RequiredParam() body: IProfileUpdateRequest, @RequiredParam() userId: string) { - return this._post(body, `${this._defaultModelPath}/profile/${userId}`); - } - - @Validate() - updateMyProfile(@RequiredParam() body: IMyProfileUpdateRequest) { - return this._post(body, `${this._defaultModelPath}/my-profile`); - } - - @Validate() - resetPassword(@RequiredParam() body: IResetPasswordRequest, @RequiredParam() userId: string) { - return this._post(body, `${this._defaultModelPath}/${userId}/reset-password`); - } - - @Validate() - create(@RequiredParam() body: ICreateUserRequest) { - return this._post(body); - } - - delete(userIds: List) { - const queryParams = userIds.map(userId => ({ key: 'userId', value: userId })); - return super.delete(userIds, this._defaultModelPath, queryParams); - } - - find(id: string): User | undefined { - if (id?.toLowerCase() === 'system') { - return new User({ username: 'System' }, [], 'system'); - } - if (!id) { - return undefined; - } - return super.find(id) || new User({ username: 'Deleted User' }, [], 'deleted'); - } + protected readonly _rolesFilter = (role: string) => role.startsWith('RED_'); } export function getCurrentUser() { diff --git a/apps/red-ui/src/app/utils/configuration.initializer.ts b/apps/red-ui/src/app/utils/configuration.initializer.ts index 202206cfd..3e409c8c3 100644 --- a/apps/red-ui/src/app/utils/configuration.initializer.ts +++ b/apps/red-ui/src/app/utils/configuration.initializer.ts @@ -1,7 +1,7 @@ -import { catchError, filter, mergeMap, switchMap, take, tap } from 'rxjs/operators'; +import { catchError, filter, mergeMap, switchMap, tap } from 'rxjs/operators'; import { ConfigService } from '@services/config.service'; import { Title } from '@angular/platform-browser'; -import { firstValueFrom, from, map, of, throwError } from 'rxjs'; +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'; @@ -35,10 +35,10 @@ export function configurationInitializer( const setup = keycloakService.keycloakEvents$.pipe( filter(event => event.type === KeycloakEventType.OnReady), map(() => featuresService.loadConfig()), - switchMap(() => from(keycloakService.isLoggedIn())), - switchMap(loggedIn => (!loggedIn ? throwError('Not Logged In') : of({}))), - switchMap(() => from(userService.loadCurrentUser())), - switchMap(user => (!user.hasAnyREDRoles ? throwError('Not user has no red roles') : of({}))), + 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({}))), mergeMap(() => generalSettingsService.getGeneralConfigurations()), tap(configuration => configService.updateDisplayName(configuration.displayName)), switchMap(() => systemPreferencesService.loadPreferences()), @@ -52,9 +52,7 @@ export function configurationInitializer( lastDossierTemplateRedirect(baseHref.replace('/', ''), userPreferenceService); }), switchMap(() => languageService.setInitialLanguage()), - tap(() => userService.initialize()), switchMap(() => licenseService.loadLicense()), - take(1), ); return () => firstValueFrom(setup); } diff --git a/apps/red-ui/src/app/utils/filter-utils.ts b/apps/red-ui/src/app/utils/filter-utils.ts index 9ab679689..a766e99c0 100644 --- a/apps/red-ui/src/app/utils/filter-utils.ts +++ b/apps/red-ui/src/app/utils/filter-utils.ts @@ -83,7 +83,7 @@ export const dossierStateChecker = (dw: Dossier, filter: INestedFilter) => export const dossierApproverChecker = (dw: Dossier, filter: INestedFilter) => dw.approverIds.includes(filter.id); export const userTypeFilters: { [key in UserType]: (user: User) => boolean } = { - INACTIVE: (user: User) => !user.isActive, + INACTIVE: (user: User) => !user.hasAnyRole, REGULAR: (user: User) => user.roles.length === 1 && user.roles[0] === 'RED_USER', RED_MANAGER: (user: User) => user.isManager && !user.isAdmin, MANAGER_ADMIN: (user: User) => user.isManager && user.isAdmin, diff --git a/apps/red-ui/src/assets/config/config.json b/apps/red-ui/src/assets/config/config.json index 8e7d077a8..0c71adeeb 100644 --- a/apps/red-ui/src/assets/config/config.json +++ b/apps/red-ui/src/assets/config/config.json @@ -1,7 +1,7 @@ { "ADMIN_CONTACT_NAME": null, "ADMIN_CONTACT_URL": null, - "API_URL": "https://dev-05.iqser.cloud/redaction-gateway-v1", + "API_URL": "https://dev-04.iqser.cloud/redaction-gateway-v1", "APP_NAME": "RedactManager", "AUTO_READ_TIME": 3, "BACKEND_APP_VERSION": "4.4.40", @@ -11,7 +11,7 @@ "MAX_RETRIES_ON_SERVER_ERROR": 3, "OAUTH_CLIENT_ID": "redaction", "OAUTH_IDP_HINT": null, - "OAUTH_URL": "https://dev-05.iqser.cloud/auth/realms/redaction", + "OAUTH_URL": "https://dev-04.iqser.cloud/auth/realms/redaction", "RECENT_PERIOD_IN_HOURS": 24, "SELECTION_MODE": "structural", "MANUAL_BASE_URL": "https://docs.redactmanager.com/preview" diff --git a/libs/red-domain/src/lib/users/create-user.request.ts b/libs/red-domain/src/lib/users/create-user.request.ts deleted file mode 100644 index 8faaea329..000000000 --- a/libs/red-domain/src/lib/users/create-user.request.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { List } from '@iqser/common-ui'; - -export interface ICreateUserRequest { - /** - * Email of user. - */ - email?: string; - /** - * First name of user. - */ - firstName?: string; - /** - * Last name of user. - */ - lastName?: string; - /** - * The list of RED_* roles. - */ - roles?: List; -} diff --git a/libs/red-domain/src/lib/users/index.ts b/libs/red-domain/src/lib/users/index.ts index 1425c58fe..b66d5cb35 100644 --- a/libs/red-domain/src/lib/users/index.ts +++ b/libs/red-domain/src/lib/users/index.ts @@ -1,8 +1,3 @@ export * from './user.model'; -export * from './user'; -export * from './my-profile-update.request'; -export * from './profile-update.request'; export * from './profile'; export * from './types'; -export * from './create-user.request'; -export * from './reset-password.request'; diff --git a/libs/red-domain/src/lib/users/my-profile-update.request.ts b/libs/red-domain/src/lib/users/my-profile-update.request.ts deleted file mode 100644 index e29d1d09f..000000000 --- a/libs/red-domain/src/lib/users/my-profile-update.request.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface IMyProfileUpdateRequest { - /** - * Email of user. - */ - email?: string; - /** - * First name of user. - */ - firstName?: string; - /** - * Last name of user. - */ - lastName?: string; -} diff --git a/libs/red-domain/src/lib/users/profile-update.request.ts b/libs/red-domain/src/lib/users/profile-update.request.ts deleted file mode 100644 index 778db3489..000000000 --- a/libs/red-domain/src/lib/users/profile-update.request.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IMyProfileUpdateRequest } from './my-profile-update.request'; -import { List } from '@iqser/common-ui'; - -export interface IProfileUpdateRequest extends IMyProfileUpdateRequest { - /** - * Roles. - */ - roles?: List; -} diff --git a/libs/red-domain/src/lib/users/reset-password.request.ts b/libs/red-domain/src/lib/users/reset-password.request.ts deleted file mode 100644 index 1222768c1..000000000 --- a/libs/red-domain/src/lib/users/reset-password.request.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IResetPasswordRequest { - password?: string; - temporary?: boolean; -} diff --git a/libs/red-domain/src/lib/users/user.model.ts b/libs/red-domain/src/lib/users/user.model.ts index e4c322bd1..d60bec90e 100644 --- a/libs/red-domain/src/lib/users/user.model.ts +++ b/libs/red-domain/src/lib/users/user.model.ts @@ -1,32 +1,14 @@ -import { IListable, List } from '@iqser/common-ui'; +import { IIqserUser, IqserUser, List } from '@iqser/common-ui'; import { KeycloakProfile } from 'keycloak-js'; -import { IUser } from './user'; -export class User implements IUser, IListable { - readonly email?: string; - readonly username?: string; - readonly firstName?: string; - readonly lastName?: string; - readonly name?: string; - readonly searchKey: string; - - readonly isActive = this.roles.length > 0; - readonly isManager = this.hasRole('RED_MANAGER'); - readonly isUserAdmin = this.hasRole('RED_USER_ADMIN'); - readonly isUser = this.hasRole('RED_USER'); - readonly isAdmin = this.hasRole('RED_ADMIN'); +export class User extends IqserUser { + readonly isManager = this.has('RED_MANAGER'); + readonly isUserAdmin = this.has('RED_USER_ADMIN'); + readonly isUser = this.has('RED_USER'); + readonly isAdmin = this.has('RED_ADMIN'); readonly hasAnyREDRoles = this.isUser || this.isManager || this.isAdmin || this.isUserAdmin; - constructor(user: KeycloakProfile | IUser, readonly roles: List, readonly id: string) { - this.email = user.email; - this.username = user.username || this.email; - this.firstName = user.firstName; - this.lastName = user.lastName; - this.name = this.firstName && this.lastName ? `${this.firstName} ${this.lastName}` : this.username; - this.searchKey = `${this.name || '-'}${this.username || '-'}${this.email || ''}`; - } - - hasRole(role: string): boolean { - return this.roles.indexOf(role) >= 0; + constructor(user: KeycloakProfile | IIqserUser, readonly roles: List, readonly userId: string) { + super(user, roles, userId); } } diff --git a/libs/red-domain/src/lib/users/user.ts b/libs/red-domain/src/lib/users/user.ts deleted file mode 100644 index 310383a93..000000000 --- a/libs/red-domain/src/lib/users/user.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Object containing information of user and roles. - */ -import { IProfileUpdateRequest } from './profile-update.request'; - -export interface IUser extends IProfileUpdateRequest { - /** - * Id of user. - */ - readonly userId?: string; - /** - * Username for login. - */ - readonly username?: string; -}