Removed switch statement, changed AuthMethod parsing in authInterceptor to be more generic

This commit is contained in:
Julius Gruber
2019-09-02 15:01:03 +02:00
parent beb0f2d410
commit 901951eaa8
12 changed files with 94 additions and 58 deletions

View File

@@ -3,7 +3,8 @@
<div> <div>
<img class="mb-4 login-logo" src="assets/images/dspace-logo.png"> <img class="mb-4 login-logo" src="assets/images/dspace-logo.png">
<h1 class="h3 mb-0 font-weight-normal">{{"login.form.header" | translate}}</h1> <h1 class="h3 mb-0 font-weight-normal">{{"login.form.header" | translate}}</h1>
<ds-login-container></ds-login-container> <!-- <ds-login-container></ds-login-container>-->
<ds-auth-methods></ds-auth-methods>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
import {Observable, of as observableOf, throwError as observableThrowError} from 'rxjs'; import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
import {catchError, filter, map, tap} from 'rxjs/operators'; import { catchError, filter, map, tap } from 'rxjs/operators';
import {Injectable, Injector} from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { import {
HttpErrorResponse, HttpErrorResponse,
HttpEvent, HttpEvent,
@@ -11,18 +11,18 @@ import {
HttpResponse, HttpResponse,
HttpResponseBase HttpResponseBase
} from '@angular/common/http'; } from '@angular/common/http';
import {find} from 'lodash'; import { find } from 'lodash';
import {AppState} from '../../app.reducer'; import { AppState } from '../../app.reducer';
import {AuthService} from './auth.service'; import { AuthService } from './auth.service';
import {AuthStatus} from './models/auth-status.model'; import { AuthStatus } from './models/auth-status.model';
import {AuthTokenInfo} from './models/auth-token-info.model'; import { AuthTokenInfo } from './models/auth-token-info.model';
import {isNotEmpty, isUndefined, isNotNull} from '../../shared/empty.util'; import { isNotEmpty, isUndefined, isNotNull } from '../../shared/empty.util';
import {RedirectWhenTokenExpiredAction, RefreshTokenAction} from './auth.actions'; import { RedirectWhenTokenExpiredAction, RefreshTokenAction } from './auth.actions';
import {Store} from '@ngrx/store'; import { Store } from '@ngrx/store';
import {Router} from '@angular/router'; import { Router } from '@angular/router';
import {AuthError} from './models/auth-error.model'; import { AuthError } from './models/auth-error.model';
import {AuthMethodModel} from './models/auth-method.model'; import { AuthMethodModel } from './models/auth-method.model';
@Injectable() @Injectable()
export class AuthInterceptor implements HttpInterceptor { export class AuthInterceptor implements HttpInterceptor {
@@ -59,8 +59,7 @@ export class AuthInterceptor implements HttpInterceptor {
return http.url && http.url.endsWith('/authn/logout'); return http.url && http.url.endsWith('/authn/logout');
} }
private parseShibbolethLocation(unparsedLocation: string): string { private parseLocation(unparsedLocation: string): string {
let parsedLocation = '';
unparsedLocation = unparsedLocation.trim(); unparsedLocation = unparsedLocation.trim();
unparsedLocation = unparsedLocation.replace('location="', ''); unparsedLocation = unparsedLocation.replace('location="', '');
unparsedLocation = unparsedLocation.replace('"', ''); unparsedLocation = unparsedLocation.replace('"', '');
@@ -68,7 +67,7 @@ export class AuthInterceptor implements HttpInterceptor {
unparsedLocation = unparsedLocation.replace(re, '://'); unparsedLocation = unparsedLocation.replace(re, '://');
re = /%3A/g re = /%3A/g
unparsedLocation = unparsedLocation.replace(re, ':') unparsedLocation = unparsedLocation.replace(re, ':')
parsedLocation = unparsedLocation + '/shibboleth'; const parsedLocation = unparsedLocation.trim(); // + '/shibboleth';
return parsedLocation; return parsedLocation;
} }
@@ -76,24 +75,32 @@ export class AuthInterceptor implements HttpInterceptor {
private parseAuthMethodsfromHeaders(headers: HttpHeaders): AuthMethodModel[] { private parseAuthMethodsfromHeaders(headers: HttpHeaders): AuthMethodModel[] {
const authMethodModels: AuthMethodModel[] = []; const authMethodModels: AuthMethodModel[] = [];
const parts: string[] = headers.get('www-authenticate').split(','); const parts: string[] = headers.get('www-authenticate').split(',');
// get the login methods names // get the realms from the header - a realm is a single auth method
const completeWWWauthenticateHeader = headers.get('www-authenticate');
const regex = /(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g;
const realms = completeWWWauthenticateHeader.match(regex);
console.log('realms: ', realms)
// tslint:disable-next-line:forin // tslint:disable-next-line:forin
for (const i in parts) { for (const j in realms) {
const part: string = parts[i].trim(); console.log('realm:', realms[j]);
if (part.includes('realm')) {
const methodName = part.split(' ')[0]; const splittedRealm = realms[j].split(', ');
const authMethod: AuthMethodModel = new AuthMethodModel(methodName); const methodName = splittedRealm[0].split(' ')[0].trim();
// check if the authentication method is shibboleth console.log('methodName: ', methodName);
// if so the next part is the shibboleth location
// e.g part i: shibboleth realm="DSpace REST API", part i+1: location="/Shibboleth.sso/Login?target=https://serverUrl" console.log('splittedRealm: ', splittedRealm);
if (methodName.includes('shibboleth')) { let authMethodModel: AuthMethodModel;
// +1: unaray + operator in the next line is necessaray because i is a string, the operator works like parseInt() if (splittedRealm.length === 1) {
const location: string = this.parseShibbolethLocation(parts[+i + 1]); authMethodModel = new AuthMethodModel(methodName);
authMethod.location = location; authMethodModels.push(authMethodModel);
} } else if (splittedRealm.length > 1) {
// if other authentication methods deliver data needed for the method to work authMethodModel = new AuthMethodModel(methodName);
// it would be checked here e.g. if (methodName.includes('ldap')) { } let location = splittedRealm[1];
authMethodModels.push(authMethod); location = this.parseLocation(location)
authMethodModel.location = location;
console.log('location: ', location);
authMethodModels.push(authMethodModel);
} }
} }
return authMethodModels; return authMethodModels;

View File

@@ -1,12 +1,10 @@
import {AuthMethodType} from '../../../shared/log-in/authMethods-type'; import {AuthMethodType} from '../../../shared/log-in/authMethods-type';
export class AuthMethodModel { export class AuthMethodModel {
authMethodName: string; authMethodType: AuthMethodType;
location?: string; location?: string;
authMethodType: AuthMethodType
constructor(authMethodName: string, location?: string) { constructor(authMethodName: string, location?: string) {
this.authMethodName = authMethodName;
this.location = location; this.location = location;
switch (authMethodName) { switch (authMethodName) {
case 'ip': { case 'ip': {

View File

@@ -4,7 +4,8 @@
<a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a> <a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a>
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin"> <div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
<!-- <ds-log-in></ds-log-in>--> <!-- <ds-log-in></ds-log-in>-->
<ds-login-container></ds-login-container> <!-- <ds-login-container></ds-login-container>-->
<ds-auth-methods></ds-auth-methods>
</div> </div>
</div> </div>
</li> </li>

View File

@@ -0,0 +1,3 @@
<ng-container *ngFor="let authMethodModel of (authMethodData | async)">
<ds-login-container [authMethodModel]="authMethodModel"></ds-login-container>
</ng-container>

View File

@@ -0,0 +1,27 @@
import { Component, Injector, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthMethodModel } from '../../core/auth/models/auth-method.model';
import { Store } from '@ngrx/store';
import { AuthState } from '../../core/auth/auth.reducer';
import { getAuthenticationMethods } from '../../core/auth/selectors';
@Component({
selector: 'ds-auth-methods',
templateUrl: './authMethods.component.html',
styleUrls: ['./authMethods.component.scss']
})
export class AuthMethodsComponent implements OnInit {
/**
* The authentication methods data
* @type {AuthMethodModel[]}
*/
@Input() authMethodData: Observable<AuthMethodModel[]>;
constructor( private store: Store<AuthState>) {
}
ngOnInit(): void {
this.authMethodData = this.authMethodData = this.store.select(getAuthenticationMethods);
}
}

View File

@@ -1,3 +1,2 @@
<div *ngFor="let authMethod of authMethodData | async"> <ng-container *ngComponentOutlet="getAuthMethodContent(); injector: objectInjector;"></ng-container>
<ng-container *ngComponentOutlet="getAuthMethodContent(authMethod.authMethodType); injector: objectInjector;"></ng-container>
</div>

View File

@@ -17,11 +17,7 @@ import { AuthMethodType } from '../authMethods-type';
}) })
export class LoginContainerComponent implements OnInit { export class LoginContainerComponent implements OnInit {
/** @Input() authMethodModel: AuthMethodModel;
* The section data
* @type {SectionDataObject}
*/
@Input() authMethodData: Observable<AuthMethodModel[]>;
/** /**
* Injector to inject a section component with the @Input parameters * Injector to inject a section component with the @Input parameters
@@ -34,7 +30,7 @@ export class LoginContainerComponent implements OnInit {
* *
* @param {Injector} injector * @param {Injector} injector
*/ */
constructor(private injector: Injector, private store: Store<AppState>) { constructor(private injector: Injector) {
} }
/** /**
@@ -43,18 +39,17 @@ export class LoginContainerComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.objectInjector = Injector.create({ this.objectInjector = Injector.create({
providers: [ providers: [
{provide: 'authMethodProvider', useFactory: () => (this.authMethodData), deps: []}, {provide: 'authMethodModelProvider', useFactory: () => (this.authMethodModel), deps: []},
], ],
parent: this.injector parent: this.injector
}); });
this.authMethodData = this.store.select(getAuthenticationMethods);
} }
/** /**
* Find the correct component based on the authMethod's type * Find the correct component based on the authMethod's type
*/ */
getAuthMethodContent(authMethodType: AuthMethodType): string { getAuthMethodContent(): string {
return rendersAuthMethodType(authMethodType) return rendersAuthMethodType(this.authMethodModel.authMethodType)
} }
} }

View File

@@ -1,6 +1,6 @@
<div> <div>
<a class="btn btn-lg btn-primary btn-block mt-3" type="submit" <a class="btn btn-lg btn-primary btn-block mt-3" type="submit"
[href]="injectedShibbolethUrl" [href]="authMethodModel.location"
role="button" role="button"
>{{"login.shibboleth" | translate}}</a> >{{"login.shibboleth" | translate}}</a>
</div> </div>

View File

@@ -12,14 +12,17 @@ import { AuthMethodModel } from '../../../../core/auth/models/auth-method.model'
@renderAuthMethodFor(AuthMethodType.Shibboleth) @renderAuthMethodFor(AuthMethodType.Shibboleth)
export class DynamicShibbolethComponent implements OnInit { export class DynamicShibbolethComponent implements OnInit {
@Input()authMethodModel: AuthMethodModel;
/** /**
* @constructor * @constructor
*/ */
constructor(@Inject('authMethodProvider') public injectedObject: AuthMethodModel) { constructor(@Inject('authMethodModelProvider') public injectedAuthMethodModel: AuthMethodModel) {
this.authMethodModel = injectedAuthMethodModel;
} }
ngOnInit(): void { ngOnInit(): void {
console.log('injectedObject', this.injectedObject) console.log('injectedAuthMethodModel', this.injectedAuthMethodModel);
} }
} }

View File

@@ -141,6 +141,7 @@ import {DynamicShibbolethComponent} from './log-in/methods/shibboleth/dynamic-sh
// import {LogInComponent} from './log-in/log-in.component'; // import {LogInComponent} from './log-in/log-in.component';
import {LogInPasswordComponent} from './log-in/methods/password/log-in-password.component'; import {LogInPasswordComponent} from './log-in/methods/password/log-in-password.component';
import { LoginContainerComponent } from './log-in/container/login-container.component'; import { LoginContainerComponent } from './log-in/container/login-container.component';
import { AuthMethodsComponent } from './log-in/authMethods.component';
const MODULES = [ const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -264,7 +265,8 @@ const COMPONENTS = [
// LogInComponent, // LogInComponent,
DynamicShibbolethComponent, DynamicShibbolethComponent,
LogInPasswordComponent, LogInPasswordComponent,
LoginContainerComponent LoginContainerComponent,
AuthMethodsComponent
]; ];
const ENTRY_COMPONENTS = [ const ENTRY_COMPONENTS = [