From 490848ebed1f2fa2dc428371fc46729003d5469c Mon Sep 17 00:00:00 2001
From: Florent Poittevin <florent.poittevin@unige.ch>
Date: Tue, 16 Apr 2019 15:13:25 +0200
Subject: [PATCH] Add OAuth

---
 README.md                           | 24 +++++++++---
 package-lock.json                   | 22 +++++++++++
 package.json                        | 58 +++++++++++++++--------------
 src/app/app.component.html          | 24 +++---------
 src/app/app.component.ts            | 13 ++++++-
 src/app/app.module.ts               | 43 ++++++++++++++++-----
 src/app/app.service.ts              | 18 +++++++++
 src/app/auth.config.ts              | 27 ++++++++++++++
 src/app/dlcm.interceptor.ts         | 14 +++++++
 src/app/dlcm.storage.ts             | 29 +++++++++++++++
 src/app/home/home.component.html    | 10 +++++
 src/app/home/home.component.scss    |  0
 src/app/home/home.component.spec.ts | 25 +++++++++++++
 src/app/home/home.component.ts      | 25 +++++++++++++
 src/index.html                      |  2 +-
 15 files changed, 270 insertions(+), 64 deletions(-)
 create mode 100644 src/app/app.service.ts
 create mode 100644 src/app/auth.config.ts
 create mode 100644 src/app/dlcm.interceptor.ts
 create mode 100644 src/app/dlcm.storage.ts
 create mode 100644 src/app/home/home.component.html
 create mode 100644 src/app/home/home.component.scss
 create mode 100644 src/app/home/home.component.spec.ts
 create mode 100644 src/app/home/home.component.ts

diff --git a/README.md b/README.md
index 183b0be17..59354e971 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,24 @@
-# DLCMFrontend
+# DLCM-Frontend
 
 This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.3.8.
 
