From cfbe3cfd869bbaab880822cb2d37545e728ef9f6 Mon Sep 17 00:00:00 2001 From: Maurizio Vitale Date: Thu, 20 Sep 2018 22:17:05 +0100 Subject: [PATCH] [ADF-3570] ADF Migration - Add the interceptor layer (#3813) * Enable impliciFlow and add new interceptor * Fix errors * add excluded path rules * Add the needed methods to the AuthenticationService core * Remove unused library and service --- demo-shell/src/app/app.module.ts | 7 +- .../app/services/auth-bearer.interceptor.ts | 77 +++++++++++++++++++ demo-shell/src/app/services/index.ts | 18 +++++ lib/core/services/authentication.service.ts | 30 +++++++- package.json | 2 +- 5 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 demo-shell/src/app/services/auth-bearer.interceptor.ts create mode 100644 demo-shell/src/app/services/index.ts diff --git a/demo-shell/src/app/app.module.ts b/demo-shell/src/app/app.module.ts index e89cb748d0..ba149db97f 100644 --- a/demo-shell/src/app/app.module.ts +++ b/demo-shell/src/app/app.module.ts @@ -20,7 +20,7 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { FlexLayoutModule } from '@angular/flex-layout'; import { ChartsModule } from 'ng2-charts'; -import { HttpClientModule } from '@angular/common/http'; +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppConfigService, TRANSLATION_PROVIDER, DebugAppConfigService, CoreModule } from '@alfresco/adf-core'; @@ -79,6 +79,7 @@ import { MonacoEditorModule } from 'ngx-monaco-editor'; import { ContentModule } from '@alfresco/adf-content-services'; import { InsightsModule } from '@alfresco/adf-insights'; import { ProcessModule } from '@alfresco/adf-process-services'; +import { AuthBearerInterceptor } from './services'; @NgModule({ imports: [ @@ -144,6 +145,10 @@ import { ProcessModule } from '@alfresco/adf-process-services'; ConfigEditorComponent ], providers: [ + { + provide: HTTP_INTERCEPTORS, useClass: + AuthBearerInterceptor, multi: true + }, { provide: AppConfigService, useClass: DebugAppConfigService }, // not use this service in production { provide: TRANSLATION_PROVIDER, diff --git a/demo-shell/src/app/services/auth-bearer.interceptor.ts b/demo-shell/src/app/services/auth-bearer.interceptor.ts new file mode 100644 index 0000000000..6d70f4f30a --- /dev/null +++ b/demo-shell/src/app/services/auth-bearer.interceptor.ts @@ -0,0 +1,77 @@ +/*! + * @license + * Copyright 2018 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { throwError as observableThrowError, Observable } from 'rxjs'; +import { Injectable, Injector } from '@angular/core'; +import { + HttpHandler, HttpInterceptor, HttpRequest, + HttpSentEvent, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent +} from '@angular/common/http'; +import { AuthenticationService } from '@alfresco/adf-core'; +import { catchError, mergeMap } from 'rxjs/operators'; + +@Injectable() +export class AuthBearerInterceptor implements HttpInterceptor { + private excludedUrlsRegex: RegExp[]; + private authService: AuthenticationService; + + constructor(private injector: Injector) { } + + private loadExcludedUrlsRegex(): void { + const excludedUrls: string[] = this.authService.getBearerExcludedUrls(); + this.excludedUrlsRegex = excludedUrls.map(urlPattern => new RegExp(urlPattern, 'gi')) || []; + + } + + intercept(req: HttpRequest, next: HttpHandler): + Observable | HttpUserEvent> { + + this.authService = this.injector.get(AuthenticationService); + + if (!this.authService || !this.authService.getBearerExcludedUrls()) { + return next.handle(req); + } + + if (!this.excludedUrlsRegex) { + this.loadExcludedUrlsRegex(); + } + + const urlRequest = req.url; + const shallPass: boolean = !!this.excludedUrlsRegex.find(regex => regex.test(urlRequest)); + if (shallPass) { + return next.handle(req) + .pipe( + catchError(error => { + return observableThrowError(error); + }) + ); + } + + return this.authService.addTokenToHeader(req.headers) + .pipe( + mergeMap(headersWithBearer => { + const kcReq = req.clone({ headers: headersWithBearer }); + return next.handle(kcReq) + .pipe( + catchError(error => { + return observableThrowError(error); + }) + ); + }) + ); + } +} diff --git a/demo-shell/src/app/services/index.ts b/demo-shell/src/app/services/index.ts new file mode 100644 index 0000000000..f8c6265617 --- /dev/null +++ b/demo-shell/src/app/services/index.ts @@ -0,0 +1,18 @@ +/*! + * @license + * Copyright 2018 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './auth-bearer.interceptor'; diff --git a/lib/core/services/authentication.service.ts b/lib/core/services/authentication.service.ts index f7339e447c..91c9b84b9e 100644 --- a/lib/core/services/authentication.service.ts +++ b/lib/core/services/authentication.service.ts @@ -16,7 +16,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable, Subject, from, throwError } from 'rxjs'; +import { Observable, Subject, from, throwError, Observer } from 'rxjs'; import { AlfrescoApiService } from './alfresco-api.service'; import { CookieService } from './cookie.service'; import { LogService } from './log.service'; @@ -24,6 +24,7 @@ import { RedirectionModel } from '../models/redirection.model'; import { AppConfigService, AppConfigValues } from '../app-config/app-config.service'; import { UserRepresentation } from 'alfresco-js-api'; import { map, catchError, tap } from 'rxjs/operators'; +import { HttpHeaders } from '@angular/common/http'; const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME'; const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30 ; @@ -32,6 +33,8 @@ const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30 ; export class AuthenticationService { private redirectUrl: RedirectionModel = null; + private bearerExcludedUrls: string[] = ['auth/realms', 'resources/', 'assets/']; + onLogin: Subject = new Subject(); onLogout: Subject = new Subject(); @@ -271,4 +274,29 @@ export class AuthenticationService { this.logService.error('Error when logging in', error); return throwError(error || 'Server error'); } + + getBearerExcludedUrls(): string[] { + return this.bearerExcludedUrls; + } + + getToken(): string { + return localStorage.getItem('access_token'); + } + + addTokenToHeader(headersArg?: HttpHeaders): Observable { + return Observable.create(async (observer: Observer) => { + let headers = headersArg; + if (!headers) { + headers = new HttpHeaders(); + } + try { + const token: string = this.getToken(); + headers = headers.set('Authorization', 'bearer ' + token); + observer.next(headers); + observer.complete(); + } catch (error) { + observer.error(error); + } + }); + } } diff --git a/package.json b/package.json index 4770ed8601..fe8cb913d5 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "@nrwl/schematics": "^1.0.3", "@schematics/angular": "^0.6.8", "adf-tslint-rules": "0.0.4", - "alfresco-js-api": "^2.6.0-c87428564306f710159976b0d740a07a94326cf1", + "alfresco-js-api": "^2.6.0-1c0a85da20c8935876a0de499d6557f51cea1d78", "alfresco-js-api-node": "2.5.0", "chart.js": "2.5.0", "classlist.js": "1.1.20150312",