Skip to content

Commit

Permalink
Authentication dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
newmanw committed Aug 13, 2024
1 parent 47e3403 commit e90cbd5
Show file tree
Hide file tree
Showing 54 changed files with 661 additions and 276 deletions.
10 changes: 8 additions & 2 deletions web-app/src/app/api/api.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ export interface AuthenticationStrategy {
icon: string
}

export type Disclaimer = {
show: boolean,
title: string,
text: string
}

export interface Api {
version: Version,
initial: boolean,
disclaimer: any,
contanctInfo: any,
disclaimer?: Disclaimer,
contactInfo: any,
localAuthenticationStrategy: AuthenticationStrategy,
authenticationStrategies: { string: AuthenticationStrategy }
}
15 changes: 9 additions & 6 deletions web-app/src/app/api/api.service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { HttpClient } from "@angular/common/http";
import { HttpClient, HttpContext } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { BYPASS_TOKEN } from "../http/token.interceptor";

@Injectable({
providedIn: 'root'
providedIn: 'root'
})
export class ApiService {
constructor(private client: HttpClient) { }
constructor(private httpClient: HttpClient) { }

getApi(): Observable<any> {
return this.client.get<any>('/api');
}
getApi(): Observable<any> {
return this.httpClient.get<any>('/api', {
context: new HttpContext().set(BYPASS_TOKEN, true)
})
}
}
24 changes: 16 additions & 8 deletions web-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,9 @@ import { AdminMapComponent } from './admin/admin-map/admin-map.component';
import { AppComponent } from './app.component';
import { LandingComponent } from './landing/landing.component';
import { InfoComponent } from './landing/info.component';
import { AuthenticationComponent } from './authentication/authentication.component';
import { AuthorizeComponent } from './authentication/authorize.component';
import { LocalAuthenticationComponent } from './authentication/local/local.component';
import { MageComponent } from './mage/mage.component';
import { AppRoutingModule } from './routing.module';
import { TokenInterceptorService } from './http/token.interceptor';
import { IdpAuthenticationComponent } from './authentication/idp/idp.component';
import { NavigationComponent } from './navigation/navigation.component';
import { FilterComponent } from './filter/filter.component';
import { PreferencesComponent } from './preferences/preferences.component';
Expand All @@ -174,8 +170,15 @@ import { CoordinateSystemComponent } from './preferences/coordinate-system/coord
import { AboutComponent } from './about/about.component';
import { ProfileComponent } from './user/profile/profile.component';
import { PasswordResetSuccessDialog } from './user/password/password-reset-success-dialog';
import { SignupComponent } from './authentication/local/signup.component';
import { StatusComponent } from './authentication/local/status/status.component';
import { AuthenticationDialogComponent } from './ingress/authentication/authentication-dialog.component';
import { AuthenticationComponent } from './ingress/authentication/authentication.component';
import { IdpAuthenticationComponent } from './ingress/authentication/idp/idp.component';
import { LocalAuthenticationComponent } from './ingress/authentication/local/local-authentication.component';
import { SignupComponent } from './ingress/authentication/local/signup.component';
import { StatusComponent } from './ingress/authentication/local/status/status.component';
import { AuthorizationComponent } from './ingress/authorization/authorization.component';
import { DisclaimerComponent } from './ingress/disclaimer/disclaimer.component';
import { IngressComponent } from './ingress/ingress.component';

@NgModule({
declarations: [
Expand All @@ -188,12 +191,15 @@ import { StatusComponent } from './authentication/local/status/status.component'
SearchComponent,
LandingComponent,
InfoComponent,
IngressComponent,
AuthenticationComponent,
AuthorizationComponent,
AuthenticationDialogComponent,
IdpAuthenticationComponent,
LocalAuthenticationComponent,
SignupComponent,
StatusComponent,
AuthorizeComponent,
DisclaimerComponent,
MageComponent,
MapComponent,
PasswordResetSuccessDialog,
Expand Down Expand Up @@ -370,7 +376,9 @@ import { StatusComponent } from './authentication/local/status/status.component'
// eventProvider,
// authenticationConfigurationServiceProvider,
// userPagingServiceProvider,
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptorService, multi: true }
{
provide: HTTP_INTERCEPTORS, useClass: TokenInterceptorService, multi: true
}
],
bootstrap: [AppComponent]
})
Expand Down
68 changes: 0 additions & 68 deletions web-app/src/app/authentication/authentication.component.ts

This file was deleted.

17 changes: 0 additions & 17 deletions web-app/src/app/authentication/authorize.component.html

This file was deleted.

4 changes: 4 additions & 0 deletions web-app/src/app/entities/user/entities.user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface User {
username: string
displayName: string
}
52 changes: 44 additions & 8 deletions web-app/src/app/http/token.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,65 @@
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpContextToken } from '@angular/common/http';
import { Observable } from 'rxjs';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpContextToken, HttpStatusCode, HttpErrorResponse, HttpClient } from '@angular/common/http';
import { catchError, Observable, Subject, switchMap, throwError } from 'rxjs';
import { LocalStorageService } from './local-storage.service';
import { AuthenticationDialogComponent } from '../ingress/authentication/authentication-dialog.component';
import { MatDialog } from '@angular/material/dialog';