+## Prerequisites
+
+- Install [NodeJS LTS](https://nodejs.org/en/) (currently version 10.15.3, check with `node -v`. Include currently npm version 6.4.1, check with `npm -v`).
+- Install [Angular CLI](https://angular.io/cli) (currently version 7.3.8, check with `ng version`).
+- For user of Jetbrains IDE (IntelliJ, WebStorm), go to `File > Settings > Appaearance & Behavior > System Settings` and disable the option `Use "safe write" (save changes to a temporary file first)`.
+
+### Apache
+
+Same as DLCM-Frontend in Php to mock Shibboleth
+
+## Install
+
+`npm install`
+
 ## Development server
 
-Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+Run `npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
 
 ## Code scaffolding
 
@@ -12,15 +26,15 @@ Run `ng generate component component-name` to generate a new component. You can
 
 ## Build
 
-Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
+Run `npm build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
 
 ## Running unit tests
 
-Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+Run `npm test` to execute the unit tests via [Karma](https://karma-runner.github.io).
 
 ## Running end-to-end tests
 
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+Run `npm e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
 
 ## Further help
 
diff --git a/package-lock.json b/package-lock.json
index 70ee5382e..34c72b8b5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -623,6 +623,14 @@
         "webpack-sources": "1.3.0"
       }
     },
+    "@ngxs/store": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.4.3.tgz",
+      "integrity": "sha512-pVwxFxCxr09+zL37ot1hf6EhgpftNLSAseBZwHUlx40x2qAT+jat8XJlj4j0p+BoVEjlaVZn8zE6qMm2oSPlZw==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "@schematics/angular": {
       "version": "7.3.8",
       "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.8.tgz",
@@ -997,6 +1005,15 @@
       "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
       "dev": true
     },
+    "angular-oauth2-oidc": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/angular-oauth2-oidc/-/angular-oauth2-oidc-5.0.2.tgz",
+      "integrity": "sha512-jtOv4IWEjSFfBHVE4seWGWT/ZfWJ95QJ1JaFhVVGJEF64ibGuPwV3ztwTOUl98QHi/Yg4PXXDAisb31JnIbxBw==",
+      "requires": {
+        "jsrsasign": "^8.0.12",
+        "tslib": "^1.9.0"
+      }
+    },
     "ansi-colors": {
       "version": "3.2.4",
       "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
@@ -5505,6 +5522,11 @@
         "verror": "1.10.0"
       }
     },
+    "jsrsasign": {
+      "version": "8.0.12",
+      "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz",
+      "integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY="
+    },
     "jszip": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.1.tgz",
diff --git a/package.json b/package.json
index b459bccc0..e0f202d1c 100644
--- a/package.json
+++ b/package.json
@@ -11,38 +11,40 @@
   },
   "private": true,
   "dependencies": {
-    "@angular/animations": "~7.2.0",
-    "@angular/common": "~7.2.0",
-    "@angular/compiler": "~7.2.0",
-    "@angular/core": "~7.2.0",
-    "@angular/forms": "~7.2.0",
-    "@angular/platform-browser": "~7.2.0",
-    "@angular/platform-browser-dynamic": "~7.2.0",
-    "@angular/router": "~7.2.0",
+    "@angular/animations": "^7.2.0",
+    "@angular/common": "^7.2.0",
+    "@angular/compiler": "^7.2.0",
+    "@angular/core": "^7.2.0",
+    "@angular/forms": "^7.2.0",
+    "@angular/platform-browser": "^7.2.0",
+    "@angular/platform-browser-dynamic": "^7.2.0",
+    "@angular/router": "^7.2.0",
+    "@ngxs/store": "^3.4.3",
+    "angular-oauth2-oidc": "^5.0.2",
     "core-js": "^2.5.4",
-    "rxjs": "~6.3.3",
+    "rxjs": "^6.3.3",
     "tslib": "^1.9.0",
-    "zone.js": "~0.8.26"
+    "zone.js": "^0.8.26"
   },
   "devDependencies": {
-    "@angular-devkit/build-angular": "~0.13.0",
-    "@angular/cli": "~7.3.8",
-    "@angular/compiler-cli": "~7.2.0",
-    "@angular/language-service": "~7.2.0",
-    "@types/node": "~8.9.4",
-    "@types/jasmine": "~2.8.8",
-    "@types/jasminewd2": "~2.0.3",
-    "codelyzer": "~4.5.0",
-    "jasmine-core": "~2.99.1",
-    "jasmine-spec-reporter": "~4.2.1",
-    "karma": "~4.0.0",
-    "karma-chrome-launcher": "~2.2.0",
-    "karma-coverage-istanbul-reporter": "~2.0.1",
-    "karma-jasmine": "~1.1.2",
+    "@angular-devkit/build-angular": "^0.13.0",
+    "@angular/cli": "^7.3.8",
+    "@angular/compiler-cli": "^7.2.0",
+    "@angular/language-service": "^7.2.0",
+    "@types/node": "^8.9.4",
+    "@types/jasmine": "^2.8.8",
+    "@types/jasminewd2": "^2.0.3",
+    "codelyzer": "^4.5.0",
+    "jasmine-core": "^2.99.1",
+    "jasmine-spec-reporter": "^4.2.1",
+    "karma": "^4.0.0",
+    "karma-chrome-launcher": "^2.2.0",
+    "karma-coverage-istanbul-reporter": "^2.0.1",
+    "karma-jasmine": "^1.1.2",
     "karma-jasmine-html-reporter": "^0.2.2",
-    "protractor": "~5.4.0",
-    "ts-node": "~7.0.0",
-    "tslint": "~5.11.0",
-    "typescript": "~3.2.2"
+    "protractor": "^5.4.0",
+    "ts-node": "^7.0.0",
+    "tslint": "^5.11.0",
+    "typescript": "^3.2.2"
   }
 }
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 0f3d9d8b9..0ea2e42fa 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,21 +1,7 @@
-<!--The content below is only a placeholder and can be replaced.-->
-<div style="text-align:center">
-  <h1>
-    Welcome to {{ title }}!
-  </h1>
-  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
-</div>
-<h2>Here are some links to help you start: </h2>
-<ul>
-  <li>
-    <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
-  </li>
-  <li>
-    <h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
-  </li>
-  <li>
-    <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
-  </li>
-</ul>
+<h1>
+  Welcome to {{ title }}!
+</h1>
+
+<dlcm-home></dlcm-home>
 
 <router-outlet></router-outlet>
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 1e55cbdb0..f99d76118 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,10 +1,19 @@
-import { Component } from '@angular/core';
+import {Component} from '@angular/core';
+
+import {OAuthService} from 'angular-oauth2-oidc';
 
 @Component({
   selector: 'dlcm-root',
   templateUrl: './app.component.html',
-  styleUrls: ['./app.component.scss']
+  styleUrls: ['./app.component.scss'],
 })
 export class AppComponent {
   title = 'DLCM-Frontend';
+
+  constructor(private oauthService: OAuthService) {
+    // This method just tries to parse the token(s) within the url when
+    // the auth-server redirects the user back to the web-app
+    // It doesn't send the user the the login page
+    this.oauthService.tryLogin();
+  }
 }
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 2c3ba2995..dcb476299 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,18 +1,43 @@
-import { BrowserModule } from '@angular/platform-browser';
-import { NgModule } from '@angular/core';
+import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
+import {NgModule} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {NgxsModule} from '@ngxs/store';
 
-import { AppRoutingModule } from './app-routing.module';
-import { AppComponent } from './app.component';
+import {AuthConfig, JwksValidationHandler, OAuthModule, OAuthModuleConfig, OAuthStorage, ValidationHandler} from 'angular-oauth2-oidc';
+
+import {AppRoutingModule} from './app-routing.module';
+import {AppComponent} from './app.component';
+import {HomeComponent} from './home/home.component';
+import {DlcmStorage} from './dlcm.storage';
+import {DlcmInterceptor} from './dlcm.interceptor';
+import {authConfig, authModuleConfig} from './auth.config';
 
 @NgModule({
   declarations: [
-    AppComponent
+    AppComponent,
+    HomeComponent,
   ],
   imports: [
     BrowserModule,
-    AppRoutingModule
+    AppRoutingModule,
+    NgxsModule.forRoot([
+      // MyState
+    ]),
+    HttpClientModule,
+    OAuthModule.forRoot(authModuleConfig),
+  ],
+  providers: [
+    {provide: OAuthModuleConfig, useValue: authModuleConfig},
+    {provide: ValidationHandler, useClass: JwksValidationHandler},
+    {provide: OAuthStorage, useClass: DlcmStorage},
+    {provide: AuthConfig, useValue: authConfig},
+    {
+      provide: HTTP_INTERCEPTORS,
+      useClass: DlcmInterceptor,
+      multi: true,
+    },
   ],
-  providers: [],
-  bootstrap: [AppComponent]
+  bootstrap: [AppComponent],
 })
-export class AppModule { }
+export class AppModule {
+}
diff --git a/src/app/app.service.ts b/src/app/app.service.ts
new file mode 100644
index 000000000..403cc92a7
--- /dev/null
+++ b/src/app/app.service.ts
@@ -0,0 +1,18 @@
+import {Injectable} from '@angular/core';
+import {Router} from '@angular/router';
+import {HttpClient} from '@angular/common/http';
+
+@Injectable()
+export class AppService {
+
+  constructor(private router: Router,
+              private http: HttpClient) {
+  }
+
+  getOrganizationUnits() {
+    this.http.get('http://localhost:16118/dlcm/access/oai-sets/organizational-units')
+      .subscribe((data) => {
+        console.log('test : ', data);
+      });
+  }
+}
diff --git a/src/app/auth.config.ts b/src/app/auth.config.ts
new file mode 100644
index 000000000..913b672f5
--- /dev/null
+++ b/src/app/auth.config.ts
@@ -0,0 +1,27 @@
+import {AuthConfig, OAuthModuleConfig} from 'angular-oauth2-oidc';
+
+export const authConfig: AuthConfig = {
+  oidc: false,
+  loginUrl: 'http://localhost/dlcm/authorisation/authorize',
+  redirectUri: window.location.origin,
+  responseType: 'code',
+  dummyClientSecret: '123abc',
+  clientId: 'local-angular',
+  scope: 'READ',
+  requireHttps: false,
+  tokenEndpoint: 'http://localhost/dlcm/authorisation/token',
+  requestAccessToken: true,
+  showDebugInformation: true,
+  useIdTokenHintForSilentRefresh: true,
+  customQueryParams: {
+    response_type: 'implicit',
+  },
+};
+
+export const authModuleConfig: OAuthModuleConfig = {
+  // Inject "Authorization: Bearer ..." header for these APIs:
+  resourceServer: {
+    allowedUrls: ['http://localhost'],
+    sendAccessToken: true,
+  },
+};
diff --git a/src/app/dlcm.interceptor.ts b/src/app/dlcm.interceptor.ts
new file mode 100644
index 000000000..f0fe16974
--- /dev/null
+++ b/src/app/dlcm.interceptor.ts
@@ -0,0 +1,14 @@
+import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs';
+
+@Injectable()
+export class DlcmInterceptor implements HttpInterceptor {
+  constructor() {
+  }
+
+  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
+    request = request.clone();
+    return next.handle(request);
+  }
+}
diff --git a/src/app/dlcm.storage.ts b/src/app/dlcm.storage.ts
new file mode 100644
index 000000000..86b514d62
--- /dev/null
+++ b/src/app/dlcm.storage.ts
@@ -0,0 +1,29 @@
+import {OAuthStorage} from 'angular-oauth2-oidc';
+import {Injectable} from '@angular/core';
+
+/**
+ * Custom storage in memory for sensible data like token
+ * (cf best practice https://auth0.com/docs/security/store-tokens)
+ */
+@Injectable()
+export class DlcmStorage extends OAuthStorage {
+  private storageValues: Map<string, string>;
+
+  constructor() {
+    super();
+    this.storageValues = new Map<string, string>();
+  }
+
+  getItem(key: string): string | null {
+    return this.storageValues[key];
+  }
+
+  removeItem(key: string): void {
+    this.storageValues.delete(key);
+  }
+
+  setItem(key: string, data: string): void {
+    this.storageValues.set(key, data);
+  }
+
+}
diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html
new file mode 100644
index 000000000..daa6b8f33
--- /dev/null
+++ b/src/app/home/home.component.html
@@ -0,0 +1,10 @@
+<div>
+  Token : {{token}}
+</div>
+<div>
+  Claims : {{claims}}
+</div>
+
+<button class="btn btn-default" (click)="login()">
+  Login
+</button>
diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts
new file mode 100644
index 000000000..490e81bdf
--- /dev/null
+++ b/src/app/home/home.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HomeComponent } from './home.component';
+
+describe('HomeComponent', () => {
+  let component: HomeComponent;
+  let fixture: ComponentFixture<HomeComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ HomeComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(HomeComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts
new file mode 100644
index 000000000..b29a37cf8
--- /dev/null
+++ b/src/app/home/home.component.ts
@@ -0,0 +1,25 @@
+import {Component} from '@angular/core';
+import {OAuthService} from 'angular-oauth2-oidc';
+
+@Component({
+  selector: 'dlcm-home',
+  templateUrl: './home.component.html',
+  styleUrls: ['./home.component.scss'],
+})
+export class HomeComponent {
+
+  constructor(private oauthService: OAuthService) {
+  }
+
+  get token() {
+    return this.oauthService.getAccessToken();
+  }
+
+  get claims() {
+    return this.oauthService.getIdentityClaims();
+  }
+
+  login() {
+    this.oauthService.initImplicitFlow();
+  }
+}
diff --git a/src/index.html b/src/index.html
index 205094712..3966aebad 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,7 +2,7 @@
 <html lang="en">
 <head>
   <meta charset="utf-8">
-  <title>DLCMFrontend</title>
+  <title>DLCM - Frontend</title>
   <base href="/">
 
   <meta name="viewport" content="width=device-width, initial-scale=1">
-- 
GitLab