changeset 257:f13e9da11350

Port authServices to TypeScript, upgrade for Angular 4 Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-October/025463.html
author Andrew Azores <aazores@redhat.com>
date Fri, 20 Oct 2017 16:16:16 -0400
parents 288f0c458eb6
children 828e021d52fd
files src/app/app.module.ts src/app/components/auth/auth-service.interface.ts src/app/components/auth/auth.module.js src/app/components/auth/auth.module.ts src/app/components/auth/basic-auth.service.js src/app/components/auth/basic-auth.service.spec.js src/app/components/auth/basic-auth.service.ts src/app/components/auth/keycloak-auth.service.js src/app/components/auth/keycloak-auth.service.spec.js src/app/components/auth/keycloak-auth.service.ts
diffstat 10 files changed, 352 insertions(+), 227 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/app.module.ts	Thu Oct 19 11:36:12 2017 -0400
+++ b/src/app/app.module.ts	Fri Oct 20 16:16:16 2017 -0400
@@ -39,6 +39,7 @@
 import { UpgradeAdapter } from "@angular/upgrade";
 import { UpgradeModule } from "@angular/upgrade/static";
 
+import { AuthModule } from "components/auth/auth.module";
 import { FiltersModule } from "shared/filters/filters.module";
 import { ServicesModule } from "shared/services/services.module";
 
@@ -51,6 +52,7 @@
     HttpClientModule,
     UpgradeModule,
 
+    AuthModule,
     ServicesModule,
     FiltersModule,
   ],
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/auth/auth-service.interface.ts	Fri Oct 20 16:16:16 2017 -0400
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import { InjectionToken } from "@angular/core";
+
+export interface IAuthService {
+
+  readonly authHeader: string;
+
+  readonly username: string;
+
+  rootScope: ng.IRootScopeService;
+
+  status(): boolean;
+
+  login(user: string, pass: string, onsuccess?: () => void): void;
+
+  goToLogin(promise: angular.IDeferred<{}>): void;
+
+  logout(onsuccess?: () => void): void;
+
+  refresh(): ng.IPromise<{}>;
+
+  getCommandChannelUrl(baseUrl: string): string;
+
+}
+
+const AuthServiceToken: InjectionToken<IAuthService> = new InjectionToken<IAuthService>("AuthService");
+export { AuthServiceToken };
--- a/src/app/components/auth/auth.module.js	Thu Oct 19 11:36:12 2017 -0400
+++ b/src/app/components/auth/auth.module.js	Fri Oct 20 16:16:16 2017 -0400
@@ -27,8 +27,8 @@
 
 import Keycloak from 'keycloak-js/dist/keycloak.js';
 
-import KeycloakAuthService from './keycloak-auth.service.js';
-import BasicAuthService from './basic-auth.service.js';
+import { KeycloakAuthService } from './keycloak-auth.service';
+import { BasicAuthService } from './basic-auth.service';
 
 let MOD_NAME = 'authModule';
 export default MOD_NAME;
