mirror of
https://github.com/ansible/awx.git
synced 2026-01-21 14:38:00 -03:30
Merge pull request #454 from jaredevantabor/session_fixes
Session timeout after idle session
This commit is contained in:
commit
4570535477
@ -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();
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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()
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -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 () {
|
||||
|
||||
@ -6,13 +6,17 @@
|
||||
ng-src="/static/assets/{{ customLogo }}" >
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="LoginModal-alert" ng-show="!sessionExpired">
|
||||
<div class="LoginModal-alert" ng-show="!sessionExpired && !sessionLimitExpired">
|
||||
Welcome to Ansible Tower! Please sign in.
|
||||
</div>
|
||||
<div class="LoginModal-alert" ng-show="sessionExpired">
|
||||
Your session timed out due to inactivity. Please
|
||||
sign in.
|
||||
</div>
|
||||
<div class="LoginModal-alert" ng-show="sessionLimitExpired">
|
||||
Maximum per-user sessions reached. Please
|
||||
sign in.
|
||||
</div>
|
||||
<form id="login-form"
|
||||
name="loginForm"
|
||||
class="form-horizontal"
|
||||
|
||||
@ -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'
|
||||
};
|
||||
|
||||
31
awx/ui/client/src/rest/interceptors.service.js
Normal file
31
awx/ui/client/src/rest/interceptors.service.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*************************************************
|
||||
* 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){
|
||||
$AnsibleConfig.session_timeout = Number(config.headers('auth-token-timeout'));
|
||||
}
|
||||
return config;
|
||||
},
|
||||
responseError: function(rejection){
|
||||
if(rejection.data.detail && rejection.data.detail === "Maximum per-user sessions reached"){
|
||||
$rootScope.sessionTimer.expireSession('session_limit');
|
||||
return rejection;
|
||||
}
|
||||
return rejection;
|
||||
}
|
||||
};
|
||||
}];
|
||||
16
awx/ui/client/src/rest/main.js
Normal file
16
awx/ui/client/src/rest/main.js
Normal file
@ -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);
|
||||
@ -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'])
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
];
|
||||
@ -158,7 +158,7 @@ angular.module('SocketIO', ['Utilities'])
|
||||
}
|
||||
else {
|
||||
// encountered expired token, redirect to login page
|
||||
$rootScope.sessionTimer.expireSession();
|
||||
$rootScope.sessionTimer.expireSession('idle');
|
||||
$location.url('/login');
|
||||
}
|
||||
},
|
||||
|
||||
@ -200,7 +200,7 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
|
||||
} else if ((status === 'Token is expired') || (status === 401 && data.detail && data.detail === 'Token is expired') ||
|
||||
(status === 401 && data.detail && data.detail === 'Invalid token')) {
|
||||
if ($rootScope.sessionTimer) {
|
||||
$rootScope.sessionTimer.expireSession();
|
||||
$rootScope.sessionTimer.expireSession('idle');
|
||||
}
|
||||
$location.url('/login');
|
||||
} else if (data.non_field_errors) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -58,6 +58,7 @@
|
||||
|
||||
<!-- Password Dialog -->
|
||||
<div id="password-modal" style="display: none;"></div>
|
||||
<div id="idle-modal" style="display:none">Your session will expire in 1 minute, would you like to continue?</div>
|
||||
|
||||
<!-- Generic Form dialog -->
|
||||
<div id="form-modal" class="modal fade">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user