RED-4718: use common-ui user related stuff

This commit is contained in:
Dan Percic 2022-07-27 12:08:27 +03:00
parent 164c527ce7
commit b57b600fc0
31 changed files with 108 additions and 368 deletions

View File

@ -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,

View File

@ -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),
},
],

View File

@ -52,7 +52,7 @@
<div class="center cell">
<mat-slide-toggle
(toggleChange)="toggleActive(user)"
[checked]="user.isActive"
[checked]="user.hasAnyRole"
[disabled]="!canDeactivate(user)"
color="primary"
></mat-slide-toggle>

View File

@ -109,7 +109,7 @@ export class UserListingScreenComponent extends ListingComponent<User> 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();
}

View File

@ -43,7 +43,7 @@ export class ConfigService {
userId =>
new NestedFilter({
id: userId,
label: this._userService.getNameForId(userId),
label: this._userService.getName(userId),
}),
);

View File

@ -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<void> {
return () =>
firstValueFrom(configService.loadLocalConfig()).then(() =>
keycloakService
.init(getKeycloakOptions(configService, baseUrl))
.then(() => configureAutomaticRedirectToLoginScreen(keycloakService)),
);
keycloakService
.init(getKeycloakOptions(configService, baseUrl))
.then(() => configureAutomaticRedirectToLoginScreen(keycloakService));
}
@NgModule({

View File

@ -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<boolean> {
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<boolean> {
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;
}
}

View File

@ -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 } });
}

View File

@ -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 = [

View File

@ -284,7 +284,7 @@ export class ConfigService {
peopleFilters.push(
new NestedFilter({
id: userId,
label: this._userService.getNameForId(userId),
label: this._userService.getName(userId),
}),
);
});

View File

@ -142,7 +142,7 @@ export class ConfigService {
userId =>
new NestedFilter({
id: userId,
label: this._userService.getNameForId(userId),
label: this._userService.getName(userId),
}),
);

View File

@ -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();

View File

@ -196,7 +196,7 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> 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',

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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<any> {
return this._httpClient.get<any>('/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<AppConfig> {}

View File

@ -71,7 +71,7 @@ export class TrashService extends EntitiesService<TrashItem, TrashItem> {
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<TrashItem, TrashItem> {
getFiles(dossierIds = this._activeDossiersService.all.map(d => d.id)): Observable<TrashFile[]> {
return this._post<Record<string, IFile[]>>(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<TrashItem, TrashItem> {
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,
);
}),

View File

@ -27,7 +27,7 @@ export class FilesService extends EntitiesService<IFile, File> {
/** 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<IFile, File> {
reload(dossierId: string, file: File): Observable<boolean> {
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')),

View File

@ -102,6 +102,6 @@ export class NotificationsService extends EntitiesService<INotification, Notific
}
private _getUsername(userId: string | undefined) {
return this._userService.getNameForId(userId) || this._translateService.instant(_('unknown'));
return this._userService.getName(userId) || this._translateService.instant(_('unknown'));
}
}

View File

@ -1,32 +1,13 @@
import { inject, Inject, Injectable } from '@angular/core';
import { KeycloakService } from 'keycloak-angular';
import jwt_decode from 'jwt-decode';
import { ICreateUserRequest, IMyProfileUpdateRequest, IProfileUpdateRequest, IResetPasswordRequest, IUser, User } from '@red/domain';
import { BASE_HREF, CacheApiService, EntitiesService, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { inject, Injectable } from '@angular/core';
import { User } from '@red/domain';
import { BaseUserService, IIqserUser } from '@iqser/common-ui';
@Injectable({
providedIn: 'root',
})
export class UserService extends EntitiesService<IUser, User> {
readonly currentUser$: Observable<User>;
export class UserService extends BaseUserService<IIqserUser, User> {
protected readonly _defaultModelPath = 'user';
protected readonly _entityClass = User;
readonly #currentUser$ = new BehaviorSubject<User>(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<IUser, User> {
return this.all.filter(user => user.isUser || user.isManager);
}
async initialize(): Promise<void> {
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<User> {
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<IUser[]> {
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<unknown>(body, `${this._defaultModelPath}/profile/${userId}`);
}
@Validate()
updateMyProfile(@RequiredParam() body: IMyProfileUpdateRequest) {
return this._post<unknown>(body, `${this._defaultModelPath}/my-profile`);
}
@Validate()
resetPassword(@RequiredParam() body: IResetPasswordRequest, @RequiredParam() userId: string) {
return this._post<unknown>(body, `${this._defaultModelPath}/${userId}/reset-password`);
}
@Validate()
create(@RequiredParam() body: ICreateUserRequest) {
return this._post(body);
}
delete(userIds: List) {
const queryParams = userIds.map<QueryParam>(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() {

View File

@ -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);
}

View File

@ -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,

View File

@ -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"

View File

@ -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;
}

View File

@ -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';

View File

@ -1,14 +0,0 @@
export interface IMyProfileUpdateRequest {
/**
* Email of user.
*/
email?: string;
/**
* First name of user.
*/
firstName?: string;
/**
* Last name of user.
*/
lastName?: string;
}

View File

@ -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;
}

View File

@ -1,4 +0,0 @@
export interface IResetPasswordRequest {
password?: string;
temporary?: boolean;
}

View File

@ -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);
}
}

View File

@ -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;
}