@@ -62,7 +62,7 @@
       .success(done)
       .error(() => window.location.reload());
   } else {
-    mod.service('authService', BasicAuthService);
+    mod.service('authService', ['$injector', BasicAuthService]);
     done();
   }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/auth/auth.module.ts	Fri Oct 20 16:16:16 2017 -0400
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import {
+  InjectionToken,
+  NgModule,
+} from "@angular/core";
+
+import {
+  AuthServiceToken,
+  IAuthService,
+} from "./auth-service.interface";
+import { BasicAuthService } from "./basic-auth.service";
+import { KeycloakAuthService } from "./keycloak-auth.service";
+
+const KEYCLOAK_ENABLED = process.env.NODE_ENV === "production";
+/* istanbul ignore next */
+const AUTH_SVC_CLASS = KEYCLOAK_ENABLED ? KeycloakAuthService : BasicAuthService;
+
+@NgModule({
+  providers: [
+    { deps: ["$injector"], provide: AuthServiceToken, useClass: AUTH_SVC_CLASS as any },
+  ],
+})
+export class AuthModule {}
--- a/src/app/components/auth/basic-auth.service.js	Thu Oct 19 11:36:12 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import * as url from 'url';
-
-const SESSION_EXPIRY_MINUTES = 15;
-
-export default class BasicAuthService {
-
-  constructor ($q, $state, localStorage) {
-    'ngInject';
-    this.q = $q;
-    this.$state = $state;
-    this._localStorage = localStorage;
-
-    this._pass = null;
-  }
-
-  set rootScope (rootScope) {
-    this._rootScope = rootScope;
-  }
-
-  status () {
-    return this._sessionIsValid();
-  }
-
-  login (user, pass, success = angular.noop) {
-    this._pass = pass;
-    if (this._rememberUser) {
-      this._localStorage.setItem('username', user);
-    }
-
-    this._localStorage.setItem('loggedInUser', user);
-    this._refreshSession();
-
-    this._rootScope.$broadcast('userLoginChanged');
-    success();
-  }
-
-  goToLogin (promise) {
-    promise.resolve(this.$state.target('login'));
-  }
-
-  logout (callback = angular.noop) {
-    this._pass = null;
-    this.$state.go('login');
-
-    this._localStorage.removeItem('session');
-    this._localStorage.removeItem('loggedInUser');
-
-    this._rootScope.$broadcast('userLoginChanged');
-    callback();
-  }
-
-  _refreshSession () {
-    let now = new Date();
-    let expiry = new Date(now);
-    expiry.setMinutes(now.getMinutes() + SESSION_EXPIRY_MINUTES);
-    this._localStorage.setItem('session', expiry);
-  }
-
-  _sessionIsValid () {
-    if (!this._localStorage.hasItem('session')) {
-      return false;
-    }
-    let session = new Date(this._localStorage.getItem('session'));
-    let now = new Date();
-
-    return session.getTime() >= now.getTime();
-  }
-
-  refresh () {
-    let defer = this.q.defer();
-    if (this._sessionIsValid()) {
-      this._refreshSession();
-      defer.resolve();
-    } else {
-      this._localStorage.removeItem('session');
-      this._localStorage.removeItem('loggedInUser');
-      defer.reject();
-    }
-    return defer.promise;
-  }
-
-  get authHeader () {
-    return 'Basic ' + btoa(this.username + ':' + this._pass);
-  }
-
-  get username () {
-    return this._localStorage.getItem('loggedInUser');
-  }
-
-  get rememberedUsername () {
-    return this._localStorage.getItem('username');
-  }
-
-  getCommandChannelUrl (baseUrl) {
-    let parsed = url.parse(baseUrl);
-    if (this.username == null && this._pass == null) {
-      // no-op
-    }
-    if (this.username != null && this._pass == null) {
-      parsed.auth = this.username;
-    }
-    if (this.username != null && this._pass != null) {
-      parsed.auth = this.username + ':' + this._pass;
-    }
-    return url.format(parsed);
-  }
-
-  rememberUser (remember) {
-    this._rememberUser = remember;
-    if (!remember) {
-      this._localStorage.removeItem('username');
-    }
-  }
-
-}
--- a/src/app/components/auth/basic-auth.service.spec.js	Thu Oct 19 11:36:12 2017 -0400
+++ b/src/app/components/auth/basic-auth.service.spec.js	Fri Oct 20 16:16:16 2017 -0400
@@ -27,7 +27,7 @@
 
 // AuthServices are set up before Angular is bootstrapped, so we manually import rather than
 // using Angular DI
