changeset 168:becd239128c2

Use authService-supplied credentials for command channel requests Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-August/024667.html
author Andrew Azores <aazores@redhat.com>
date Wed, 23 Aug 2017 17:21:50 -0400
parents 9726d75909c1
children 6d744040aa4e
files package.json src/app/components/auth/basic-auth.service.js src/app/components/auth/basic-auth.service.spec.js src/app/components/auth/keycloak-auth.service.js src/app/components/auth/keycloak-auth.service.spec.js src/app/shared/services/command-channel.service.js src/app/shared/services/command-channel.service.spec.js
diffstat 7 files changed, 73 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/package.json	Wed Aug 23 17:21:40 2017 -0400
+++ b/package.json	Wed Aug 23 17:21:50 2017 -0400
@@ -67,6 +67,7 @@
     "should": "^11.2.1",
     "sinon": "^2.1.0",
     "style-loader": "^0.16.1",
+    "url": "^0.11.0",
     "url-join": "^2.0.1",
     "webpack": "^2.6.1",
     "webpack-dev-server": "^2.4.2",
--- a/src/app/components/auth/basic-auth.service.js	Wed Aug 23 17:21:40 2017 -0400
+++ b/src/app/components/auth/basic-auth.service.js	Wed Aug 23 17:21:50 2017 -0400
@@ -25,6 +25,8 @@
  * exception statement from your version.
  */
 
+import * as url from 'url';
+
 export default class BasicAuthService {
 
   constructor ($state) {
@@ -75,4 +77,18 @@
     return this._user;
   }
 
+  getCommandChannelUrl (baseUrl) {
+    let parsed = url.parse(baseUrl);
+    if (this._user == null && this._pass == null) {
+      // no-op
+    }
+    if (this._user != null && this._pass == null) {
+      parsed.auth = this._user;
+    }
+    if (this._user != null && this._pass != null) {
+      parsed.auth = this.username + ':' + this._pass;
+    }
+    return url.format(parsed);
+  }
+
 }
--- a/src/app/components/auth/basic-auth.service.spec.js	Wed Aug 23 17:21:40 2017 -0400
+++ b/src/app/components/auth/basic-auth.service.spec.js	Wed Aug 23 17:21:50 2017 -0400
@@ -136,4 +136,25 @@
       });
     });
   });
+
+  describe('#getCommandChannelUrl()', () => {
+    it('should return provided value if not logged in', () => {
+      let mockUrl = 'http://example.com:1234/';
+      basicAuthService.getCommandChannelUrl(mockUrl).should.equal(mockUrl);
+    });
+
+    it('should only add basic auth username when only username provided', done => {
+      basicAuthService.login('foo', null, () => {
+        basicAuthService.getCommandChannelUrl('http://example.com/').should.equal('http://foo@example.com/');
+        done();
+      });
+    });
+
+    it('should add basic auth username and password when provided', done => {
+      basicAuthService.login('foo', 'bar', () => {
+        basicAuthService.getCommandChannelUrl('http://example.com/').should.equal('http://foo:bar@example.com/');
+        done();
+      });
+    });
+  });
 });
--- a/src/app/components/auth/keycloak-auth.service.js	Wed Aug 23 17:21:40 2017 -0400
+++ b/src/app/components/auth/keycloak-auth.service.js	Wed Aug 23 17:21:50 2017 -0400
@@ -25,6 +25,8 @@
  * exception statement from your version.
  */
 
+import * as url from 'url';
+
 export default class KeycloakAuthService {
 
   constructor (keycloak) {
@@ -57,4 +59,10 @@
     return this.keycloak.idTokenParsed.preferred_username;
   }
 
+  getCommandChannelUrl (baseUrl) {
+    let parsed = url.parse(baseUrl);
+    parsed.query = { token: this.keycloak.token };
+    return url.format(parsed);
+  }
+
 }
--- a/src/app/components/auth/keycloak-auth.service.spec.js	Wed Aug 23 17:21:40 2017 -0400
+++ b/src/app/components/auth/keycloak-auth.service.spec.js	Wed Aug 23 17:21:50 2017 -0400
@@ -99,4 +99,13 @@
       keycloakAuthService.username.should.equal('client');
     });
   });
+
+  describe('#getCommandChannelUrl()', () => {
+    it('should add the Keycloak token to the query', done => {
+      keycloakAuthService.login('foo', 'bar', () => {
+        keycloakAuthService.getCommandChannelUrl('http://example.com/').should.equal('http://example.com/?token=fakeToken');
+        done();
+      });
+    });
+  });
 });
--- a/src/app/shared/services/command-channel.service.js	Wed Aug 23 17:21:40 2017 -0400
+++ b/src/app/shared/services/command-channel.service.js	Wed Aug 23 17:21:50 2017 -0400
@@ -26,20 +26,19 @@
  */
 
 import servicesModule from './services.module.js';
