From b58bff4686d834454ede2a967619fb04ac689e0e Mon Sep 17 00:00:00 2001 From: Graham Mainwaring Date: Tue, 10 Dec 2019 13:09:46 -0500 Subject: [PATCH 01/12] Add setting for configurable login redirect URL --- awx/api/conf.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/awx/api/conf.py b/awx/api/conf.py index 688aad162f..b2cb2a641c 100644 --- a/awx/api/conf.py +++ b/awx/api/conf.py @@ -62,3 +62,14 @@ register( category=_('Authentication'), category_slug='authentication', ) +register( + 'LOGIN_REDIRECT_OVERRIDE', + field_class=fields.CharField, + allow_blank=True, + required=False, + label=_('Login redirect override URL'), + help_text=_('URL to which unauthorized users will be redirected to log in. ' + 'If blank, users will be sent to the Tower login page.'), + category=_('Authentication'), + category_slug='authentication', +) From 2569ec4f4fa6373be993ae0a17bae15ea2592a05 Mon Sep 17 00:00:00 2001 From: Graham Mainwaring Date: Tue, 10 Dec 2019 14:57:03 -0500 Subject: [PATCH 02/12] Add default for LOGIN_REDIRECT_OVERRIDE --- awx/settings/defaults.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 617ac2e440..ed3a4dfdcb 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -379,6 +379,10 @@ TACACSPLUS_AUTH_PROTOCOL = 'ascii' # Note: This setting may be overridden by database settings. AUTH_BASIC_ENABLED = True +# If set, specifies a URL that unauthenticated users will be redirected to +# when trying to access a UI page that requries authentication. +LOGIN_REDIRECT_OVERRIDE = None + # If set, serve only minified JS for UI. USE_MINIFIED_JS = False From f94438cf9b10e734a25fbaa4d86d172249f70b41 Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 10 Dec 2019 15:07:12 -0500 Subject: [PATCH 03/12] Adds login redirect override field to the System (Misc System) Settings interface --- .../forms/system-form/sub-forms/system-misc.form.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/awx/ui/client/src/configuration/forms/system-form/sub-forms/system-misc.form.js b/awx/ui/client/src/configuration/forms/system-form/sub-forms/system-misc.form.js index b283107729..22f2b8bdc0 100644 --- a/awx/ui/client/src/configuration/forms/system-form/sub-forms/system-misc.form.js +++ b/awx/ui/client/src/configuration/forms/system-form/sub-forms/system-misc.form.js @@ -43,6 +43,10 @@ export default ['i18n', function(i18n) { ALLOW_OAUTH2_FOR_EXTERNAL_USERS: { type: 'toggleSwitch', }, + LOGIN_REDIRECT_OVERRIDE: { + type: 'text', + reset: 'LOGIN_REDIRECT_OVERRIDE' + }, ACCESS_TOKEN_EXPIRE_SECONDS: { type: 'text', reset: 'ACCESS_TOKEN_EXPIRE_SECONDS' From ab2f212b044560492d2ca343ecdff79bd3d71f87 Mon Sep 17 00:00:00 2001 From: Graham Mainwaring Date: Tue, 10 Dec 2019 15:29:21 -0500 Subject: [PATCH 04/12] Add /login convenience URL --- awx/main/views.py | 5 ++++- awx/urls.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/awx/main/views.py b/awx/main/views.py index 1947bd001c..bc791976db 100644 --- a/awx/main/views.py +++ b/awx/main/views.py @@ -4,7 +4,7 @@ import json # Django -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render from django.utils.html import format_html from django.utils.translation import ugettext_lazy as _ @@ -97,3 +97,6 @@ def handle_csp_violation(request): logger = logging.getLogger('awx') logger.error(json.loads(request.body)) return HttpResponse(content=None) + +def handle_login_redirect(request): + return HttpResponseRedirect("/#/login") diff --git a/awx/urls.py b/awx/urls.py index 970047151d..ba0f0ee421 100644 --- a/awx/urls.py +++ b/awx/urls.py @@ -9,6 +9,7 @@ from awx.main.views import ( handle_404, handle_500, handle_csp_violation, + handle_login_redirect, ) @@ -22,6 +23,7 @@ urlpatterns = [ url(r'^(?:api/)?404.html$', handle_404), url(r'^(?:api/)?500.html$', handle_500), url(r'^csp-violation/', handle_csp_violation), + url(r'^login/', handle_login_redirect), ] if settings.SETTINGS_MODULE == 'awx.settings.development': From 732da522393c53929cd137e37fb034d8b25e7933 Mon Sep 17 00:00:00 2001 From: Graham Mainwaring Date: Tue, 10 Dec 2019 15:46:56 -0500 Subject: [PATCH 05/12] Expose login redirect URL in unauthenticated /api view --- awx/api/views/root.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/api/views/root.py b/awx/api/views/root.py index 91f6b62149..e4e0657652 100644 --- a/awx/api/views/root.py +++ b/awx/api/views/root.py @@ -60,6 +60,7 @@ class ApiRootView(APIView): data['oauth2'] = drf_reverse('api:oauth_authorization_root_view') data['custom_logo'] = settings.CUSTOM_LOGO data['custom_login_info'] = settings.CUSTOM_LOGIN_INFO + data['login_redirect_override'] = settings.LOGIN_REDIRECT_OVERRIDE return Response(data) From d899e75ad7c223d6ec9302f3a9069cf3f5874af4 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 11 Dec 2019 09:19:38 -0500 Subject: [PATCH 06/12] Adds logic to redirect unauthenticated user if LOGIN_REDIRECT_OVERRIDE is set as long as the user is not navigating to /login or /#/login. Also redirects on logout if LOGIN_REDIRECT_OVERRIDE is set. --- awx/ui/client/src/app.js | 14 ++++++++++++-- awx/ui/client/src/login/logout.route.js | 6 +++++- .../src/shared/load-config/load-config.factory.js | 10 ++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 54e05c4a1e..e6902339e2 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -375,7 +375,13 @@ angular if (!/^\/(login|logout)/.test($location.path())) { $rootScope.preAuthUrl = $location.path(); } - $location.path('/login'); + if ($location.path() !== '/login') { + if (global.$AnsibleConfig.login_redirect_override) { + window.location.replace(global.$AnsibleConfig.login_redirect_override); + } else { + $location.path('/login'); + } + } } else { var lastUser = $cookies.getObject('current_user'), timestammp = Store('sessionTime'); @@ -383,7 +389,11 @@ angular var stime = timestammp[lastUser.id].time, now = new Date().getTime(); if ((stime - now) <= 0) { - $location.path('/login'); + if (global.$AnsibleConfig.login_redirect_override) { + window.location.replace(global.$AnsibleConfig.login_redirect_override); + } else { + $location.path('/login'); + } } } // If browser refresh, set the user_is_superuser value diff --git a/awx/ui/client/src/login/logout.route.js b/awx/ui/client/src/login/logout.route.js index 47da767ec5..cf5c228a30 100644 --- a/awx/ui/client/src/login/logout.route.js +++ b/awx/ui/client/src/login/logout.route.js @@ -11,7 +11,11 @@ export default { route: '/logout', controller: ['Authorization', '$state', function(Authorization, $state) { Authorization.logout().then( () =>{ - $state.go('signIn'); + if (global.$AnsibleConfig.login_redirect_override) { + window.location.replace(global.$AnsibleConfig.login_redirect_override); + } else { + $state.go('signIn'); + } }); }], diff --git a/awx/ui/client/src/shared/load-config/load-config.factory.js b/awx/ui/client/src/shared/load-config/load-config.factory.js index ca12f1739b..38a2d8b3b7 100644 --- a/awx/ui/client/src/shared/load-config/load-config.factory.js +++ b/awx/ui/client/src/shared/load-config/load-config.factory.js @@ -2,7 +2,6 @@ export default function LoadConfig($log, $rootScope, $http, Store) { return function() { - var configSettings = {}; var configInit = function() { @@ -10,12 +9,11 @@ export default if ($rootScope.loginConfig) { $rootScope.loginConfig.resolve('config loaded'); } + global.$AnsibleConfig = configSettings; + Store('AnsibleConfig', global.$AnsibleConfig); $rootScope.$emit('ConfigReady'); // Load new hardcoded settings from above - - global.$AnsibleConfig = configSettings; - Store('AnsibleConfig', global.$AnsibleConfig); $rootScope.$emit('LoadConfig'); }; @@ -39,6 +37,10 @@ export default configSettings.custom_login_info = false; } + if (data.login_redirect_override) { + configSettings.login_redirect_override = data.login_redirect_override; + } + configInit(); }).catch(({error}) => { From 25cc34188870af55e0e31f6ba42d4240df3cf253 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 11 Dec 2019 10:56:46 -0500 Subject: [PATCH 07/12] Reverts changes to logout logic. We don't want to redirect to an override url if the user explicitly logs out. --- awx/ui/client/src/login/logout.route.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/awx/ui/client/src/login/logout.route.js b/awx/ui/client/src/login/logout.route.js index cf5c228a30..47da767ec5 100644 --- a/awx/ui/client/src/login/logout.route.js +++ b/awx/ui/client/src/login/logout.route.js @@ -11,11 +11,7 @@ export default { route: '/logout', controller: ['Authorization', '$state', function(Authorization, $state) { Authorization.logout().then( () =>{ - if (global.$AnsibleConfig.login_redirect_override) { - window.location.replace(global.$AnsibleConfig.login_redirect_override); - } else { - $state.go('signIn'); - } + $state.go('signIn'); }); }], From bf6c16197ca3fec33d8363ebb5866a586eda6e01 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 11 Dec 2019 11:19:55 -0500 Subject: [PATCH 08/12] Moves config request out to block of code that gets executed before the app is bootstrapped. This should allow us to redirect to the override url before the app begins to render, improving the UX. --- awx/ui/client/src/app.js | 8 +-- awx/ui/client/src/app.start.js | 23 ++++++- .../shared/load-config/load-config.factory.js | 69 +++++++------------ 3 files changed, 49 insertions(+), 51 deletions(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index e6902339e2..071db41067 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -375,13 +375,7 @@ angular if (!/^\/(login|logout)/.test($location.path())) { $rootScope.preAuthUrl = $location.path(); } - if ($location.path() !== '/login') { - if (global.$AnsibleConfig.login_redirect_override) { - window.location.replace(global.$AnsibleConfig.login_redirect_override); - } else { - $location.path('/login'); - } - } + $location.path('/login'); } else { var lastUser = $cookies.getObject('current_user'), timestammp = Store('sessionTime'); diff --git a/awx/ui/client/src/app.start.js b/awx/ui/client/src/app.start.js index 44f8113567..61c8ac5bb4 100644 --- a/awx/ui/client/src/app.start.js +++ b/awx/ui/client/src/app.start.js @@ -15,7 +15,9 @@ function bootstrap (callback) { angular.module('I18N').constant('LOCALE', locale); } - angular.element(document).ready(() => callback()); + fetchConfig((config) => { + angular.element(document).ready(() => callback()); + }); }); } @@ -49,6 +51,25 @@ function fetchLocaleStrings (callback) { request.fail(() => callback({ code: DEFAULT_LOCALE })); } +function fetchConfig (callback) { + const request = $.ajax(`/api`); + + request.done(res => { + angular.module('awApp').constant('ConfigSettings', res); + if (res.login_redirect_override) { + if (!document.cookie.split(';').filter((item) => item.includes('userLoggedIn=true')).length && !window.location.href.includes('/#/login')) { + window.location.replace(res.login_redirect_override); + } else { + callback(); + } + } else { + callback(); + } + }); + + request.fail(() => callback()); +} + /** * Grabs the language off of navigator for browser compatibility. * If the language isn't set, then it falls back to the DEFAULT_LOCALE. The diff --git a/awx/ui/client/src/shared/load-config/load-config.factory.js b/awx/ui/client/src/shared/load-config/load-config.factory.js index 38a2d8b3b7..c1adfeee8f 100644 --- a/awx/ui/client/src/shared/load-config/load-config.factory.js +++ b/awx/ui/client/src/shared/load-config/load-config.factory.js @@ -1,57 +1,40 @@ export default - function LoadConfig($log, $rootScope, $http, Store) { + function LoadConfig($rootScope, Store, ConfigSettings) { return function() { var configSettings = {}; - var configInit = function() { - // Auto-resolving what used to be found when attempting to load local_setting.json - if ($rootScope.loginConfig) { - $rootScope.loginConfig.resolve('config loaded'); - } - global.$AnsibleConfig = configSettings; - Store('AnsibleConfig', global.$AnsibleConfig); - $rootScope.$emit('ConfigReady'); + if(ConfigSettings.custom_logo) { + configSettings.custom_logo = true; + $rootScope.custom_logo = ConfigSettings.custom_logo; + } else { + configSettings.custom_logo = false; + } - // Load new hardcoded settings from above - $rootScope.$emit('LoadConfig'); - }; + if(ConfigSettings.custom_login_info) { + configSettings.custom_login_info = ConfigSettings.custom_login_info; + $rootScope.custom_login_info = ConfigSettings.custom_login_info; + } else { + configSettings.custom_login_info = false; + } - // Retrieve the custom logo information - update configSettings from above - $http({ - method: 'GET', - url: '/api/', - }) - .then(function({data}) { - if(data.custom_logo) { - configSettings.custom_logo = true; - $rootScope.custom_logo = data.custom_logo; - } else { - configSettings.custom_logo = false; - } + if (ConfigSettings.login_redirect_override) { + configSettings.login_redirect_override = ConfigSettings.login_redirect_override; + } - if(data.custom_login_info) { - configSettings.custom_login_info = data.custom_login_info; - $rootScope.custom_login_info = data.custom_login_info; - } else { - configSettings.custom_login_info = false; - } + // Auto-resolving what used to be found when attempting to load local_setting.json + if ($rootScope.loginConfig) { + $rootScope.loginConfig.resolve('config loaded'); + } + global.$AnsibleConfig = configSettings; + Store('AnsibleConfig', global.$AnsibleConfig); + $rootScope.$emit('ConfigReady'); - if (data.login_redirect_override) { - configSettings.login_redirect_override = data.login_redirect_override; - } - - configInit(); - - }).catch(({error}) => { - $log.debug(error); - configInit(); - }); + // Load new hardcoded settings from above + $rootScope.$emit('LoadConfig'); }; } LoadConfig.$inject = - [ '$log', '$rootScope', '$http', - 'Store' - ]; + [ '$rootScope', 'Store', 'ConfigSettings' ]; From 425d1168b9e37a834839b9173e75399e1ada9568 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 11 Dec 2019 13:33:54 -0500 Subject: [PATCH 09/12] Adds trailing slash to /api request --- awx/ui/client/src/app.start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/app.start.js b/awx/ui/client/src/app.start.js index 61c8ac5bb4..310fae9de3 100644 --- a/awx/ui/client/src/app.start.js +++ b/awx/ui/client/src/app.start.js @@ -52,7 +52,7 @@ function fetchLocaleStrings (callback) { } function fetchConfig (callback) { - const request = $.ajax(`/api`); + const request = $.ajax('/api/'); request.done(res => { angular.module('awApp').constant('ConfigSettings', res); From c16ad89ff9e74ac281c113eb66a184ef6b21274e Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 11 Dec 2019 13:37:39 -0500 Subject: [PATCH 10/12] Fix linting error (unused var) --- awx/ui/client/src/app.start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/app.start.js b/awx/ui/client/src/app.start.js index 310fae9de3..026ee0162c 100644 --- a/awx/ui/client/src/app.start.js +++ b/awx/ui/client/src/app.start.js @@ -15,7 +15,7 @@ function bootstrap (callback) { angular.module('I18N').constant('LOCALE', locale); } - fetchConfig((config) => { + fetchConfig(() => { angular.element(document).ready(() => callback()); }); }); From 475e2605d4daed13c1ee673e992ebe86f62e1d6a Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 11 Dec 2019 16:31:39 -0500 Subject: [PATCH 11/12] Changes redirect logic slightly to lean on a global var to store the config response rather than a constant on the awApp module. This should allow us to avoid test changes. --- awx/ui/client/src/app.js | 2 ++ awx/ui/client/src/app.start.js | 2 +- .../shared/load-config/load-config.factory.js | 18 +++++++++--------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 071db41067..5ba84826d4 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -3,6 +3,8 @@ global.$AnsibleConfig = null; // Provided via Webpack DefinePlugin in webpack.config.js global.$ENV = {}; +global.$ConfigResponse = {}; + var urlPrefix; if ($basePath) { diff --git a/awx/ui/client/src/app.start.js b/awx/ui/client/src/app.start.js index 026ee0162c..ef0c4a8fd5 100644 --- a/awx/ui/client/src/app.start.js +++ b/awx/ui/client/src/app.start.js @@ -55,7 +55,7 @@ function fetchConfig (callback) { const request = $.ajax('/api/'); request.done(res => { - angular.module('awApp').constant('ConfigSettings', res); + global.$ConfigResponse = res; if (res.login_redirect_override) { if (!document.cookie.split(';').filter((item) => item.includes('userLoggedIn=true')).length && !window.location.href.includes('/#/login')) { window.location.replace(res.login_redirect_override); diff --git a/awx/ui/client/src/shared/load-config/load-config.factory.js b/awx/ui/client/src/shared/load-config/load-config.factory.js index c1adfeee8f..56916cd7a4 100644 --- a/awx/ui/client/src/shared/load-config/load-config.factory.js +++ b/awx/ui/client/src/shared/load-config/load-config.factory.js @@ -1,25 +1,25 @@ export default - function LoadConfig($rootScope, Store, ConfigSettings) { + function LoadConfig($rootScope, Store) { return function() { var configSettings = {}; - if(ConfigSettings.custom_logo) { + if(global.$ConfigResponse.custom_logo) { configSettings.custom_logo = true; - $rootScope.custom_logo = ConfigSettings.custom_logo; + $rootScope.custom_logo = global.$ConfigResponse.custom_logo; } else { configSettings.custom_logo = false; } - if(ConfigSettings.custom_login_info) { - configSettings.custom_login_info = ConfigSettings.custom_login_info; - $rootScope.custom_login_info = ConfigSettings.custom_login_info; + if(global.$ConfigResponse.custom_login_info) { + configSettings.custom_login_info = global.$ConfigResponse.custom_login_info; + $rootScope.custom_login_info = global.$ConfigResponse.custom_login_info; } else { configSettings.custom_login_info = false; } - if (ConfigSettings.login_redirect_override) { - configSettings.login_redirect_override = ConfigSettings.login_redirect_override; + if (global.$ConfigResponse.login_redirect_override) { + configSettings.login_redirect_override = global.$ConfigResponse.login_redirect_override; } // Auto-resolving what used to be found when attempting to load local_setting.json @@ -37,4 +37,4 @@ export default } LoadConfig.$inject = - [ '$rootScope', 'Store', 'ConfigSettings' ]; + [ '$rootScope', 'Store' ]; From e4145b580c23f5419beeb1975dbf1f25958d0f1d Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 12 Dec 2019 15:43:23 -0500 Subject: [PATCH 12/12] fix a flake8 nit --- awx/main/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/views.py b/awx/main/views.py index bc791976db..887d44318c 100644 --- a/awx/main/views.py +++ b/awx/main/views.py @@ -98,5 +98,6 @@ def handle_csp_violation(request): logger.error(json.loads(request.body)) return HttpResponse(content=None) + def handle_login_redirect(request): return HttpResponseRedirect("/#/login")