-import BasicAuthService from './basic-auth.service.js';
+import { BasicAuthService } from './basic-auth.service';
 
 describe('BasicAuthService', () => {
 
@@ -61,7 +61,12 @@
       clear: sinon.spy()
     };
     rootScope = { $broadcast: sinon.spy() };
-    basicAuthService = new BasicAuthService(q, state, localStorage);
+
+    let injector = sinon.stub();
+    injector.withArgs('$q').returns(q);
+    injector.withArgs('$state').returns(state);
+    injector.withArgs('localStorage').returns(localStorage);
+    basicAuthService = new BasicAuthService({ get: injector });
     basicAuthService.rootScope = rootScope;
   });
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/auth/basic-auth.service.ts	Fri Oct 20 16:16:16 2017 -0400
@@ -0,0 +1,154 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import * as angular from "angular";
+import * as url from "url";
+import { IAuthService } from "./auth-service.interface";
+
+import { StateService } from "@uirouter/angularjs";
+
+import { LocalStorageService } from "../../shared/services/local-storage.service.js";
+
+const SESSION_EXPIRY_MINUTES = 15;
+
+export class BasicAuthService implements IAuthService {
+
+  private q: ng.IQService;
+  private $state: StateService;
+  private localStorage: LocalStorageService;
+  private pass: string = null;
+  private scope: ng.IRootScopeService;
+  private remember: boolean;
+
+  public constructor($injector: ng.auto.IInjectorService) {
+    this.q = $injector.get("$q");
+    this.$state = $injector.get("$state");
+    this.localStorage = $injector.get("localStorage");
+
+    this.pass = null;
+  }
+
+  public set rootScope(rootScope: ng.IRootScopeService) {
+    this.scope = rootScope;
+  }
+
+  public status(): boolean {
+    return this._sessionIsValid();
+  }
+
+  public login(user: string, pass: string, success = angular.noop): void {
+    this.pass = pass;
+    if (this.remember) {
+      this.localStorage.setItem("username", user);
+    }
+
+    this.localStorage.setItem("loggedInUser", user);
+    this._refreshSession();
+
+    this.scope.$broadcast("userLoginChanged");
+    success();
+  }
+
+  public goToLogin(promise: ng.IDeferred<{}>): void {
+    promise.resolve(this.$state.target("login"));
+  }
+
+  public logout(callback = angular.noop): void {
+    this.pass = null;
+    this.$state.go("login");
+
+    this.localStorage.removeItem("session");
+    this.localStorage.removeItem("loggedInUser");
+
+    this.scope.$broadcast("userLoginChanged");
+    callback();
+  }
+
+  public refresh(): ng.IPromise<{}> {
+    const defer = this.q.defer();
+    if (this._sessionIsValid()) {
+      this._refreshSession();
+      defer.resolve();
+    } else {
+      this.localStorage.removeItem("session");
+      this.localStorage.removeItem("loggedInUser");
+      defer.reject();
+    }
+    return defer.promise;
+  }
+
+  public get authHeader(): string {
+    return "Basic " + btoa(this.username + ":" + this.pass);
+  }
+
+  public get username(): string {
+    return this.localStorage.getItem("loggedInUser");
+  }
+
+  public get rememberedUsername(): string {
+    return this.localStorage.getItem("username");
+  }
+
+  public getCommandChannelUrl(baseUrl: string): string {
+    const parsed = url.parse(baseUrl);
+    if (this.username == null && this.pass == null) {
+      // no-op
+    }
+    if (this.username != null && this.pass == null) {
+      parsed.auth = this.username;
+    }
+    if (this.username != null && this.pass != null) {
+      parsed.auth = this.username + ":" + this.pass;
+    }
+    return url.format(parsed);
+  }
+
+  public rememberUser(rememberUser: boolean): void {
+    this.remember = rememberUser;
+    if (!this.remember) {
+      this.localStorage.removeItem("username");
+    }
+  }
+
+  private _refreshSession(): void {
+    const now = new Date();
+    const expiry = new Date(now);
+    expiry.setMinutes(now.getMinutes() + SESSION_EXPIRY_MINUTES);
+    this.localStorage.setItem("session", expiry);
+  }
+
+  private _sessionIsValid(): boolean {
+    if (!this.localStorage.hasItem("session")) {
+      return false;
+    }
+    const session = new Date(this.localStorage.getItem("session"));
+    const now = new Date();
+
+    return session.getTime() >= now.getTime();
+  }
+
+}
--- a/src/app/components/auth/keycloak-auth.service.js	Thu Oct 19 11:36:12 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import * as url from 'url';
-
-export default class KeycloakAuthService {
-
-  constructor (keycloak) {
-    this.keycloak = keycloak;
-  }
-
-  set rootScope (rootScope) {
-    this._rootScope = rootScope;
-  }
-
-  login () {
-    // no-op
-  }
-
-  goToLogin (promise) {
-    this.keycloak.login();
-    this._rootScope.$broadcast('userLoginChanged');
-    promise.resolve();
-  }
-
-  logout (callback = angular.noop) {
-    this._rootScope.$broadcast('userLoginChanged');
-    this.keycloak.logout();
-    callback();
-  }
-
-  status () {
-    return this.keycloak.authenticated;
-  }
-
-  refresh () {
-    // refresh the token if it expires within 300 seconds
-    return this.keycloak.updateToken(300);
-  }
-
-  get authHeader () {
-    return 'Bearer ' + this.keycloak.token;
-  }
-
-  get username () {
-    return this.keycloak.idTokenParsed.preferred_username;
-  }
-
-  getCommandChannelUrl (baseUrl) {
-    let parsed = url.parse(baseUrl);
-    parsed.query = { access_token: this.keycloak.token };
-    return url.format(parsed);
-  }
-
-}
--- a/src/app/components/auth/keycloak-auth.service.spec.js	Thu Oct 19 11:36:12 2017 -0400
+++ b/src/app/components/auth/keycloak-auth.service.spec.js	Fri Oct 20 16:16:16 2017 -0400
@@ -27,7 +27,7 @@
 
 // AuthServices are set up before Angular is bootstrapped, so we manually import rather than
 // using Angular DI
