From 70d95b47648e0c8d6f4618e95b3f716a954f7122 Mon Sep 17 00:00:00 2001 From: Julius Gruber Date: Mon, 3 Jun 2019 09:57:52 +0200 Subject: [PATCH] Changed auth-interceptor: location used to makeAuthStatusObject() --- src/app/core/auth/auth.actions.ts | 1 + src/app/core/auth/auth.effects.ts | 15 +++- src/app/core/auth/auth.interceptor.ts | 6 +- src/app/core/auth/auth.service.ts | 100 +++++++++++++++++--------- src/config/global-config.interface.ts | 2 + 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/src/app/core/auth/auth.actions.ts b/src/app/core/auth/auth.actions.ts index f54ceea686..c2ab855aad 100644 --- a/src/app/core/auth/auth.actions.ts +++ b/src/app/core/auth/auth.actions.ts @@ -311,6 +311,7 @@ export class ResetAuthenticationMessagesAction implements Action { public type: string = AuthActionTypes.RESET_MESSAGES; } +// Next three Actions are used by shibboleth login /** * Check if token is already present upon initial load. * @class CheckAuthenticationTokenAction diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index 1e68802af8..fa314b5bc2 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -26,7 +26,7 @@ import { RefreshTokenSuccessAction, RegistrationAction, RegistrationErrorAction, - RegistrationSuccessAction + RegistrationSuccessAction, RetrieveAuthMethodsErrorAction, RetrieveAuthMethodsSuccessAction } from './auth.actions'; import { EPerson } from '../eperson/models/eperson.model'; import { AuthStatus } from './models/auth-status.model'; @@ -169,6 +169,19 @@ export class AuthEffects { tap(() => this.authService.redirectToLoginWhenTokenExpired()) ); + @Effect() + public retrieveMethods$: Observable = this.actions$ + .pipe( + ofType(AuthActionTypes.RETRIEVE_AUTH_METHODS), + switchMap(() => { + return this.authService.retrieveAuthMethods() + .pipe( + map((location: any) => new RetrieveAuthMethodsSuccessAction(location)), + catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction())) + ) + }) + ) + /** * @constructor * @param {Actions} actions$ diff --git a/src/app/core/auth/auth.interceptor.ts b/src/app/core/auth/auth.interceptor.ts index da760b8faa..958491f601 100644 --- a/src/app/core/auth/auth.interceptor.ts +++ b/src/app/core/auth/auth.interceptor.ts @@ -56,10 +56,11 @@ export class AuthInterceptor implements HttpInterceptor { return http.url && http.url.endsWith('/authn/logout'); } - private makeAuthStatusObject(authenticated:boolean, accessToken?: string, error?: string): AuthStatus { + private makeAuthStatusObject(authenticated:boolean, accessToken?: string, error?: string, location?): AuthStatus { const authStatus = new AuthStatus(); authStatus.id = null; authStatus.okay = true; + authStatus.ssoLoginUrl = location; // this line added while developing shibboleth dev if (authenticated) { authStatus.authenticated = true; authStatus.token = new AuthTokenInfo(accessToken); @@ -133,9 +134,10 @@ export class AuthInterceptor implements HttpInterceptor { if (this.isAuthRequest(error)) { // clean eventually refresh Requests list this.refreshTokenRequestUrls = []; + const location = error.headers.get('Location');// this line added while shibboleth dev // Create a new HttpResponse and return it, so it can be handle properly by AuthService. const authResponse = new HttpResponse({ - body: this.makeAuthStatusObject(false, null, error.error), + body: this.makeAuthStatusObject(false, null, error.error, location), headers: error.headers, status: error.status, statusText: error.statusText, diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index a01768e687..c5941ebace 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -1,27 +1,29 @@ -import { Inject, Injectable, Optional } from '@angular/core'; -import { PRIMARY_OUTLET, Router, UrlSegmentGroup, UrlTree } from '@angular/router'; -import { HttpHeaders } from '@angular/common/http'; -import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; +import {Inject, Injectable, Optional} from '@angular/core'; +import {PRIMARY_OUTLET, Router, UrlSegmentGroup, UrlTree} from '@angular/router'; +import {HttpHeaders} from '@angular/common/http'; +import {REQUEST, RESPONSE} from '@nguniversal/express-engine/tokens'; -import { Observable, of as observableOf } from 'rxjs'; -import { distinctUntilChanged, filter, map, startWith, switchMap, take, withLatestFrom } from 'rxjs/operators'; -import { RouterReducerState } from '@ngrx/router-store'; -import { select, Store } from '@ngrx/store'; -import { CookieAttributes } from 'js-cookie'; +import {Observable, of as observableOf} from 'rxjs'; +import {distinctUntilChanged, filter, map, startWith, switchMap, take, withLatestFrom} from 'rxjs/operators'; +import {RouterReducerState} from '@ngrx/router-store'; +import {select, Store} from '@ngrx/store'; +import {CookieAttributes} from 'js-cookie'; -import { EPerson } from '../eperson/models/eperson.model'; -import { AuthRequestService } from './auth-request.service'; -import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; -import { AuthStatus } from './models/auth-status.model'; -import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model'; -import { isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util'; -import { CookieService } from '../../shared/services/cookie.service'; -import { getAuthenticationToken, getRedirectUrl, isAuthenticated, isTokenRefreshing } from './selectors'; -import { AppState, routerStateSelector } from '../../app.reducer'; -import { ResetAuthenticationMessagesAction, SetRedirectUrlAction } from './auth.actions'; -import { NativeWindowRef, NativeWindowService } from '../../shared/services/window.service'; -import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util'; -import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import {EPerson} from '../eperson/models/eperson.model'; +import {AuthRequestService} from './auth-request.service'; +import {HttpOptions} from '../dspace-rest-v2/dspace-rest-v2.service'; +import {AuthStatus} from './models/auth-status.model'; +import {AuthTokenInfo, TOKENITEM} from './models/auth-token-info.model'; +import {isEmpty, isNotEmpty, isNotNull, isNotUndefined} from '../../shared/empty.util'; +import {CookieService} from '../../shared/services/cookie.service'; +import {getAuthenticationToken, getRedirectUrl, isAuthenticated, isTokenRefreshing} from './selectors'; +import {AppState, routerStateSelector} from '../../app.reducer'; +import {ResetAuthenticationMessagesAction, SetRedirectUrlAction} from './auth.actions'; +import {NativeWindowRef, NativeWindowService} from '../../shared/services/window.service'; +import {Base64EncodeUrl} from '../../shared/utils/encode-decode.util'; +import {RemoteDataBuildService} from '../cache/builders/remote-data-build.service'; +import {GlobalConfig} from '../../../config/global-config.interface'; +import {GLOBAL_CONFIG} from '../../../config'; export const LOGIN_ROUTE = '/login'; export const LOGOUT_ROUTE = '/logout'; @@ -40,14 +42,16 @@ export class AuthService { */ protected _authenticated: boolean; - constructor(@Inject(REQUEST) protected req: any, - @Inject(NativeWindowService) protected _window: NativeWindowRef, - protected authRequestService: AuthRequestService, - @Optional() @Inject(RESPONSE) private response: any, - protected router: Router, - protected storage: CookieService, - protected store: Store, - protected rdbService: RemoteDataBuildService + constructor( + @Inject(GLOBAL_CONFIG) public config: GlobalConfig, + @Inject(REQUEST) protected req: any, + @Inject(NativeWindowService) protected _window: NativeWindowRef, + protected authRequestService: AuthRequestService, + @Optional() @Inject(RESPONSE) private response: any, + protected router: Router, + protected storage: CookieService, + protected store: Store, + protected rdbService: RemoteDataBuildService ) { this.store.pipe( select(isAuthenticated), @@ -193,6 +197,36 @@ export class AuthService { this.store.dispatch(new ResetAuthenticationMessagesAction()); } + /** + * Retrieve authentication methods available + * @returns {User} + */ + public retrieveAuthMethods(): Observable { + return this.authRequestService.getRequest('login').pipe( + map((status: AuthStatus) => { + let url = ''; + if (isNotEmpty(status.ssoLoginUrl)) { + url = this.parseSSOLocation(status.ssoLoginUrl); + } + return url; + }) + ) + } + + private parseSSOLocation(url: string): string { + const parseUrl = decodeURIComponent(url); + // const urlTree: UrlTree = this.router.parseUrl(url); + // this.router.parseUrl(url); + // if (url.endsWith('/')) { + // url += 'login'; + // } else { + // url = url.replace('/?target=http(.+)/g', 'https://hasselt-dspace.dev01.4science.it/dspace-spring-rest/shib.html'); + // } + // console.log(url); + const target = `?target=${this.config.auth.target.host}${this.config.auth.target.page}`; + return parseUrl.replace(/\?target=http.+/g, target); + } + /** * Create a new user * @returns {User} @@ -213,7 +247,7 @@ export class AuthService { // Send a request that sign end the session let headers = new HttpHeaders(); headers = headers.append('Content-Type', 'application/x-www-form-urlencoded'); - const options: HttpOptions = Object.create({ headers, responseType: 'text' }); + const options: HttpOptions = Object.create({headers, responseType: 'text'}); return this.authRequestService.getRequest('logout', options).pipe( map((status: AuthStatus) => { if (!status.authenticated) { @@ -289,7 +323,7 @@ export class AuthService { // Set the cookie expire date const expires = new Date(expireDate); - const options: CookieAttributes = { expires: expires }; + const options: CookieAttributes = {expires: expires}; // Save cookie with the token return this.storage.set(TOKENITEM, token, options); @@ -388,7 +422,7 @@ export class AuthService { // Set the cookie expire date const expires = new Date(expireDate); - const options: CookieAttributes = { expires: expires }; + const options: CookieAttributes = {expires: expires}; this.storage.set(REDIRECT_COOKIE, url, options); this.store.dispatch(new SetRedirectUrlAction(isNotUndefined(url) ? url : '')); } diff --git a/src/config/global-config.interface.ts b/src/config/global-config.interface.ts index 22b4b0500f..938b5be984 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/global-config.interface.ts @@ -9,12 +9,14 @@ import {LangConfig} from './lang-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; import { ItemPageConfig } from './item-page-config.interface'; import { Theme } from './theme.inferface'; +import {AuthConfig} from './auth-config.interfaces'; export interface GlobalConfig extends Config { ui: ServerConfig; rest: ServerConfig; production: boolean; cache: CacheConfig; + auth: AuthConfig; form: FormConfig; notifications: INotificationBoardOptions; submission: SubmissionConfig;