diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 4e598b7be8..36f416a268 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(); @@ -887,7 +887,7 @@ var tower = angular.module('Tower', [ // Listen for job changes and issue callbacks to initiate // DOM updates function openSocket() { - var schedule_socket; + var schedule_socket, control_socket; sock = Socket({ scope: $rootScope, endpoint: "jobs" }); sock.init(); @@ -936,6 +936,16 @@ var tower = angular.module('Tower', [ $log.debug('Schedule ' + data.unified_job_id + ' status changed to ' + data.status); $rootScope.$emit('ScheduleStatusChange', data); }); + + control_socket = Socket({ + scope: $rootScope, + endpoint: "control" + }); + control_socket.init(); + control_socket.on("limit_reached", function(data) { + $log.debug(data.reason); + Timer.expireSession('session_limit'); + }); } openSocket(); @@ -976,13 +986,13 @@ 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')) { - $rootScope.sessionTimer.expireSession(); + if (next.templateUrl !== (urlPrefix + 'login/loginBackDrop.partial.html')) { + $rootScope.sessionTimer.expireSession('idle'); if (sock) { sock.socket.socket.disconnect(); } @@ -1010,6 +1020,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..d5d225b295 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,32 @@ export default } }, - expireSession: function () { + 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 (reason) { + if(reason === 'session_limit'){ + $rootScope.sessionLimitExpired = true; + $rootScope.sessionExpired = false; + } + else if(reason === 'idle'){ + $rootScope.sessionExpired = true; + $rootScope.sessionLimitExpired = false; + } this.sessionTime = 0; - $rootScope.sessionExpired = true; + this.clearTimers(); $cookieStore.put('sessionExpired', true); + transitionTo('signOut'); }, moveForward: function () { @@ -60,6 +82,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('idle'); + }, 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/loginModal/loginModal.partial.html b/awx/ui/client/src/login/loginModal/loginModal.partial.html index 4aadf504fd..8382cce5a5 100644 --- a/awx/ui/client/src/login/loginModal/loginModal.partial.html +++ b/awx/ui/client/src/login/loginModal/loginModal.partial.html @@ -6,13 +6,17 @@ ng-src="/static/assets/{{ customLogo }}" >