-import urlJoin from 'url-join';
+import * as url from 'url';
 
 const CLIENT_REQUEST_TYPE = 2;
 
 class CommandChannelService {
-  constructor ($q, webSocketFactory, $translate, commandChannelUrl) {
+  constructor ($q, authService, commandChannelUrl, webSocketFactory, $translate) {
     'ngInject';
     this._sequence = 1;
     this.q = $q;
+    this.authService = authService;
+    this.commandChannelUrl = commandChannelUrl;
     this.socketFactory = webSocketFactory;
     this.translate = $translate;
-    this._commandChannelUrl = commandChannelUrl;
-
-    this.setCredentials('bar-client-user', 'client-pwd');
 
     this._responseMessages = {};
     $translate([
@@ -65,29 +64,6 @@
     return Object.freeze(responseCodes);
   }
 
-  setCredentials (username, password) {
-    this.username = username;
-    this.password = password;
-  }
-
-  get commandChannelUrl () {
-    if (!angular.isDefined(this.username) || this.username === '') {
-      return this._commandChannelUrl;
-    }
-    let protocolDelimiterIndex = this._commandChannelUrl.indexOf('://');
-    let protocol = this._commandChannelUrl.substring(0, protocolDelimiterIndex + 3);
-    let url = this._commandChannelUrl.substring(protocol.length);
-
-    let credentials;
-    if (angular.isDefined(this.password) && this.password !== '') {
-      credentials = this.username + ':' + this.password;
-    } else {
-      credentials = this.username;
-    }
-
-    return protocol + credentials + '@' + url;
-  }
-
   get sequence () {
     let val = this._sequence;
     if (val === Number.MAX_SAFE_INTEGER) {
@@ -100,7 +76,11 @@
 
   sendMessage (connectPath, payload = {}) {
     let defer = this.q.defer();
-    let socket = this.socketFactory.createSocket(urlJoin(this.commandChannelUrl, connectPath));
+    let commandChannelUrl = this.authService.getCommandChannelUrl(this.commandChannelUrl);
+    let parsed = url.parse(commandChannelUrl);
+    parsed.pathname = connectPath;
+    commandChannelUrl = url.format(parsed);
+    let socket = this.socketFactory.createSocket(commandChannelUrl);
     if (!socket) {
       this.translate('services.commandChannel.WEBSOCKETS_NOT_SUPPORTED').then(s => defer.reject(s));
       return defer.promise;
--- a/src/app/shared/services/command-channel.service.spec.js	Wed Aug 23 17:21:40 2017 -0400
+++ b/src/app/shared/services/command-channel.service.spec.js	Wed Aug 23 17:21:50 2017 -0400
@@ -30,12 +30,15 @@
 
 describe('CommandChannelService', () => {
 
-  let svc, scope, webSocketFactory, translate;
+  let svc, scope, authService, webSocketFactory, translate;
   beforeEach(() => {
     let addEventListener = sinon.spy();
     let removeEventListener = sinon.spy();
     let send = sinon.spy();
     let close = sinon.spy();
+    authService = {
+      getCommandChannelUrl: sinon.stub().returns('http://foo:bar@example.com:1234/?token=fakeToken')
+    };
     webSocketFactory = {
       addEventListener: addEventListener,
       removeEventListener: removeEventListener,
@@ -65,6 +68,7 @@
     angular.mock.module(servicesModule);
     angular.mock.module($provide => {
       'ngInject';
+      $provide.value('authService', authService);
       $provide.value('webSocketFactory', webSocketFactory);
       $provide.value('$translate', translate);
     });
@@ -72,7 +76,6 @@
       'ngInject';
       scope = $rootScope;
       svc = commandChannelService;
-      svc.setCredentials('', '');
     });
   });
 
@@ -98,7 +101,7 @@
   describe('sendMessage', () => {
     it('should connect to correct command channel URL', () => {
       svc.sendMessage('foo', 'bar');
-      webSocketFactory.createSocket.should.be.calledWith('ws://foo-host:1234/foo');
+      webSocketFactory.createSocket.should.be.calledWith('http://foo:bar@example.com:1234/foo?token=fakeToken');
       scope.$apply();
     });
 
@@ -181,18 +184,9 @@
 
   });
 
-  describe('auth credentials', () => {
-    it('should connect to websocket with basic auth credentials', () => {
-      svc.setCredentials('fooUser', 'fooPass');
-      svc.sendMessage('foo', 'bar');
-      webSocketFactory.createSocket.should.be.calledWith('ws://fooUser:fooPass@foo-host:1234/foo');
-    });
-
-    it('should connect to websocket with basic auth credentials and empty password', () => {
-      svc.setCredentials('fooUser', '');
-      svc.sendMessage('foo', 'bar');
-      webSocketFactory.createSocket.should.be.calledWith('ws://fooUser@foo-host:1234/foo');
-    });
+  it('should connect to websocket with credentials from authService', () => {
+    svc.sendMessage('foo', 'bar');
+    webSocketFactory.createSocket.should.be.calledWith('http://foo:bar@example.com:1234/foo?token=fakeToken');
   });
 
   describe('responseCodes', () => {