added role handling
This commit is contained in:
parent
31902a78b5
commit
e046c74da0
@ -1,58 +1,60 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {APP_INITIALIZER, NgModule} from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import {ActivatedRoute, ActivatedRouteSnapshot, Router, RouterModule} from '@angular/router';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
import { BaseScreenComponent } from './screens/base-screen/base-screen.component';
|
||||
import { ProjectListingScreenComponent } from './screens/project-listing-screen/project-listing-screen.component';
|
||||
import { ProjectOverviewScreenComponent } from './screens/project-overview-screen/project-overview-screen.component';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { ApiModule } from '@redaction/red-ui-http';
|
||||
import { ApiPathInterceptorService } from './interceptor/api-path-interceptor.service';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { languageInitializer } from './i18n/language.initializer';
|
||||
import { LanguageService } from './i18n/language.service';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { IconsModule } from './icons/icons.module';
|
||||
import { AddEditProjectDialogComponent } from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ConfirmationDialogComponent } from './common/confirmation-dialog/confirmation-dialog.component';
|
||||
import { FilePreviewScreenComponent } from './screens/file/file-preview-screen/file-preview-screen.component';
|
||||
import { PdfViewerComponent } from './screens/file/pdf-viewer/pdf-viewer.component';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||
import { NgpSortModule } from 'ngp-sort-pipe';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { FileDetailsDialogComponent } from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
import { ProjectDetailsDialogComponent } from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { FileUploadModule } from './upload/file-upload.module';
|
||||
import { FullPageLoadingIndicatorComponent } from './utils/full-page-loading-indicator/full-page-loading-indicator.component';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { InitialsAvatarComponent } from './common/initials-avatar/initials-avatar.component';
|
||||
import { StatusBarComponent } from './components/status-bar/status-bar.component';
|
||||
import { LogoComponent } from './logo/logo.component';
|
||||
import { CompositeRouteGuard } from './utils/composite-route.guard';
|
||||
import { AppStateGuard } from './state/app-state.guard';
|
||||
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { ManualRedactionDialogComponent } from './screens/file/manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
|
||||
import { AuthGuard } from "./auth/auth.guard";
|
||||
import {AppComponent} from './app.component';
|
||||
import {ActivatedRoute, Router, RouterModule} from '@angular/router';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http';
|
||||
import {BaseScreenComponent} from './screens/base-screen/base-screen.component';
|
||||
import {ProjectListingScreenComponent} from './screens/project-listing-screen/project-listing-screen.component';
|
||||
import {ProjectOverviewScreenComponent} from './screens/project-overview-screen/project-overview-screen.component';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {ApiModule} from '@redaction/red-ui-http';
|
||||
import {ApiPathInterceptorService} from './interceptor/api-path-interceptor.service';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
||||
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {languageInitializer} from './i18n/language.initializer';
|
||||
import {LanguageService} from './i18n/language.service';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {IconsModule} from './icons/icons.module';
|
||||
import {AddEditProjectDialogComponent} from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {ConfirmationDialogComponent} from './common/confirmation-dialog/confirmation-dialog.component';
|
||||
import {FilePreviewScreenComponent} from './screens/file/file-preview-screen/file-preview-screen.component';
|
||||
import {PdfViewerComponent} from './screens/file/pdf-viewer/pdf-viewer.component';
|
||||
import {MatTabsModule} from '@angular/material/tabs';
|
||||
import {MatButtonToggleModule} from '@angular/material/button-toggle';
|
||||
import {NgpSortModule} from 'ngp-sort-pipe';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {FileDetailsDialogComponent} from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component';
|
||||
import {ToastrModule} from 'ngx-toastr';
|
||||
import {ServiceWorkerModule} from '@angular/service-worker';
|
||||
import {environment} from '../environments/environment';
|
||||
import {ProjectDetailsDialogComponent} from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component';
|
||||
import {AuthModule} from './auth/auth.module';
|
||||
import {FileUploadModule} from './upload/file-upload.module';
|
||||
import {FullPageLoadingIndicatorComponent} from './utils/full-page-loading-indicator/full-page-loading-indicator.component';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {InitialsAvatarComponent} from './common/initials-avatar/initials-avatar.component';
|
||||
import {StatusBarComponent} from './components/status-bar/status-bar.component';
|
||||
import {LogoComponent} from './logo/logo.component';
|
||||
import {CompositeRouteGuard} from './utils/composite-route.guard';
|
||||
import {AppStateGuard} from './state/app-state.guard';
|
||||
import {SimpleDoughnutChartComponent} from './components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import {ManualRedactionDialogComponent} from './screens/file/manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import {AnnotationIconComponent} from './components/annotation-icon/annotation-icon.component';
|
||||
import {AuthGuard} from "./auth/auth.guard";
|
||||
import {AuthErrorComponent} from './screens/auth-error/auth-error.component';
|
||||
import {RedRoleGuard} from "./auth/red-role.guard";
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -77,6 +79,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
SimpleDoughnutChartComponent,
|
||||
ManualRedactionDialogComponent,
|
||||
AnnotationIconComponent,
|
||||
AuthErrorComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -101,6 +104,11 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
redirectTo: 'ui/projects',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'auth-error',
|
||||
component: AuthErrorComponent,
|
||||
canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'ui',
|
||||
component: BaseScreenComponent,
|
||||
@ -110,7 +118,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
component: ProjectListingScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, AppStateGuard]
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -118,7 +126,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
component: ProjectOverviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, AppStateGuard]
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -126,7 +134,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
component: FilePreviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, AppStateGuard]
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -148,7 +156,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
FileUploadModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||
ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}),
|
||||
MatProgressSpinnerModule,
|
||||
MatCheckboxModule
|
||||
],
|
||||
@ -166,9 +174,9 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
})
|
||||
export class AppModule {
|
||||
|
||||
constructor(private router: Router,private route: ActivatedRoute) {
|
||||
route.queryParamMap.subscribe(queryParams=>{
|
||||
if(queryParams.has('code') || queryParams.has('state') || queryParams.has('session_state')) {
|
||||
constructor(private router: Router, private route: ActivatedRoute) {
|
||||
route.queryParamMap.subscribe(queryParams => {
|
||||
if (queryParams.has('code') || queryParams.has('state') || queryParams.has('session_state')) {
|
||||
this.router.navigate([], {
|
||||
queryParams: {
|
||||
'state': null,
|
||||
|
||||
@ -2,6 +2,7 @@ import {Injectable} from "@angular/core";
|
||||
import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from "@angular/router";
|
||||
import {KeycloakAuthGuard, KeycloakService} from "keycloak-angular";
|
||||
import {UserService} from "../user/user.service";
|
||||
import {AppLoadStateService} from "../utils/app-load-state.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -10,6 +11,7 @@ export class AuthGuard extends KeycloakAuthGuard {
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _keycloak: KeycloakService,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _userService: UserService
|
||||
) {
|
||||
super(_router, _keycloak);
|
||||
@ -25,6 +27,7 @@ export class AuthGuard extends KeycloakAuthGuard {
|
||||
|
||||
await this._userService.loadCurrentUser();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
30
apps/red-ui/src/app/auth/red-role.guard.ts
Normal file
30
apps/red-ui/src/app/auth/red-role.guard.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from "@angular/router";
|
||||
import {KeycloakAuthGuard, KeycloakService} from "keycloak-angular";
|
||||
import {UserService} from "../user/user.service";
|
||||
import {AppLoadStateService} from "../utils/app-load-state.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RedRoleGuard extends KeycloakAuthGuard {
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _keycloak: KeycloakService,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _userService: UserService
|
||||
) {
|
||||
super(_router, _keycloak);
|
||||
}
|
||||
|
||||
public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
|
||||
if (!this._userService.user.hasAnyREDRoles) {
|
||||
this._router.navigate(['/auth-error']);
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
<section>
|
||||
<p class="heading-xl" translate="auth-error.heading.label"></p>
|
||||
<a (click)="logout()" translate="auth-error.logout.label"></a>
|
||||
</section>
|
||||
@ -0,0 +1,3 @@
|
||||
section {
|
||||
padding: 24px;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {UserService} from "../../user/user.service";
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-auth-error',
|
||||
templateUrl: './auth-error.component.html',
|
||||
styleUrls: ['./auth-error.component.scss']
|
||||
})
|
||||
export class AuthErrorComponent implements OnInit {
|
||||
|
||||
constructor(private readonly _userService: UserService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._userService.logout();
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ export class UserService {
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._keycloakService.logout();
|
||||
this._keycloakService.logout(window.location.origin);
|
||||
}
|
||||
|
||||
async loadCurrentUser() {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, mergeMap, tap } from 'rxjs/operators';
|
||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
|
||||
import {Injectable, Injector} from '@angular/core';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {catchError, mergeMap, tap} from 'rxjs/operators';
|
||||
import {AppLoadStateService} from "./app-load-state.service";
|
||||
|
||||
@Injectable({
|
||||
@ -10,40 +10,41 @@ import {AppLoadStateService} from "./app-load-state.service";
|
||||
export class CompositeRouteGuard implements CanActivate {
|
||||
|
||||
|
||||
constructor(protected router: Router, protected injector: Injector, private appLoadStateService: AppLoadStateService) {}
|
||||
constructor(protected readonly _router: Router, protected readonly _injector: Injector, private readonly _appLoadStateService: AppLoadStateService) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
this.appLoadStateService.pushLoadingEvent(true);
|
||||
let compositeCanActivateObservable: Observable<boolean> = of(true);
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
this._appLoadStateService.pushLoadingEvent(true);
|
||||
let compositeCanActivateObservable: Observable<boolean> = of(true);
|
||||
|
||||
const routeGuards = route.data.routeGuards;
|
||||
|
||||
if (routeGuards) {
|
||||
for (let i = 0; i < routeGuards.length; i++) {
|
||||
const routeGuard = this.injector.get(routeGuards[i]);
|
||||
const canActivateObservable: Observable<boolean> = routeGuard.canActivate(route, state);
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
mergeMap(bool => {
|
||||
if (!bool) {
|
||||
return of(false);
|
||||
} else {
|
||||
return canActivateObservable;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
const routeGuards = route.data.routeGuards;
|
||||
|
||||
if (routeGuards) {
|
||||
for (let i = 0; i < routeGuards.length; i++) {
|
||||
const routeGuard = this._injector.get(routeGuards[i]);
|
||||
const canActivateObservable: Observable<boolean> = routeGuard.canActivate(route, state);
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
tap(() => {
|
||||
this.appLoadStateService.pushLoadingEvent(false);
|
||||
}),
|
||||
catchError(() => {
|
||||
this.appLoadStateService.pushLoadingEvent(false);
|
||||
return of(false);
|
||||
})
|
||||
mergeMap(bool => {
|
||||
if (!bool) {
|
||||
return of(false);
|
||||
} else {
|
||||
return canActivateObservable;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return compositeCanActivateObservable;
|
||||
}
|
||||
}
|
||||
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
tap(() => {
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
}),
|
||||
catchError(() => {
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
return of(false);
|
||||
})
|
||||
);
|
||||
|
||||
return compositeCanActivateObservable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
{
|
||||
"auth-error": {
|
||||
"heading": {
|
||||
"label": "Your user doesn't have the required RED-* roles to access this application"
|
||||
},
|
||||
"logout": {
|
||||
"label": "Logout"
|
||||
}
|
||||
},
|
||||
"manual-redaction": {
|
||||
"remove-annotation": {
|
||||
"success": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user