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', +) 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) diff --git a/awx/main/views.py b/awx/main/views.py index 1947bd001c..887d44318c 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,7 @@ 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/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 diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 54e05c4a1e..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) { @@ -383,7 +385,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/app.start.js b/awx/ui/client/src/app.start.js index 44f8113567..ef0c4a8fd5 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(() => { + 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 => { + 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); + } 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/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' 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..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,55 +1,40 @@ export default - function LoadConfig($log, $rootScope, $http, Store) { + function LoadConfig($rootScope, Store) { 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'); - } - $rootScope.$emit('ConfigReady'); + if(global.$ConfigResponse.custom_logo) { + configSettings.custom_logo = true; + $rootScope.custom_logo = global.$ConfigResponse.custom_logo; + } else { + configSettings.custom_logo = false; + } - // Load new hardcoded settings from above + 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; + } - global.$AnsibleConfig = configSettings; - Store('AnsibleConfig', global.$AnsibleConfig); - $rootScope.$emit('LoadConfig'); - }; + if (global.$ConfigResponse.login_redirect_override) { + configSettings.login_redirect_override = global.$ConfigResponse.login_redirect_override; + } - // 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; - } + // 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.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; - } - - configInit(); - - }).catch(({error}) => { - $log.debug(error); - configInit(); - }); + // Load new hardcoded settings from above + $rootScope.$emit('LoadConfig'); }; } LoadConfig.$inject = - [ '$log', '$rootScope', '$http', - 'Store' - ]; + [ '$rootScope', 'Store' ]; 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':