133 lines
5.8 KiB
TypeScript
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],
|
|
},
|
|
],
|
|
};
|
|
}
|
|
}
|