From 16a8419a90d66fe6e98458317ef08951648067b2 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 7 Mar 2018 11:01:34 +0200 Subject: [PATCH] CHE-9024: fix ability to start a workspace from Dashboard (#9034) * CHE-9024: fix ability to start a workspace from Dashboard Signed-off-by: Oleksii Kurinnyi * fixup! CHE-9024: fix ability to start a workspace from Dashboard --- workspace-loader/src/index.ts | 174 ++++++++++++++++++++++++++------ workspace-loader/webpack.dev.js | 2 +- 2 files changed, 144 insertions(+), 32 deletions(-) diff --git a/workspace-loader/src/index.ts b/workspace-loader/src/index.ts index 6434cc0478..c27c7cfff5 100644 --- a/workspace-loader/src/index.ts +++ b/workspace-loader/src/index.ts @@ -18,13 +18,95 @@ import { Loader } from './loader/loader' const WEBSOCKET_CONTEXT = '/api/websocket'; +declare const Keycloak: Function; +export class KeycloakLoader { + /** + * Load keycloak settings + */ + public loadKeycloakSettings(): Promise { + const msg = "Cannot load keycloak settings. This is normal for single-user mode."; + + return new Promise((resolve, reject) => { + try { + const request = new XMLHttpRequest(); + + request.onerror = request.onabort = function () { + reject(msg); + }; + + request.onload = () => { + if (request.status == 200) { + resolve(this.injectKeycloakScript(JSON.parse(request.responseText))); + } else { + reject(msg); + } + }; + + const url = "/api/keycloak/settings"; + request.open("GET", url, true); + request.send(); + } catch (e) { + reject(msg + e.message); + } + }); + } + + /** + * Injects keycloak javascript + */ + private injectKeycloakScript(keycloakSettings: any): Promise { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.type = 'text/javascript'; + (script as any).language = 'javascript'; + script.async = true; + script.src = keycloakSettings['che.keycloak.auth_server_url'] + '/js/keycloak.js'; + + script.onload = () => { + resolve(this.initKeycloak(keycloakSettings)); + }; + + script.onerror = script.onabort = () => { + reject('Cannot load ' + script.src); + }; + + document.head.appendChild(script); + }); + } + + /** + * Initialize keycloak and load the IDE + */ + private initKeycloak(keycloakSettings: any): Promise { + return new Promise((resolve, reject) => { + const keycloak = Keycloak({ + url: keycloakSettings['che.keycloak.auth_server_url'], + realm: keycloakSettings['che.keycloak.realm'], + clientId: keycloakSettings['che.keycloak.client_id'] + }); + + window['_keycloak'] = keycloak; + + keycloak + .init({onLoad: 'login-required', checkLoginIframe: false}) + .success(() => { + resolve(keycloak); + }) + .error(() => { + reject('[Keycloak] Failed to initialize Keycloak'); + }); + }); + } + +} + export class WorkspaceLoader { - loader: Loader; workspace: che.IWorkspace; startAfterStopping = false; - constructor(loader: Loader) { + constructor(private readonly loader: Loader, + private readonly keycloak: any) { this.loader = loader; /** Ask dashboard to show the IDE. */ @@ -112,38 +194,42 @@ export class WorkspaceLoader { * @param workspaceId workspace id */ getWorkspace(workspaceId: string): Promise { - return new Promise((resolve, reject) => { - let request = new XMLHttpRequest(); - request.open("GET", '/api/workspace/' + workspaceId); - request.send(); - request.onreadystatechange = function () { - if (this.readyState !== 4) { return; } - if (this.status !== 200) { - reject(this.status ? this.statusText : "Unknown error"); - return; - } - resolve(JSON.parse(this.responseText)); - }; - }); + const request = new XMLHttpRequest(); + request.open("GET", '/api/workspace/' + workspaceId); + return this.setAuthorizationHeader(request).then((xhr: XMLHttpRequest) => { + return new Promise((resolve, reject) => { + xhr.send(); + xhr.onreadystatechange = () => { + if (xhr.readyState !== 4) { return; } + if (xhr.status !== 200) { + reject(xhr.status ? xhr.statusText : "Unknown error"); + return; + } + resolve(JSON.parse(xhr.responseText)); + }; + }); + }); } /** * Start current workspace. */ startWorkspace(): Promise { - return new Promise((resolve, reject) => { - let request = new XMLHttpRequest(); - request.open("POST", `/api/workspace/${this.workspace.id}/runtime`); - request.send(); - request.onreadystatechange = function () { - if (this.readyState !== 4) { return; } - if (this.status !== 200) { - reject(this.status ? this.statusText : "Unknown error"); - return; - } - resolve(JSON.parse(this.responseText)); - }; - }); + const request = new XMLHttpRequest(); + request.open("POST", `/api/workspace/${this.workspace.id}/runtime`); + return this.setAuthorizationHeader(request).then((xhr: XMLHttpRequest) => { + return new Promise((resolve, reject) => { + xhr.send(); + xhr.onreadystatechange = () => { + if (xhr.readyState !== 4) { return; } + if (xhr.status !== 200) { + reject(xhr.status ? xhr.statusText : "Unknown error"); + return; + } + resolve(JSON.parse(xhr.responseText)); + }; + }); + }); } /** @@ -192,7 +278,8 @@ export class WorkspaceLoader { subscribeWorkspaceEvents() : Promise { let master = new CheJsonRpcMasterApi(new WebsocketClient()); return new Promise((resolve) => { - master.connect(this.websocketBaseURL() + WEBSOCKET_CONTEXT).then(() => { + const entryPoint = this.websocketBaseURL() + WEBSOCKET_CONTEXT + this.getAuthenticationToken(); + master.connect(entryPoint).then(() => { master.subscribeEnvironmentOutput(this.workspace.id, (message: any) => this.onEnvironmentOutput(message.text)); @@ -239,9 +326,34 @@ export class WorkspaceLoader { }, 1000); } -}; + setAuthorizationHeader(xhr: XMLHttpRequest): Promise { + return new Promise((resolve, reject) => { + if (this.keycloak && this.keycloak.token) { + this.keycloak.updateToken(5).success(() => { + xhr.setRequestHeader('Authorization', 'Bearer ' + this.keycloak.token); + resolve(xhr); + }).error(() => { + console.log('Failed to refresh token'); + this.keycloak.login(); + reject(); + }); + } + + resolve(xhr); + }); + } + + getAuthenticationToken(): string { + return this.keycloak && this.keycloak.token ? '?token=' + this.keycloak.token : ''; + } + +} /** Initialize */ if (document.getElementById('workspace-console')) { - new WorkspaceLoader(new Loader()).load(); + new KeycloakLoader().loadKeycloakSettings().catch((error: any) => { + console.log(error); + }).then((keycloak: any) => { + new WorkspaceLoader(new Loader(), keycloak).load(); + }); } diff --git a/workspace-loader/webpack.dev.js b/workspace-loader/webpack.dev.js index 132b962e64..eaa7f8e72f 100644 --- a/workspace-loader/webpack.dev.js +++ b/workspace-loader/webpack.dev.js @@ -37,7 +37,7 @@ module.exports = merge(common, { target: 'http://localhost:8080', ws: true, }, - '/api/workspace': "http://localhost:8080", + '/api': "http://localhost:8080", } }, plugins:[