1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-02 18:21:12 +03:00

AC-363 token expiration now handled exclusively on server.

This commit is contained in:
chouseknecht 2013-09-06 17:21:05 -04:00
parent 1763d373eb
commit f8e1d2e0e3
13 changed files with 72 additions and 93 deletions

View File

@ -230,8 +230,8 @@ angular.module('ansible', [
otherwise({redirectTo: '/'}); otherwise({redirectTo: '/'});
}]) }])
.run(['$rootScope', 'CheckLicense', '$location', 'Authorization','LoadBasePaths', 'ViewLicense', .run(['$cookieStore', '$rootScope', 'CheckLicense', '$location', 'Authorization','LoadBasePaths', 'ViewLicense',
function($rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense) { function($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense) {
LoadBasePaths(); LoadBasePaths();
@ -239,8 +239,8 @@ angular.module('ansible', [
$rootScope.crumbCache = new Array(); $rootScope.crumbCache = new Array();
$rootScope.$on("$routeChangeStart", function(event, next, current) { $rootScope.$on("$routeChangeStart", function(event, next, current) {
// Evaluate the token on each navigation request. Redirect to login page when not valid // On each navigation request, check that the user is logged in
if (Authorization.isTokenValid() == false) { if (Authorization.isUserLoggedIn() == false) {
if ( next.templateUrl != (urlPrefix + 'partials/login.html') ) { if ( next.templateUrl != (urlPrefix + 'partials/login.html') ) {
$location.path('/login'); $location.path('/login');
} }
@ -262,8 +262,10 @@ angular.module('ansible', [
} }
}); });
if (! Authorization.isTokenValid() ) { if (!Authorization.getToken()) {
// When the app first loads, redirect to login page // When the app first loads, redirect to login page
$rootScope.sessionExpired = false;
$cookieStore.put('sessionExpired', false);
$location.path('/login'); $location.path('/login');
} }

View File

@ -9,10 +9,7 @@
*/ */
var $AnsibleConfig = var $AnsibleConfig =
{ {
session_timeout: 3600, // cookie expiration in seconds. session will expire after this many
// seconds of inactivity.
tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips
debug_mode: true, // Enable console logging messages debug_mode: true, // Enable console logging messages

View File

@ -10,7 +10,7 @@
'use strict'; 'use strict';
function Authenticate($window, $scope, $rootScope, $location, Authorization, ToggleClass, Alert) function Authenticate($cookieStore, $window, $scope, $rootScope, $location, Authorization, ToggleClass, Alert)
{ {
var setLoginFocus = function() { var setLoginFocus = function() {
$('#login-username').focus(); $('#login-username').focus();
@ -18,6 +18,7 @@ function Authenticate($window, $scope, $rootScope, $location, Authorization, Tog
// Display the login dialog // Display the login dialog
$('#login-modal').modal({ show: true, keyboard: false, backdrop: 'static' }); $('#login-modal').modal({ show: true, keyboard: false, backdrop: 'static' });
// Set focus to username field // Set focus to username field
$('#login-modal').on('shown.bs.modal', function() { $('#login-modal').on('shown.bs.modal', function() {
setLoginFocus(); setLoginFocus();
@ -36,18 +37,8 @@ function Authenticate($window, $scope, $rootScope, $location, Authorization, Tog
Authorization.logout(); Authorization.logout();
} }
scope.sessionTimeout = ($AnsibleConfig.session_timeout / 60).toFixed(2); $rootScope.userLoggedIn = false; //hide the logout link. if you got here, you're logged out.
$cookieStore.put('userLoggedIn', false); //gets set back to true by Authorization.setToken().
if ($rootScope.userLoggedIn) {
// If we're logged in, check for session timeout
scope.sessionExpired = Authorization.didSessionExpire();
}
else {
scope.sessionExpired = false;
}
$rootScope.userLoggedIn = false; //hide the logout link. if you got here, you're logged out.
//gets set back to true by Authorization.setToken().
$('#login-password').bind('keypress', function(e) { $('#login-password').bind('keypress', function(e) {
var code = (e.keyCode ? e.keyCode : e.which); var code = (e.keyCode ? e.keyCode : e.which);
@ -73,16 +64,7 @@ function Authenticate($window, $scope, $rootScope, $location, Authorization, Tog
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
$('#login-modal').modal('hide'); $('#login-modal').modal('hide');
token = data.token; token = data.token;
Authorization.setToken(data.token); Authorization.setToken(data.token, data.expires);
scope.reset();
// Force request to /organizations to query with the correct token -in the event a new user
// has logged in.
var today = new Date();
today.setTime(today.getTime() + ($AnsibleConfig.session_timeout * 1000));
$rootScope.token = token;
$rootScope.userLoggedIn = true;
$rootScope.token_expire = today.getTime();
// Get all the profile/access info regarding the logged in user // Get all the profile/access info regarding the logged in user
Authorization.getUser() Authorization.getUser()
@ -126,5 +108,5 @@ function Authenticate($window, $scope, $rootScope, $location, Authorization, Tog
} }
} }
Authenticate.$inject = ['$window', '$scope', '$rootScope', '$location', 'Authorization', 'ToggleClass', 'Alert']; Authenticate.$inject = ['$cookieStore', '$window', '$scope', '$rootScope', '$location', 'Authorization', 'ToggleClass', 'Alert'];

View File

@ -29,6 +29,9 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res
SelectionInit({ scope: scope, list: list, url: url, returnToCaller: 1 }); SelectionInit({ scope: scope, list: list, url: url, returnToCaller: 1 });
if (scope.PostRefreshRemove) {
scope.PostRefreshRemove();
}
scope.PostRefershRemove = scope.$on('PostRefresh', function() { scope.PostRefershRemove = scope.$on('PostRefresh', function() {
// After a refresh, populate the organization name on each row // After a refresh, populate the organization name on each row
for(var i=0; i < scope.credentials.length; i++) { for(var i=0; i < scope.credentials.length; i++) {

View File

@ -176,7 +176,7 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve organization: ' + $routeParams.id + '. GET status: ' + status }); { hdr: 'Error!', msg: 'Failed to retrieve organization: ' + $routeParams.id + '. GET status: ' + status });
}); });
@ -194,7 +194,7 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, OrganizationForm, ProcessErrors(scope, data, status, OrganizationForm,
{ hdr: 'Error!', msg: 'Failed to update organization: ' + id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update organization: ' + id + '. PUT status: ' + status });
}); });
}; };

View File

@ -54,6 +54,7 @@ angular.module('JobTemplateFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
ngClick: 'lookUpInventory()', ngClick: 'lookUpInventory()',
awRequiredWhen: {variable: "inventoryrequired", init: "true" },
column: 1 column: 1
}, },
project: { project: {
@ -64,6 +65,7 @@ angular.module('JobTemplateFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
ngClick: 'lookUpProject()', ngClick: 'lookUpProject()',
awRequiredWhen: {variable: "projectrequired", init: "true" },
column: 1 column: 1
}, },
playbook: { playbook: {
@ -73,6 +75,7 @@ angular.module('JobTemplateFormDefinition', [])
id: 'playbook-select', id: 'playbook-select',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
awRequiredWhen: {variable: "playbookrequired", init: "true" },
column: 1 column: 1
}, },
credential: { credential: {

View File

@ -15,7 +15,7 @@
*/ */
angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities']) angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities'])
.factory('RefreshRelated', ['Alert', 'Rest', function(Alert, Rest) { .factory('RefreshRelated', ['ProcessErrors', 'Rest', function(ProcessErrors, Rest) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
@ -40,7 +40,8 @@ angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities'])
}) })
.error ( function(data, status, headers, config) { .error ( function(data, status, headers, config) {
scope[iterator + 'SearchSpin'] = true; scope[iterator + 'SearchSpin'] = true;
Alert('Error!', 'Failed to retrieve related set: ' + set + '. GET returned status: ' + status); ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
}); });
} }
}]); }]);

View File

@ -15,7 +15,7 @@
*/ */
angular.module('RefreshHelper', ['RestServices', 'Utilities']) angular.module('RefreshHelper', ['RestServices', 'Utilities'])
.factory('Refresh', ['Alert', 'Rest', function(Alert, Rest) { .factory('Refresh', ['ProcessErrors', 'Rest', function(ProcessErrors, Rest) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
@ -37,7 +37,8 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities'])
}) })
.error ( function(data, status, headers, config) { .error ( function(data, status, headers, config) {
scope[iterator + 'SearchSpin'] = false; scope[iterator + 'SearchSpin'] = false;
Alert('Error!', 'Failed to retrieve ' + set + '. GET returned status: ' + status); ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
}); });
} }
}]); }]);

View File

@ -13,11 +13,11 @@ angular.module('JobHostDefinition', [])
name: 'jobhosts', name: 'jobhosts',
iterator: 'jobhost', iterator: 'jobhost',
editTitle: 'Job Host Summary', editTitle: 'Job Host Summary',
index: true, indexShow: 'host_id == null',
hover: true, hover: true,
fields: { fields: {
job: { id: {
label: 'Job ID', label: 'Job ID',
ngClick: "showJob(\{\{ jobhost.job \}\})", ngClick: "showJob(\{\{ jobhost.job \}\})",
columnShow: 'host_id !== null', columnShow: 'host_id !== null',

View File

@ -1,67 +1,45 @@
/********************************************* /*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc. * Copyright (c) 2013 AnsibleWorks, Inc.
* *
* User authentication functions * AuthService.js
* *
* User authentication functions
*/ */
angular.module('AuthService', ['ngCookies']) angular.module('AuthService', ['ngCookies', 'Utilities'])
.factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', function($http, $rootScope, $location, $cookieStore) { .factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath',
function($http, $rootScope, $location, $cookieStore, GetBasePath) {
return { return {
setToken: function(token) { setToken: function(token, expires) {
// set the session cookie // set the session cookie
var today = new Date();
today.setTime(today.getTime() + ($AnsibleConfig.session_timeout * 1000));
$cookieStore.remove('token'); $cookieStore.remove('token');
$cookieStore.remove('token_expire'); $cookieStore.remove('token_expires');
$cookieStore.remove('userLoggedIn');
$cookieStore.put('token', token); $cookieStore.put('token', token);
$cookieStore.put('token_expire', today.getTime()); $cookieStore.put('token_expires', expires);
$cookieStore.put('userLoggedIn', true);
$cookieStore.put('sessionExpired', false);
$rootScope.token = token; $rootScope.token = token;
$rootScope.userLoggedIn = true; $rootScope.userLoggedIn = true;
$rootScope.token_expire = today.getTime(); $rootScope.token_expires = expires;
$rootScope.sessionExpired = false;
}, },
isTokenValid: function() { isUserLoggedIn: function() {
// check if token exists and is not expired if ($rootScope.userLoggedIn == undefined) {
var response = false; // Browser refresh may have occurred
var token = ($rootScope.token) ? $rootScope.token : $cookieStore.get('token'); $rootScope.userLoggedIn = $cookieStore.get('userLoggedIn');
var token_expire = ($rootScope.token_expire) ? $rootScope.token_expire : $cookieStore.get('token_expire'); $rootScope.sessionExpired = $cookieStore.get('sessionExpired');
if (token && token_expire) {
var exp = new Date(token_expire);
var today = new Date();
if (today < exp) {
this.setToken(token); //push expiration into the future while user is active
response = true;
}
} }
return response; return $rootScope.userLoggedIn;
}, },
didSessionExpire: function() {
// use only to test why user was sent to login page.
var response = false;
var token_expire = ($rootScope.token_expire) ? $rootScope.token_expire : $cookieStore.get('token_expire');
if (token_expire) {
var exp = new Date(token_expire);
var today = new Date();
if (exp < today) {
response = true;
}
}
return response;
},
getToken: function() { getToken: function() {
if ( this.isTokenValid() ) { return ($rootScope.token) ? $rootScope.token : $cookieStore.get('token');
return ($rootScope.token) ? $rootScope.token : $cookieStore.get('token'); },
}
else {
$location.path('/login');
}
},
retrieveToken: function(username, password) { retrieveToken: function(username, password) {
return $http({ method: 'POST', url: '/api/v1/authtoken/', return $http({ method: 'POST', url: GetBasePath('authtoken'),
data: {"username": username, "password": password} }); data: {"username": username, "password": password} });
}, },
@ -70,15 +48,17 @@ angular.module('AuthService', ['ngCookies'])
// should prevent content flash from the prior user. // should prevent content flash from the prior user.
var scope = angular.element(document.getElementById('main-view')).scope(); var scope = angular.element(document.getElementById('main-view')).scope();
scope.$destroy(); scope.$destroy();
$rootScope.$destroy(); $rootScope.$destroy();
$cookieStore.remove('accordions'); $cookieStore.remove('accordions');
$cookieStore.remove('token'); $cookieStore.remove('token');
$cookieStore.remove('token_expire'); $cookieStore.remove('token_expire');
$cookieStore.remove('current_user'); $cookieStore.remove('current_user');
$cookieStore.put('userLoggedIn', false);
$cookieStore.put('sessionExpired', false);
$rootScope.current_user = {}; $rootScope.current_user = {};
$rootScope.license_tested = undefined; $rootScope.license_tested = undefined;
$rootScope.userLoggedIn = false; $rootScope.userLoggedIn = false;
$rootScope.sessionExpired = false;
$rootScope.token = null; $rootScope.token = null;
$rootScope.token_expire = new Date(1970, 0, 1, 0, 0, 0, 0); $rootScope.token_expire = new Date(1970, 0, 1, 0, 0, 0, 0);
}, },
@ -86,7 +66,7 @@ angular.module('AuthService', ['ngCookies'])
getLicense: function() { getLicense: function() {
return $http({ return $http({
method: 'GET', method: 'GET',
url: '/api/v1/config/', url: GetBasePath('config'),
headers: { 'Authorization': 'Token ' + this.getToken() } headers: { 'Authorization': 'Token ' + this.getToken() }
}); });
}, },

View File

@ -78,8 +78,14 @@ angular.module('Utilities',[])
} }
}]) }])
.factory('ProcessErrors', ['$log', 'Alert', function($log, Alert) { .factory('ProcessErrors', ['$cookieStore', '$log', '$location', '$rootScope', 'Alert',
function($cookieStore, $log, $location, $rootScope, Alert) {
return function(scope, data, status, form, defaultMsg) { return function(scope, data, status, form, defaultMsg) {
if ($AnsibleConfig.debug_mode && console) {
console.log('Debug status: ' + status);
console.log('Debug data: ');
console.log(data);
}
if (status == 403) { if (status == 403) {
var msg = 'The API responded with a 403 Access Denied error. '; var msg = 'The API responded with a 403 Access Denied error. ';
if (data['detail']) { if (data['detail']) {
@ -90,6 +96,11 @@ angular.module('Utilities',[])
} }
Alert(defaultMsg.hdr, msg); Alert(defaultMsg.hdr, msg);
} }
else if (status == 401 && data.detail && data.detail == 'Token is expired') {
$rootScope.sessionExpired = true;
$cookieStore.put('sessionExpired', true);
$location.path('/login');
}
else if (data.non_field_errors) { else if (data.non_field_errors) {
Alert('Error!', data.non_field_errors); Alert('Error!', data.non_field_errors);
} }
@ -250,4 +261,3 @@ angular.module('Utilities',[])
} }
} }
}]); }]);

View File

@ -25,9 +25,9 @@
<script src="{{ STATIC_URL }}js/awx-min.js"></script> <script src="{{ STATIC_URL }}js/awx-min.js"></script>
{% else %} {% else %}
<script src="{{ STATIC_URL }}js/app.js"></script> <script src="{{ STATIC_URL }}js/app.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/authenticate.js"></script> <script src="{{ STATIC_URL }}lib/ansible/AuthService.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/rest-services.js"></script> <script src="{{ STATIC_URL }}lib/ansible/RestServices.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/utilities.js"></script> <script src="{{ STATIC_URL }}lib/ansible/Utilities.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/form-generator.js"></script> <script src="{{ STATIC_URL }}lib/ansible/form-generator.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/list-generator.js"></script> <script src="{{ STATIC_URL }}lib/ansible/list-generator.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/prompt-dialog.js"></script> <script src="{{ STATIC_URL }}lib/ansible/prompt-dialog.js"></script>