add base user service
This commit is contained in:
parent
93d3f294d6
commit
f1fa9464a9
@ -17,3 +17,4 @@ export * from './lib/search';
|
||||
export * from './lib/empty-states';
|
||||
export * from './lib/scrollbar';
|
||||
export * from './lib/caching';
|
||||
export * from './lib/auth';
|
||||
|
||||
121
src/lib/auth/base-user.service.ts
Normal file
121
src/lib/auth/base-user.service.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import { BASE_HREF, List, mapEach, RequiredParam, Validate } from '../utils';
|
||||
import { QueryParam } from '../services';
|
||||
import { CacheApiService } from '../caching';
|
||||
import { EntitiesService } from '../listing';
|
||||
import { IqserUser } from './user.model';
|
||||
import { IIqserUser } from './types/user.response';
|
||||
import { ICreateUserRequest } from './types/create-user.request';
|
||||
import { IResetPasswordRequest } from './types/reset-password.request';
|
||||
import { IMyProfileUpdateRequest } from './types/my-profile-update.request';
|
||||
import { IProfileUpdateRequest } from './types/profile-update.request';
|
||||
import { KeycloakProfile } from 'keycloak-js';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BaseUserService<Interface extends IIqserUser, Class extends IqserUser & Interface> extends EntitiesService<
|
||||
Interface,
|
||||
Class
|
||||
> {
|
||||
readonly currentUser$: Observable<Class | undefined>;
|
||||
protected abstract readonly _defaultModelPath: string;
|
||||
protected abstract readonly _entityClass: new (entityInterface: Interface | KeycloakProfile, ...args: unknown[]) => Class;
|
||||
protected readonly _currentUser$ = new BehaviorSubject<Class | undefined>(undefined);
|
||||
protected readonly _baseHref = inject(BASE_HREF);
|
||||
protected readonly _keycloakService = inject(KeycloakService);
|
||||
protected readonly _cacheApiService = inject(CacheApiService);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.currentUser$ = this._currentUser$.asObservable();
|
||||
}
|
||||
|
||||
get currentUser(): Class | undefined {
|
||||
return this._currentUser$.value;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.loadCurrentUser();
|
||||
|
||||
console.log('Initializing users for: ', this.currentUser);
|
||||
if (!this.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
await firstValueFrom(this.loadAll());
|
||||
}
|
||||
|
||||
logout() {
|
||||
void this._cacheApiService.wipeCaches().then();
|
||||
void this._keycloakService.logout(window.location.origin + this._baseHref).then();
|
||||
}
|
||||
|
||||
loadAll() {
|
||||
return this.getAll().pipe(
|
||||
mapEach(user => new this._entityClass(user, user.roles, user.userId)),
|
||||
tap(users => this.setEntities(users)),
|
||||
);
|
||||
}
|
||||
|
||||
async loadCurrentUser(): Promise<Class> {
|
||||
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_'));
|
||||
const user = new this._entityClass(await this._keycloakService.loadUserProfile(true), roles, userId);
|
||||
this.replace(user);
|
||||
|
||||
this._currentUser$.next(this.find(userId));
|
||||
return user;
|
||||
}
|
||||
|
||||
getName(user: string | Interface | Class): string | undefined {
|
||||
const userId = typeof user === 'string' ? user : user.userId;
|
||||
return this.find(userId)?.name;
|
||||
}
|
||||
|
||||
getAll(): Observable<Interface[]> {
|
||||
return super.getAll(this._defaultModelPath, [{ key: 'refreshCache', value: true }]);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
updateProfile<T = IProfileUpdateRequest>(@RequiredParam() body: T, @RequiredParam() userId: string) {
|
||||
return this._post<unknown>(body, `${this._defaultModelPath}/profile/${userId}`);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
updateMyProfile<T = IMyProfileUpdateRequest>(@RequiredParam() body: T) {
|
||||
return this._post<unknown>(body, `${this._defaultModelPath}/my-profile`);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
resetPassword<T = IResetPasswordRequest>(@RequiredParam() body: T, @RequiredParam() userId: string) {
|
||||
return this._post<unknown>(body, `${this._defaultModelPath}/${userId}/reset-password`);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
create<T = ICreateUserRequest>(@RequiredParam() body: T) {
|
||||
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): Class | undefined {
|
||||
if (id?.toLowerCase() === 'system') {
|
||||
return new this._entityClass({ username: 'System' }, [], 'system');
|
||||
}
|
||||
|
||||
return super.find(id) || new this._entityClass({ username: 'Deleted User' }, [], 'deleted');
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentUser() {
|
||||
return inject(BaseUserService).currentUser;
|
||||
}
|
||||
10
src/lib/auth/default-user.service.ts
Normal file
10
src/lib/auth/default-user.service.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { IqserUser } from './user.model';
|
||||
import { IIqserUser } from './types/user.response';
|
||||
import { BaseUserService } from './base-user.service';
|
||||
|
||||
@Injectable()
|
||||
export class DefaultUserService extends BaseUserService<IIqserUser, IqserUser> {
|
||||
protected readonly _defaultModelPath = 'user';
|
||||
protected readonly _entityClass = IqserUser;
|
||||
}
|
||||
7
src/lib/auth/index.ts
Normal file
7
src/lib/auth/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './types/user.response';
|
||||
export * from './types/create-user.request';
|
||||
export * from './types/reset-password.request';
|
||||
export * from './types/my-profile-update.request';
|
||||
export * from './types/profile-update.request';
|
||||
export * from './user.model';
|
||||
export * from './base-user.service';
|
||||
8
src/lib/auth/types/auth-module-options.ts
Normal file
8
src/lib/auth/types/auth-module-options.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Type } from '@angular/core';
|
||||
import { BaseUserService } from '../base-user.service';
|
||||
import { IIqserUser } from './user.response';
|
||||
import { IqserUser } from '../user.model';
|
||||
|
||||
export interface AuthModuleOptions<I extends IIqserUser, C extends IqserUser & I, T extends BaseUserService<I, C> = BaseUserService<I, C>> {
|
||||
existingUserService: Type<T>;
|
||||
}
|
||||
8
src/lib/auth/types/create-user.request.ts
Normal file
8
src/lib/auth/types/create-user.request.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { List } from '../../utils';
|
||||
|
||||
export interface ICreateUserRequest {
|
||||
email: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
roles?: List;
|
||||
}
|
||||
5
src/lib/auth/types/my-profile-update.request.ts
Normal file
5
src/lib/auth/types/my-profile-update.request.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface IMyProfileUpdateRequest {
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
}
|
||||
8
src/lib/auth/types/profile-update.request.ts
Normal file
8
src/lib/auth/types/profile-update.request.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { List } from '../../utils';
|
||||
|
||||
export interface IProfileUpdateRequest {
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
roles?: List;
|
||||
}
|
||||
4
src/lib/auth/types/reset-password.request.ts
Normal file
4
src/lib/auth/types/reset-password.request.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IResetPasswordRequest {
|
||||
password: string;
|
||||
temporary: boolean;
|
||||
}
|
||||
10
src/lib/auth/types/user.response.ts
Normal file
10
src/lib/auth/types/user.response.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { List } from '../../utils';
|
||||
|
||||
export interface IIqserUser {
|
||||
readonly userId: string;
|
||||
readonly username: string;
|
||||
readonly roles?: List;
|
||||
readonly email?: string;
|
||||
readonly firstName?: string;
|
||||
readonly lastName?: string;
|
||||
}
|
||||
47
src/lib/auth/user.model.ts
Normal file
47
src/lib/auth/user.model.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { KeycloakProfile } from 'keycloak-js';
|
||||
import { IIqserUser } from './types/user.response';
|
||||
import { IListable } from '../listing';
|
||||
import { List } from '../utils';
|
||||
|
||||
export class IqserUser implements IIqserUser, IListable {
|
||||
readonly username: string;
|
||||
readonly email?: string;
|
||||
readonly firstName?: string;
|
||||
readonly lastName?: string;
|
||||
readonly name: string;
|
||||
readonly searchKey: string;
|
||||
|
||||
readonly hasAnyRole = this.roles.length > 0;
|
||||
|
||||
constructor(user: KeycloakProfile | IIqserUser, ...args: unknown[]);
|
||||
constructor(user: KeycloakProfile | IIqserUser, readonly roles: List, readonly userId: string) {
|
||||
this.email = user.email;
|
||||
this.username = user.username ?? this.email ?? 'unknown user';
|
||||
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 || ''}`;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
has(role: string): boolean {
|
||||
return this.roles.includes(role);
|
||||
}
|
||||
|
||||
hasAny(roles: List): boolean {
|
||||
if (roles?.length > 0) {
|
||||
for (const role of roles) {
|
||||
if (this.has(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -19,3 +19,4 @@ export * from './headers-configuration';
|
||||
export * from './context.component';
|
||||
export * from './tokens';
|
||||
export * from './base-app-config';
|
||||
export * from './types/common-ui-options';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user