export const BYPASS_TOKEN = new HttpContextToken(() => false);

@Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
isRefreshingToken: boolean = false
tokenSubject: Subject<void> = new Subject<void>()

constructor(private localStorageService: LocalStorageService) { }
constructor(
public dialog: MatDialog,
private localStorageService: LocalStorageService
) { }

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.context.get(BYPASS_TOKEN) === true) {
const bypassToken = req.context.get(BYPASS_TOKEN) === true
if (bypassToken) {
return next.handle(req);
}

const token = this.localStorageService.getToken();
if (req.url.startsWith('/api/')) {
return next.handle(this.tokenRequest(req)).pipe(
catchError((error) => {
if (error instanceof HttpErrorResponse) {
if (error.status === HttpStatusCode.Unauthorized) {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true
this.dialog.open(AuthenticationDialogComponent, {
width: '600px',
disableClose: true,
autoFocus: false
}).afterClosed().subscribe(() => {
this.isRefreshingToken = false
this.tokenSubject.next()
})
}

return this.tokenSubject.pipe(
switchMap(() => {
return next.handle(this.tokenRequest(req))
})
)
}
}

if (token && req.url.startsWith('/api/')) {
const newReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) });
return next.handle(newReq)
return throwError(() => error)
})
)
} else {
return next.handle(req)
}
}

tokenRequest(req: HttpRequest<any>): HttpRequest<any> {
const token = this.localStorageService.getToken();
return req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div>
<h3 class="mat-dialog-title">Please signin to continue to Mage</h3>
<div class="mat-dialog-content">
<div class="ingress" >
<ingress [landing]="false" [api]="api" (complete)="onIngress()"></ingress>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.ingress {
width: 100%;
height: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { AuthorizeComponent } from '../../authentication/authorize.component';
import { AuthenticationDialogComponent } from './authentication-dialog.component';

describe('Authentication Dialog', () => {
let component: AuthenticationDialogComponent;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [AuthorizeComponent]
})
.compileComponents();
}));


it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../api/api.service';
import { Api } from '../../api/api.entity';
import { MatDialogRef } from '@angular/material/dialog';

@Component({
selector: 'authentication-dialog',
templateUrl: 'authentication-dialog.component.html',
styleUrls: ['./authentication-dialog.component.scss']
})
export class AuthenticationDialogComponent implements OnInit {
api: Api

constructor(
private apiService: ApiService,
public dialogRef: MatDialogRef<AuthenticationDialogComponent>
) {}

ngOnInit(): void {
this.apiService.getApi().subscribe((api: Api) => {
this.api = api
})
}

onIngress(): void {
this.dialogRef.close()
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
<div class="signin-container">
<div class="welcome">
<div *ngIf="landing" class="welcome">
<div class="welcome__title">Welcome to Mage</div>
<div class="welcome__message">Sign in to your account</div>
</div>

<div class="authentications">
<local-authentication *ngIf="localAuthenticationStrategy" strategy="localAuthenticationStrategy"
[hideSignup]="hideSignup" (onSignup)="onSignup({strategy: localAuthenticationStrategy})"
(onSignin)="onSignin($event)">
</local-authentication>

<div *ngIf=" localAuthenticationStrategy && thirdPartyStrategies.length">
<local-authentication *ngIf="localAuthenticationStrategy" strategy="localAuthenticationStrategy" [landing]="landing" (authenticated)="signin($event)"></local-authentication>

<div *ngIf="localAuthenticationStrategy && thirdPartyStrategies.length">
<div class="or">
<div class="or__divider"></div>
<div class="or__text">or</div>
<div class="or__divider"></div>
</div>
</div>

<div *ngFor='let strategy of thirdPartyStrategies; index as i'>
<div [ngClass]="{'spacer': i < thirdPartyStrategies.length - 1}">
<ng-container [ngSwitch]="strategy?.type">
<idp-authentication *ngSwitchCase="'oauth'" [strategy]="strategy" onSignin="signin($event)"></idp-authentication>
<idp-authentication *ngSwitchCase="'saml'" [strategy]="strategy" onSignin="signin($event)"></idp-authentication>
<idp-authentication *ngSwitchCase="'oauth'" [strategy]="strategy"
(authenticated)="signin($event)"></idp-authentication>
<idp-authentication *ngSwitchCase="'saml'" [strategy]="strategy"
(authenticated)="signin($event)"></idp-authentication>
<idp-authentication *ngSwitchCase="'openidconnect'" [strategy]="strategy"
onSignin="signin($event)"></idp-authentication>
(authenticated)="signin($event)"></idp-authentication>
<!-- <ldap-signin ng-switch-when="ldap" strategy="strategy" on-signin="signin($event)"></ldap-signin> -->
</ng-container>
</div>
Expand Down
Loading

0 comments on commit e90cbd5

Please sign in to comment.