common-ui/src/lib/users/iqser-users.module.ts
2023-03-31 12:30:39 +03:00

133 lines
5.8 KiB
TypeScript

import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
import { KeycloakAngularModule, KeycloakEventType, KeycloakOptions, KeycloakService } from 'keycloak-angular';
import { DefaultUserService } from './services/default-user.service';
import { IIqserUser } from './types/user.response';
import { IqserUserService } from './services/iqser-user.service';
import { BASE_HREF, ModuleOptions } from '../utils';
import { IqserUsersModuleOptions } from './types/iqser-users-module-options';
import { IqserUser } from './iqser-user.model';
import { IqserRoleGuard } from './guards/iqser-role-guard.service';
import { IqserAuthGuard } from './guards/iqser-auth-guard.service';
import { IqserConfigService } from '../services';
import { NamePipe } from './name.pipe';
import { InitialsAvatarComponent } from './components/initials-avatar/initials-avatar.component';
import { MatTooltipModule } from '@angular/material/tooltip';
import { CommonModule } from '@angular/common';
import { UserButtonComponent } from './components/user-button/user-button.component';
import { MatIconModule } from '@angular/material/icon';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { TranslateModule } from '@ngx-translate/core';
import { TenantContext, TenantContextHolder } from '../tenants';
import { filter, firstValueFrom, of, switchMap } from 'rxjs';
import { KeycloakStatus, KeycloakStatusService } from './services/keycloak-status.service';
import { map, tap } from 'rxjs/operators';
function getKeycloakOptions(baseUrl: string, tenantContextHolder: TenantContextHolder, configService: IqserConfigService): KeycloakOptions {
console.log('keycloak config for: ', tenantContextHolder.currentTenant);
return {
config: {
url: configService.values.OAUTH_URL,
realm: tenantContextHolder.currentTenant,
clientId: configService.values.OAUTH_CLIENT_ID,
},
initOptions: {
checkLoginIframe: false,
onLoad: 'check-sso',
silentCheckSsoRedirectUri: window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html',
flow: 'standard',
},
enableBearerInterceptor: true,
};
}
function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakService, keycloakStatusService: KeycloakStatusService) {
keyCloakService.getKeycloakInstance().onAuthRefreshError = () => {
keycloakStatusService.createLoginUrlAndExecute();
};
}
export function keycloakInitializer(
keycloakService: KeycloakService,
configService: IqserConfigService,
baseUrl: string,
keycloakStatusService: KeycloakStatusService,
tenantContext: TenantContext,
tenantContextHolder: TenantContextHolder,
): () => Promise<boolean> {
const tenantsReady = tenantContext.tenantsReady$.pipe(
filter(t => t),
switchMap(() => {
if (tenantContextHolder.currentTenant) {
const x = keycloakService.init(getKeycloakOptions(baseUrl, tenantContextHolder, configService));
configureAutomaticRedirectToLoginScreen(keycloakService, keycloakStatusService);
keycloakStatusService.updateStatus(KeycloakStatus.PENDING);
return x;
} else {
keycloakStatusService.updateStatus(KeycloakStatus.NOT_ACTIVE);
return of(true);
}
}),
);
return () => firstValueFrom(tenantsReady);
}
export function keycloakStatusInitializer(keycloakService: KeycloakService, keycloakStatusService: KeycloakStatusService) {
const pipe = keycloakStatusService.keycloakStatus$.pipe(
filter(status => status === KeycloakStatus.PENDING || status === KeycloakStatus.NOT_ACTIVE),
switchMap(status => {
if (status === KeycloakStatus.NOT_ACTIVE) {
return of(true);
} else {
return keycloakService.keycloakEvents$.pipe(
filter(event => event.type === KeycloakEventType.OnReady),
tap(() => keycloakStatusService.updateStatus(KeycloakStatus.READY)),
map(() => true),
);
}
}),
);
return () => firstValueFrom(pipe);
}
const components = [NamePipe, InitialsAvatarComponent, UserButtonComponent];
@NgModule({
imports: [KeycloakAngularModule, MatTooltipModule, CommonModule, MatIconModule, MatButtonModule, TranslateModule],
declarations: [...components],
exports: [...components],
})
export class IqserUsersModule {
static forRoot<
Interface extends IIqserUser,
Class extends IqserUser & Interface,
UserService extends IqserUserService<Interface, Class>,
RolesGuard extends IqserRoleGuard = IqserRoleGuard,
>(options: IqserUsersModuleOptions<Interface, Class, UserService, RolesGuard>): ModuleWithProviders<IqserUsersModule> {
const userService = ModuleOptions.getService(IqserUserService, DefaultUserService, options.existingUserService);
const roleGuard = ModuleOptions.getService(IqserRoleGuard, IqserRoleGuard, options.existingRoleGuard);
return {
ngModule: IqserUsersModule,
providers: [
userService,
roleGuard,
IqserAuthGuard,
{
provide: APP_INITIALIZER,
useFactory: keycloakStatusInitializer,
multi: true,
deps: [KeycloakService, KeycloakStatusService],
},
{
provide: APP_INITIALIZER,
useFactory: keycloakInitializer,
multi: true,
deps: [KeycloakService, IqserConfigService, BASE_HREF, KeycloakStatusService, TenantContext, TenantContextHolder],
},
],
};
}
}