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

View File

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

View File

@@ -4,7 +4,8 @@
<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">
<!-- <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>
</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(authMethod.authMethodType); injector: objectInjector;"></ng-container>
</div>
<ng-container *ngComponentOutlet="getAuthMethodContent(); injector: objectInjector;"></ng-container>

View File

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

View File

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

View File

@@ -12,14 +12,17 @@ import { AuthMethodModel } from '../../../../core/auth/models/auth-method.model'
@renderAuthMethodFor(AuthMethodType.Shibboleth)
export class DynamicShibbolethComponent implements OnInit {
@Input()authMethodModel: AuthMethodModel;
/**
* @constructor
*/
constructor(@Inject('authMethodProvider') public injectedObject: AuthMethodModel) {
constructor(@Inject('authMethodModelProvider') public injectedAuthMethodModel: AuthMethodModel) {
this.authMethodModel = injectedAuthMethodModel;
}
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 {LogInPasswordComponent} from './log-in/methods/password/log-in-password.component';
import { LoginContainerComponent } from './log-in/container/login-container.component';
import { AuthMethodsComponent } from './log-in/authMethods.component';
const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -264,7 +265,8 @@ const COMPONENTS = [
// LogInComponent,
DynamicShibbolethComponent,
LogInPasswordComponent,
LoginContainerComponent
LoginContainerComponent,
AuthMethodsComponent
];
const ENTRY_COMPONENTS = [