diff --git a/awx/api/authentication.py b/awx/api/authentication.py index f0bcef9e4d..faf8b8d56c 100644 --- a/awx/api/authentication.py +++ b/awx/api/authentication.py @@ -18,7 +18,8 @@ class TokenAuthentication(authentication.TokenAuthentication): model = AuthToken - def _get_x_auth_token_header(self, request): + @staticmethod + def _get_x_auth_token_header(request): auth = request.META.get('HTTP_X_AUTH_TOKEN', '') if isinstance(auth, type('')): # Work around django test client oddness @@ -31,7 +32,7 @@ class TokenAuthentication(authentication.TokenAuthentication): # Prefer the custom X-Auth-Token header over the Authorization header, # to handle cases where the browser submits saved Basic auth and # overrides the UI's normal use of the Authorization header. - auth = self._get_x_auth_token_header(request).split() + auth = TokenAuthentication._get_x_auth_token_header(request).split() if not auth or auth[0].lower() != 'token': auth = authentication.get_authorization_header(request).split() if not auth or auth[0].lower() != 'token': diff --git a/awx/main/middleware.py b/awx/main/middleware.py index b32ce932cb..b36ac9b53c 100644 --- a/awx/main/middleware.py +++ b/awx/main/middleware.py @@ -11,9 +11,11 @@ from django.db import IntegrityError from django.http import HttpResponseRedirect from django.template.response import TemplateResponse from django.utils.functional import curry +from django.conf import settings from awx import __version__ as version from awx.main.models import ActivityStream, Instance +from awx.api.authentication import TokenAuthentication logger = logging.getLogger('awx.main.middleware') @@ -100,3 +102,18 @@ class HAMiddleware(object): # Redirect to the base page of the primary instance. return HttpResponseRedirect('http://%s%s' % (primary.hostname, request.path)) + +class AuthTokenTimeoutMiddleware(object): + """Presume that when the user includes the auth header, they go through the + authentication mechanism. Further, that mechanism is presumed to extend + the users session validity time by AUTH_TOKEN_EXPIRATION. + + If the auth token is not supplied, then don't include the header + """ + def process_response(self, request, response): + if not TokenAuthentication._get_x_auth_token_header(request): + return response + + response['Auth-Token-Timeout'] = int(settings.AUTH_TOKEN_EXPIRATION) + return response + diff --git a/awx/main/tests/base.py b/awx/main/tests/base.py index 771eac2fbf..ec7bccf909 100644 --- a/awx/main/tests/base.py +++ b/awx/main/tests/base.py @@ -460,8 +460,8 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin): assert response.status_code == expect, "expected status %s, got %s for url=%s as auth=%s: %s" % (expect, response.status_code, url, auth, response.content) if method_name == 'head': self.assertFalse(response.content) - #if return_response_object: - # return response + if return_response_object: + return response if response.status_code not in [204, 405] and method_name != 'head' and response.content: # no JSON responses in these at least for now, 409 should probably return some (FIXME) if response['Content-Type'].startswith('application/json'): diff --git a/awx/main/tests/users.py b/awx/main/tests/users.py index e0456fdc10..a2f111d5c6 100644 --- a/awx/main/tests/users.py +++ b/awx/main/tests/users.py @@ -15,7 +15,28 @@ from django.core.urlresolvers import reverse from awx.main.models import * # noqa from awx.main.tests.base import BaseTest -__all__ = ['AuthTokenProxyTest', 'UsersTest', 'LdapTest'] +__all__ = ['AuthTokenTimeoutTest', 'AuthTokenProxyTest', 'UsersTest', 'LdapTest'] + + +class AuthTokenTimeoutTest(BaseTest): + def setUp(self): + super(AuthTokenTimeoutTest, self).setUp() + self.setup_users() + self.setup_instances() + + def test_auth_token_timeout_exists(self): + auth_token_url = reverse('api:auth_token_view') + dashboard_url = reverse('api:dashboard_view') + + data = dict(zip(('username', 'password'), self.get_super_credentials())) + auth = self.post(auth_token_url, data, expect=200) + kwargs = { + 'HTTP_X_AUTH_TOKEN': 'Token %s' % auth['token'] + } + + response = self._generic_rest(dashboard_url, expect=200, method='get', return_response_object=True, client_kwargs=kwargs) + self.assertIn('Auth-Token-Timeout', response) + self.assertEqual(response['Auth-Token-Timeout'], str(settings.AUTH_TOKEN_EXPIRATION)) ''' Ensure ips from the X-Forwarded-For get honored and used in auth tokens diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index ad2412f78c..b2d7c295bf 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -128,6 +128,7 @@ MIDDLEWARE_CLASSES += ( # NOQA 'awx.main.middleware.HAMiddleware', 'awx.main.middleware.ActivityStreamMiddleware', 'crum.CurrentRequestUserMiddleware', + 'awx.main.middleware.AuthTokenTimeoutMiddleware', ) TEMPLATE_DIRS = (