Mercurial > hg > thermostat-ng > web-client
changeset 178:4c0492488b9c
Remember user session when using basic auth
Reviewed-by: jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/024806.html
author | Andrew Azores <aazores@redhat.com> |
---|---|
date | Fri, 01 Sep 2017 14:17:02 -0400 |
parents | 97e433c06ff8 |
children | 8f116d9b0130 |
files | src/app/app.controller.js src/app/app.controller.spec.js src/app/app.routing.js src/app/app.routing.spec.js src/app/components/auth/basic-auth.service.js src/app/components/auth/basic-auth.service.spec.js |
diffstat | 6 files changed, 92 insertions(+), 40 deletions(-) [+] |
line wrap: on
line diff
--- a/src/app/app.controller.js Thu Aug 31 14:54:11 2017 -0400 +++ b/src/app/app.controller.js Fri Sep 01 14:17:02 2017 -0400 @@ -31,6 +31,9 @@ constructor ($scope, environment, $state, authService) { 'ngInject'; + this._scope = $scope; + this._authService = authService; + angular.element('logoutButton').removeAttr('hidden'); if (environment !== 'production') { $scope.env = environment; @@ -41,7 +44,12 @@ $scope.logout = () => authService.logout(); - $scope.$on('userLoginChanged', () => $scope.username = authService.username); + $scope.$on('userLoginChanged', () => this.updateUsernameLabel()); + this.updateUsernameLabel(); + } + + updateUsernameLabel () { + this._scope.username = this._authService.username; } }
--- a/src/app/app.controller.spec.js Thu Aug 31 14:54:11 2017 -0400 +++ b/src/app/app.controller.spec.js Fri Sep 01 14:17:02 2017 -0400 @@ -134,8 +134,13 @@ }); })); + it('should be set on init', () => { + scope.should.have.ownProperty('username'); + scope.username.should.equal(authService.username); + }); + it('should be set on userLoginChanged according to authService username', () => { - scope.should.not.have.ownProperty('username'); + authService.username = 'new-username'; rootScope.$broadcast('userLoginChanged'); scope.should.have.ownProperty('username'); scope.username.should.equal(authService.username);
--- a/src/app/app.routing.js Thu Aug 31 14:54:11 2017 -0400 +++ b/src/app/app.routing.js Fri Sep 01 14:17:02 2017 -0400 @@ -56,21 +56,18 @@ function transitionHook ($q, $transitions, $state, authService) { 'ngInject'; - $transitions.onBefore({ to: '/' }, () => { - return $state.target('landing'); - }); + $transitions.onBefore({ to: '/' }, () => $state.target('landing')); + + $transitions.onEnter({}, () => { authService.refresh() }); - $transitions.onBefore({ to: state => { - return state.name !== 'about' && state.name !== 'login' && !authService.status(); - }}, () => { - let defer = $q.defer(); - authService.refresh() - .then(() => defer.resolve(), - () => { - authService.goToLogin(defer); - }); - return defer.promise; - }); + $transitions.onBefore( + { to: state => state.name !== 'about' && state.name !== 'login' && !authService.status() }, + () => { + let defer = $q.defer(); + authService.refresh().then(() => defer.resolve(), () => authService.goToLogin(defer)); + return defer.promise; + } + ); } appRouter.run(transitionHook); export default appRouter.name;
--- a/src/app/app.routing.spec.js Thu Aug 31 14:54:11 2017 -0400 +++ b/src/app/app.routing.spec.js Fri Sep 01 14:17:02 2017 -0400 @@ -56,7 +56,8 @@ goToLogin: sinon.spy() }; transitions = { - onBefore: sinon.spy() + onBefore: sinon.spy(), + onEnter: sinon.spy() }; module.errorRouting(stateProvider, urlRouterProvider); @@ -107,11 +108,20 @@ }); describe('state change hook', () => { - it('should only be on state change start', () => { - transitions.onBefore.should.be.calledTwice(); + + describe('onEnter hook', () => { + it('should perform authService refresh on all transitions', () => { + authSvc.refresh.should.not.be.called(); + let args = transitions.onEnter.args[0]; + args[0].should.be.an.Object(); + args[0].should.deepEqual({}); + args[1].should.be.a.Function(); + args[1](); + authSvc.refresh.should.be.calledOnce(); + }); }); - describe('first hook', () => { + describe('first onBefore hook', () => { it('should match root transitions', () => { transitions.onBefore.args[0][0].should.have.ownProperty('to'); transitions.onBefore.args[0][0].to.should.equal('/'); @@ -126,7 +136,7 @@ }); }); - describe('second hook', () => { + describe('second onBefore hook', () => { it('should match non-login transitions', () => { transitions.onBefore.args[1][0].should.have.ownProperty('to'); let fn = transitions.onBefore.args[1][0].to;
--- a/src/app/components/auth/basic-auth.service.js Thu Aug 31 14:54:11 2017 -0400 +++ b/src/app/components/auth/basic-auth.service.js Fri Sep 01 14:17:02 2017 -0400 @@ -34,9 +34,7 @@ this.q = $q; this.$state = $state; this.cookies = $cookies; - this.state = false; - this._user = null; this._pass = null; } @@ -45,16 +43,18 @@ } status () { - return this.state; + return angular.isDefined(this.cookies.get('session')); } login (user, pass, success = angular.noop) { - this._user = user; this._pass = pass; - this.state = true; if (this._rememberUser) { this.cookies.put('username', user); } + + this._refreshSession(); + this.cookies.put('loggedInUser', user); + this._rootScope.$broadcast('userLoginChanged'); success(); } @@ -64,30 +64,43 @@ } logout (callback = angular.noop) { - this._user = null; this._pass = null; - this.state = false; this.$state.go('login'); + + this.cookies.remove('session'); + this.cookies.remove('loggedInUser'); + this._rootScope.$broadcast('userLoginChanged'); callback(); } + _refreshSession () { + let now = new Date(); + let expiry = new Date(now); + expiry.setMinutes(now.getMinutes() + 15); + this.cookies.put('session', true, { expires: expiry }); + } + refresh () { let defer = this.q.defer(); - if (this.state) { + let session = this.cookies.get('session'); + if (session) { + this._refreshSession(); defer.resolve(); } else { + this.cookies.remove('session'); + this.cookies.remove('loggedInUser'); defer.reject(); } return defer.promise; } get authHeader () { - return 'Basic ' + btoa(this._user + ':' + this._pass); + return 'Basic ' + btoa(this.username + ':' + this._pass); } get username () { - return this._user; + return this.cookies.get('loggedInUser'); } get rememberedUsername () { @@ -96,13 +109,13 @@ getCommandChannelUrl (baseUrl) { let parsed = url.parse(baseUrl); - if (this._user == null && this._pass == null) { + if (this.username == null && this._pass == null) { // no-op } - if (this._user != null && this._pass == null) { - parsed.auth = this._user; + if (this.username != null && this._pass == null) { + parsed.auth = this.username; } - if (this._user != null && this._pass != null) { + if (this.username != null && this._pass != null) { parsed.auth = this.username + ':' + this._pass; } return url.format(parsed);
--- a/src/app/components/auth/basic-auth.service.spec.js Thu Aug 31 14:54:11 2017 -0400 +++ b/src/app/components/auth/basic-auth.service.spec.js Fri Sep 01 14:17:02 2017 -0400 @@ -63,14 +63,18 @@ describe('#login()', () => { it('should set logged in status on successful login', done => { basicAuthService.login('client', 'client-pwd', () => { + cookies.put.should.be.calledWith('session', true, sinon.match.object); + cookies.get.withArgs('session').returns(true); basicAuthService.status().should.equal(true); done(); }); }); it('should set username on successful login', done => { - should(basicAuthService.username).be.null(); + should(basicAuthService.username).be.undefined(); basicAuthService.login('client', 'client-pwd', () => { + cookies.put.should.be.calledWith('loggedInUser', 'client'); + cookies.get.withArgs('loggedInUser').returns('client'); basicAuthService.username.should.equal('client'); done(); }); @@ -78,7 +82,8 @@ it('should not store username in cookies by default', done => { basicAuthService.login('client', 'client-pwd', () => { - cookies.put.should.not.be.called(); + cookies.put.should.be.called(); + cookies.put.should.not.be.calledWith('username'); done(); }); }); @@ -86,8 +91,10 @@ it('should store username in cookies when set', done => { basicAuthService.rememberUser(true); basicAuthService.login('client', 'client-pwd', () => { - cookies.put.should.be.calledOnce(); + cookies.put.should.be.calledThrice(); + cookies.put.should.be.calledWith('loggedInUser', 'client'); cookies.put.should.be.calledWith('username', 'client'); + cookies.put.should.be.calledWith('session', true, sinon.match.object); done(); }); }); @@ -114,8 +121,12 @@ describe('#logout()', () => { it('should set logged out status', done => { basicAuthService.login('client', 'client-pwd'); + cookies.put.should.be.calledWith('session', true, sinon.match.object); + cookies.get.withArgs('session').returns(true); basicAuthService.status().should.equal(true); basicAuthService.logout(() => { + cookies.remove.should.be.calledWith('session'); + cookies.get.withArgs('session').returns(undefined); basicAuthService.status().should.equal(false); done(); }); @@ -156,6 +167,8 @@ it('should call success handler if logged in', done => { basicAuthService.login('foo', 'bar', () => { + cookies.put.should.be.calledWith('session', true, sinon.match.object); + cookies.get.withArgs('session').returns(true); basicAuthService.refresh().then(done, angular.noop); }); }); @@ -163,6 +176,8 @@ it('should call error handler if logged out', done => { qPromise.callsArg(1); basicAuthService.logout(); + cookies.remove.should.be.calledWith('session'); + cookies.get.withArgs('session').returns(false); basicAuthService.refresh().then(angular.noop, done); }); }); @@ -170,6 +185,7 @@ describe('#get authHeader()', () => { it('should return base64-encoded credentials', done => { basicAuthService.login('foo', 'bar', () => { + cookies.get.withArgs('loggedInUser').returns('foo'); basicAuthService.authHeader.should.equal('Basic ' + btoa('foo:bar')); done(); }); @@ -177,12 +193,13 @@ }); describe('#get username()', () => { - it('should be null before login', () => { - should(basicAuthService.username).be.null(); + it('should be undefined before login', () => { + should(basicAuthService.username).be.undefined(); }); it('should return logged in user', done => { basicAuthService.login('foo', 'bar', () => { + cookies.get.withArgs('loggedInUser').returns('foo'); basicAuthService.username.should.equal('foo'); done(); }); @@ -206,6 +223,7 @@ it('should only add basic auth username when only username provided', done => { basicAuthService.login('foo', null, () => { + cookies.get.withArgs('loggedInUser').returns('foo'); basicAuthService.getCommandChannelUrl('http://example.com/').should.equal('http://foo@example.com/'); done(); }); @@ -213,6 +231,7 @@ it('should add basic auth username and password when provided', done => { basicAuthService.login('foo', 'bar', () => { + cookies.get.withArgs('loggedInUser').returns('foo'); basicAuthService.getCommandChannelUrl('http://example.com/').should.equal('http://foo:bar@example.com/'); done(); });