-import KeycloakAuthService from './keycloak-auth.service.js';
+import { KeycloakAuthService } from './keycloak-auth.service';
 
 describe('KeycloakAuthService', () => {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/auth/keycloak-auth.service.ts	Fri Oct 20 16:16:16 2017 -0400
@@ -0,0 +1,83 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import * as angular from "angular";
+import * as url from "url";
+
+import { IAuthService } from "./auth-service.interface";
+
+export class KeycloakAuthService implements IAuthService {
+
+  private scope: ng.IRootScopeService;
+
+  public constructor(private keycloak: any) {
+  }
+
+  public set rootScope(rootScope: ng.IRootScopeService) {
+    this.scope = rootScope;
+  }
+
+  public login(): void {
+    // no-op
+  }
+
+  public goToLogin(promise: ng.IDeferred<{}>): void {
+    this.keycloak.login();
+    this.scope.$broadcast("userLoginChanged");
+    promise.resolve();
+  }
+
+  public logout(callback = angular.noop): void {
+    this.scope.$broadcast("userLoginChanged");
+    this.keycloak.logout();
+    callback();
+  }
+
+  public status(): boolean {
+    return this.keycloak.authenticated;
+  }
+
+  public refresh(): ng.IPromise<{}> {
+    // refresh the token if it expires within 300 seconds
+    return this.keycloak.updateToken(300);
+  }
+
+  public get authHeader(): string {
+    return "Bearer " + this.keycloak.token;
+  }
+
+  public get username(): string {
+    return this.keycloak.idTokenParsed.preferred_username;
+  }
+
+  public getCommandChannelUrl(baseUrl: string): string {
+    const parsed = url.parse(baseUrl);
+    parsed.query = { access_token: this.keycloak.token };
+    return url.format(parsed);
+  }
+
+}