diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 4e598b7be8..67f60969e0 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -55,7 +55,8 @@ import {InventoriesList, InventoriesAdd, InventoriesEdit, InventoriesManage} fro import {AdminsList} from './controllers/Admins'; import {UsersList, UsersAdd, UsersEdit} from './controllers/Users'; import {TeamsList, TeamsAdd, TeamsEdit} from './controllers/Teams'; -import './shared/RestServices'; + +import RestServices from './rest/main'; import './shared/api-loader'; import './shared/form-generator'; import './shared/Modal'; @@ -78,7 +79,7 @@ var tower = angular.module('Tower', [ 'ngRoute', 'ngSanitize', 'ngCookies', - 'RestServices', + RestServices.name, routeExtensions.name, browserData.name, breadcrumbs.name, @@ -878,7 +879,6 @@ var tower = angular.module('Tower', [ $rootScope.breadcrumbs = []; $rootScope.crumbCache = []; - $rootScope.sessionTimer = Timer.init(); if ($rootScope.removeOpenSocket) { $rootScope.removeOpenSocket(); @@ -976,12 +976,12 @@ var tower = angular.module('Tower', [ } if (Authorization.isUserLoggedIn() === false) { - if (next.templateUrl !== (urlPrefix + 'partials/login.html')) { + if (next.templateUrl !== (urlPrefix + 'login/loginBackDrop.partial.html')) { $location.path('/login'); } } else if ($rootScope.sessionTimer.isExpired()) { // gets here on timeout - if (next.templateUrl !== (urlPrefix + 'partials/login.html')) { + if (next.templateUrl !== (urlPrefix + 'login/loginBackDrop.partial.html')) { $rootScope.sessionTimer.expireSession(); if (sock) { sock.socket.socket.disconnect(); @@ -1010,6 +1010,7 @@ var tower = angular.module('Tower', [ $rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser'); // when the user refreshes we want to open the socket, except if the user is on the login page, which should happen after the user logs in (see the AuthService module for that call to OpenSocket) if($location.$$url !== '/login'){ + $rootScope.sessionTimer = Timer.init(); $rootScope.$emit('OpenSocket'); pendoService.issuePendoIdentity(); } diff --git a/awx/ui/client/src/config.js b/awx/ui/client/src/config.js index cc68e83a8d..d420608c51 100644 --- a/awx/ui/client/src/config.js +++ b/awx/ui/client/src/config.js @@ -40,10 +40,6 @@ password_hasSymbol: false, // require one of these symbols to be // in the password: -!$%^&*()_+|~=`{}[]:";'<>?,./ - session_timeout: 1800, // Number of seconds before an inactive session is automatically timed out and forced to log in again. - // Separate from time out value set in API. - - variable_edit_modes: { // Options we pass to ControlMirror for editing YAML/JSON variables yaml: { mode:"text/x-yaml", diff --git a/awx/ui/client/src/controllers/JobTemplates.js b/awx/ui/client/src/controllers/JobTemplates.js index b95d1b3bd0..f1dc484dbd 100644 --- a/awx/ui/client/src/controllers/JobTemplates.js +++ b/awx/ui/client/src/controllers/JobTemplates.js @@ -144,7 +144,7 @@ export function JobTemplatesList($scope, $rootScope, $location, $log, $routePara CreateDialog({ - id: 'copy-job-modal' , + id: 'copy-job-modal', title: "Copy", scope: $scope, buttons: buttons, diff --git a/awx/ui/client/src/helpers/LoadConfig.js b/awx/ui/client/src/helpers/LoadConfig.js index 05c4a82233..8c4245697e 100644 --- a/awx/ui/client/src/helpers/LoadConfig.js +++ b/awx/ui/client/src/helpers/LoadConfig.js @@ -46,7 +46,7 @@ angular.module('LoadConfigHelper', ['Utilities']) $rootScope.$emit('ConfigReady'); } - }, function(response) { + }, function() { //local_settings.json not found $log.info('local_settings.json not found'); $rootScope.$emit('ConfigReady'); diff --git a/awx/ui/client/src/login/authenticationServices/authentication.service.js b/awx/ui/client/src/login/authenticationServices/authentication.service.js index ae14be47e5..463cf9679e 100644 --- a/awx/ui/client/src/login/authenticationServices/authentication.service.js +++ b/awx/ui/client/src/login/authenticationServices/authentication.service.js @@ -94,6 +94,8 @@ export default $rootScope.token_expires = null; $rootScope.login_username = null; $rootScope.login_password = null; + clearTimeout($rootScope.idleTimer); + clearTimeout($rootScope.endTimer); }, getLicense: function () { @@ -109,7 +111,7 @@ export default setLicense: function (data) { var license = data.license_info; - license.analytics_status = data.analytics_status; + license.analytics_status = data.analytics_status; license.version = data.version; license.tested = false; Store('license', license); @@ -138,7 +140,8 @@ export default method: 'GET', url: '/api/v1/me/', headers: { - 'Authorization': 'Token ' + this.getToken() + 'Authorization': 'Token ' + this.getToken(), + "X-Auth-Token": 'Token ' + this.getToken() } }); }, diff --git a/awx/ui/client/src/login/authenticationServices/timer.factory.js b/awx/ui/client/src/login/authenticationServices/timer.factory.js index 481d6a899c..51efe4078d 100644 --- a/awx/ui/client/src/login/authenticationServices/timer.factory.js +++ b/awx/ui/client/src/login/authenticationServices/timer.factory.js @@ -22,8 +22,8 @@ * @description */ export default - ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty', - function ($rootScope, $cookieStore) { + ['$rootScope', '$cookieStore', 'transitionTo', 'CreateDialog', 'Authorization', + function ($rootScope, $cookieStore, transitionTo, CreateDialog, Authorization) { return { sessionTime: null, @@ -46,10 +46,25 @@ export default } }, + isIdle: function() { + var stime = this.getSessionTime()/1000, + now = new Date().getTime()/1000, + diff = stime-now; + + if(diff < 61){ + return true; + } + else { + return false; + } + }, + expireSession: function () { this.sessionTime = 0; $rootScope.sessionExpired = true; + this.clearTimers(); $cookieStore.put('sessionExpired', true); + transitionTo('signOut'); }, moveForward: function () { @@ -60,6 +75,70 @@ export default $cookieStore.put('sessionTime', t); $rootScope.sessionExpired = false; $cookieStore.put('sessionExpired', false); + + this.startTimers(); + }, + + startTimers: function() { + var that = this, + tm = ($AnsibleConfig) ? $AnsibleConfig.session_timeout : 1800, + t = tm - 60; + + this.clearTimers(); + + // make a timeout that will go off in 30 mins to log them out + // unless they extend their time + $rootScope.endTimer = setTimeout(function(){ + that.expireSession(); + }, tm * 1000); + + // notify the user a minute before the end of their session that + // their session is about to expire + if($rootScope.idleTimer){ + clearTimeout($rootScope.idleTimer); + } + $rootScope.idleTimer = setTimeout(function() { + if(that.isIdle() === true){ + var buttons = [{ + "label": "Continue", + "onClick": function() { + // make a rest call here to force the API to + // move the session time forward + Authorization.getUser(); + that.moveForward(); + $(this).dialog('close'); + + }, + "class": "btn btn-primary", + "id": "idle-modal-button" + }]; + + if ($rootScope.removeIdleDialogReady) { + $rootScope.removeIdleDialogReady(); + } + $rootScope.removeIdleDialogReady = $rootScope.$on('IdleDialogReady', function() { + $('#idle-modal').show(); + $('#idle-modal').dialog('open'); + }); + CreateDialog({ + id: 'idle-modal' , + title: "Idle Session", + scope: $rootScope, + buttons: buttons, + width: 470, + height: 240, + minWidth: 200, + callback: 'IdleDialogReady' + }); + } + }, t * 1000); + + + }, + + clearTimers: function(){ + clearTimeout($rootScope.idleTimer); + clearTimeout($rootScope.endTimer); }, init: function () { diff --git a/awx/ui/client/src/login/logout.route.js b/awx/ui/client/src/login/logout.route.js index 942a436a04..86f4be8f29 100644 --- a/awx/ui/client/src/login/logout.route.js +++ b/awx/ui/client/src/login/logout.route.js @@ -13,10 +13,5 @@ export default { Authorization.logout(); $location.path('/login'); }], - templateUrl: '/static/partials/blank.html', - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } + templateUrl: '/static/partials/blank.html' }; diff --git a/awx/ui/client/src/rest/interceptors.service.js b/awx/ui/client/src/rest/interceptors.service.js new file mode 100644 index 0000000000..3943ec0a5d --- /dev/null +++ b/awx/ui/client/src/rest/interceptors.service.js @@ -0,0 +1,26 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + /************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$rootScope', + function ($rootScope) { + return { + response: function(config) { + if(config.headers('auth-token-timeout') !== null){ + // $rootScope.sessionTimer = Number(config.headers('auth-token-timeout')); + $AnsibleConfig.session_timeout = Number(config.headers('auth-token-timeout')); + // $rootScope.sessionTimer = Timer.init(); + } + return config; + } + }; + }]; diff --git a/awx/ui/client/src/rest/main.js b/awx/ui/client/src/rest/main.js new file mode 100644 index 0000000000..335106af5d --- /dev/null +++ b/awx/ui/client/src/rest/main.js @@ -0,0 +1,16 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import restServicesFactory from './restServices.factory'; +import interceptors from './interceptors.service'; + +export default + angular.module('RestServices', []) + .config(['$httpProvider', function($httpProvider) { + $httpProvider.interceptors.push('RestInterceptor'); + }]) + .factory('Rest', restServicesFactory) + .service('RestInterceptor', interceptors); diff --git a/awx/ui/client/src/shared/RestServices.js b/awx/ui/client/src/rest/restServices.factory.js similarity index 98% rename from awx/ui/client/src/shared/RestServices.js rename to awx/ui/client/src/rest/restServices.factory.js index b038232ef3..0d03c7ef9e 100644 --- a/awx/ui/client/src/shared/RestServices.js +++ b/awx/ui/client/src/rest/restServices.factory.js @@ -55,8 +55,7 @@ */ export default -angular.module('RestServices', ['ngCookies']) - .factory('Rest', ['$http', '$rootScope', '$cookieStore', '$q', 'Authorization', + ['$http', '$rootScope', '$cookieStore', '$q', 'Authorization', function ($http, $rootScope, $cookieStore, $q, Authorization) { return { @@ -269,4 +268,4 @@ angular.module('RestServices', ['ngCookies']) } }; } - ]); + ]; diff --git a/awx/ui/client/tests/directives/job-status-graph-test.js b/awx/ui/client/tests/directives/job-status-graph-test.js index d1f816a7ee..b2a29f68d7 100644 --- a/awx/ui/client/tests/directives/job-status-graph-test.js +++ b/awx/ui/client/tests/directives/job-status-graph-test.js @@ -4,7 +4,7 @@ import '../support/node'; import {describeModule} from '../support/describe-module'; import 'shared/Utilities'; -import 'shared/RestServices'; + import JobStatusGraph from 'dashboard/graphs/job-status/main'; var resizeHandler = sinon.spy(); diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 637cc36312..c796f29527 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -58,6 +58,7 @@
+