2009-03-15 06:54:28 +01:00
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from django.conf import settings
|
2012-09-09 22:25:06 +02:00
|
|
|
from django.contrib.auth import authenticate
|
2009-03-15 06:54:28 +01:00
|
|
|
from django.contrib.auth.backends import RemoteUserBackend
|
2013-12-07 20:03:11 +01:00
|
|
|
from django.contrib.auth.middleware import RemoteUserMiddleware
|
2013-06-19 17:18:40 +02:00
|
|
|
from django.contrib.auth.models import User
|
2020-02-01 08:42:24 +01:00
|
|
|
from django.middleware.csrf import _get_new_csrf_string, _mask_cipher_secret
|
|
|
|
from django.test import Client, TestCase, modify_settings, override_settings
|
2011-11-20 11:50:18 +01:00
|
|
|
from django.utils import timezone
|
2009-03-15 06:54:28 +01:00
|
|
|
|
|
|
|
|
2015-02-10 15:17:08 +01:00
|
|
|
@override_settings(ROOT_URLCONF='auth_tests.urls')
|
2009-03-15 06:54:28 +01:00
|
|
|
class RemoteUserTest(TestCase):
|
|
|
|
|
|
|
|
middleware = 'django.contrib.auth.middleware.RemoteUserMiddleware'
|
|
|
|
backend = 'django.contrib.auth.backends.RemoteUserBackend'
|
2013-12-07 20:03:11 +01:00
|
|
|
header = 'REMOTE_USER'
|
2018-12-13 17:14:03 +01:00
|
|
|
email_header = 'REMOTE_EMAIL'
|
2009-03-15 06:54:28 +01:00
|
|
|
|
|
|
|
# Usernames to be passed in REMOTE_USER for the test_known_user test case.
|
|
|
|
known_user = 'knownuser'
|
|
|
|
known_user2 = 'knownuser2'
|
|
|
|
|
|
|
|
def setUp(self):
|
2015-02-04 15:56:55 +01:00
|
|
|
self.patched_settings = modify_settings(
|
|
|
|
AUTHENTICATION_BACKENDS={'append': self.backend},
|
2015-11-07 16:12:37 +01:00
|
|
|
MIDDLEWARE={'append': self.middleware},
|
2015-02-04 15:56:55 +01:00
|
|
|
)
|
|
|
|
self.patched_settings.enable()
|
2015-01-21 17:55:57 +01:00
|
|
|
|
|
|
|
def tearDown(self):
|
2015-02-04 15:56:55 +01:00
|
|
|
self.patched_settings.disable()
|
2009-03-15 06:54:28 +01:00
|
|
|
|
|
|
|
def test_no_remote_user(self):
|
|
|
|
"""
|
|
|
|
Tests requests where no remote user is specified and insures that no
|
|
|
|
users get created.
|
|
|
|
"""
|
|
|
|
num_users = User.objects.count()
|
|
|
|
|
2009-05-04 09:05:44 +02:00
|
|
|
response = self.client.get('/remote_user/')
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertTrue(response.context['user'].is_anonymous)
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(User.objects.count(), num_users)
|
|
|
|
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/', **{self.header: None})
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertTrue(response.context['user'].is_anonymous)
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(User.objects.count(), num_users)
|
|
|
|
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/', **{self.header: ''})
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertTrue(response.context['user'].is_anonymous)
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(User.objects.count(), num_users)
|
|
|
|
|
2020-02-01 08:42:24 +01:00
|
|
|
def test_csrf_validation_passes_after_process_request_login(self):
|
|
|
|
"""
|
|
|
|
CSRF check must access the CSRF token from the session or cookie,
|
|
|
|
rather than the request, as rotate_token() may have been called by an
|
|
|
|
authentication middleware during the process_request() phase.
|
|
|
|
"""
|
|
|
|
csrf_client = Client(enforce_csrf_checks=True)
|
|
|
|
csrf_secret = _get_new_csrf_string()
|
|
|
|
csrf_token = _mask_cipher_secret(csrf_secret)
|
|
|
|
csrf_token_form = _mask_cipher_secret(csrf_secret)
|
|
|
|
headers = {self.header: 'fakeuser'}
|
|
|
|
data = {'csrfmiddlewaretoken': csrf_token_form}
|
|
|
|
|
|
|
|
# Verify that CSRF is configured for the view
|
|
|
|
csrf_client.cookies.load({settings.CSRF_COOKIE_NAME: csrf_token})
|
|
|
|
response = csrf_client.post('/remote_user/', **headers)
|
|
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
self.assertIn(b'CSRF verification failed.', response.content)
|
|
|
|
|
|
|
|
# This request will call django.contrib.auth.login() which will call
|
|
|
|
# django.middleware.csrf.rotate_token() thus changing the value of
|
|
|
|
# request.META['CSRF_COOKIE'] from the user submitted value set by
|
|
|
|
# CsrfViewMiddleware.process_request() to the new csrftoken value set
|
|
|
|
# by rotate_token(). Csrf validation should still pass when the view is
|
|
|
|
# later processed by CsrfViewMiddleware.process_view()
|
|
|
|
csrf_client.cookies.load({settings.CSRF_COOKIE_NAME: csrf_token})
|
|
|
|
response = csrf_client.post('/remote_user/', data, **headers)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
2009-03-15 06:54:28 +01:00
|
|
|
def test_unknown_user(self):
|
|
|
|
"""
|
|
|
|
Tests the case where the username passed in the header does not exist
|
|
|
|
as a User.
|
|
|
|
"""
|
|
|
|
num_users = User.objects.count()
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/', **{self.header: 'newuser'})
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(response.context['user'].username, 'newuser')
|
|
|
|
self.assertEqual(User.objects.count(), num_users + 1)
|
|
|
|
User.objects.get(username='newuser')
|
|
|
|
|
|
|
|
# Another request with same user should not create any new users.
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/', **{self.header: 'newuser'})
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(User.objects.count(), num_users + 1)
|
|
|
|
|
|
|
|
def test_known_user(self):
|
|
|
|
"""
|
|
|
|
Tests the case where the username passed in the header is a valid User.
|
|
|
|
"""
|
|
|
|
User.objects.create(username='knownuser')
|
|
|
|
User.objects.create(username='knownuser2')
|
|
|
|
num_users = User.objects.count()
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: self.known_user})
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(response.context['user'].username, 'knownuser')
|
|
|
|
self.assertEqual(User.objects.count(), num_users)
|
2016-10-27 09:53:39 +02:00
|
|
|
# A different user passed in the headers causes the new user
|
2009-03-15 06:54:28 +01:00
|
|
|
# to be logged in.
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: self.known_user2})
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(response.context['user'].username, 'knownuser2')
|
|
|
|
self.assertEqual(User.objects.count(), num_users)
|
|
|
|
|
|
|
|
def test_last_login(self):
|
|
|
|
"""
|
2016-10-27 09:53:39 +02:00
|
|
|
A user's last_login is set the first time they make a
|
2009-03-15 06:54:28 +01:00
|
|
|
request but not updated in subsequent requests with the same session.
|
|
|
|
"""
|
|
|
|
user = User.objects.create(username='knownuser')
|
|
|
|
# Set last_login to something so we can determine if it changes.
|
|
|
|
default_login = datetime(2000, 1, 1)
|
2011-11-20 11:50:18 +01:00
|
|
|
if settings.USE_TZ:
|
|
|
|
default_login = default_login.replace(tzinfo=timezone.utc)
|
2009-03-15 06:54:28 +01:00
|
|
|
user.last_login = default_login
|
|
|
|
user.save()
|
|
|
|
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: self.known_user})
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertNotEqual(default_login, response.context['user'].last_login)
|
|
|
|
|
|
|
|
user = User.objects.get(username='knownuser')
|
|
|
|
user.last_login = default_login
|
|
|
|
user.save()
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: self.known_user})
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(default_login, response.context['user'].last_login)
|
|
|
|
|
2012-09-09 22:25:06 +02:00
|
|
|
def test_header_disappears(self):
|
|
|
|
"""
|
2016-10-27 09:53:39 +02:00
|
|
|
A logged in user is logged out automatically when
|
2012-09-09 22:25:06 +02:00
|
|
|
the REMOTE_USER header disappears during the same browser session.
|
|
|
|
"""
|
|
|
|
User.objects.create(username='knownuser')
|
|
|
|
# Known user authenticates
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: self.known_user})
|
2012-09-09 22:25:06 +02:00
|
|
|
self.assertEqual(response.context['user'].username, 'knownuser')
|
|
|
|
# During the session, the REMOTE_USER header disappears. Should trigger logout.
|
|
|
|
response = self.client.get('/remote_user/')
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertTrue(response.context['user'].is_anonymous)
|
2012-09-09 22:25:06 +02:00
|
|
|
# verify the remoteuser middleware will not remove a user
|
|
|
|
# authenticated via another backend
|
|
|
|
User.objects.create_user(username='modeluser', password='foo')
|
|
|
|
self.client.login(username='modeluser', password='foo')
|
|
|
|
authenticate(username='modeluser', password='foo')
|
|
|
|
response = self.client.get('/remote_user/')
|
|
|
|
self.assertEqual(response.context['user'].username, 'modeluser')
|
|
|
|
|
2014-07-28 06:54:29 +02:00
|
|
|
def test_user_switch_forces_new_login(self):
|
|
|
|
"""
|
2016-10-27 09:53:39 +02:00
|
|
|
If the username in the header changes between requests
|
2014-07-28 06:54:29 +02:00
|
|
|
that the original user is logged out
|
|
|
|
"""
|
|
|
|
User.objects.create(username='knownuser')
|
|
|
|
# Known user authenticates
|
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: self.known_user})
|
|
|
|
self.assertEqual(response.context['user'].username, 'knownuser')
|
|
|
|
# During the session, the REMOTE_USER changes to a different user.
|
|
|
|
response = self.client.get('/remote_user/',
|
|
|
|
**{self.header: "newnewuser"})
|
2016-10-27 09:53:39 +02:00
|
|
|
# The current user is not the prior remote_user.
|
2014-07-28 06:54:29 +02:00
|
|
|
# In backends that create a new user, username is "newnewuser"
|
|
|
|
# In backends that do not create new users, it is '' (anonymous user)
|
|
|
|
self.assertNotEqual(response.context['user'].username, 'knownuser')
|
|
|
|
|
2016-02-05 15:46:19 +01:00
|
|
|
def test_inactive_user(self):
|
|
|
|
User.objects.create(username='knownuser', is_active=False)
|
|
|
|
response = self.client.get('/remote_user/', **{self.header: 'knownuser'})
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertTrue(response.context['user'].is_anonymous)
|
2016-02-05 15:46:19 +01:00
|
|
|
|
2009-03-15 06:54:28 +01:00
|
|
|
|
|
|
|
class RemoteUserNoCreateBackend(RemoteUserBackend):
|
|
|
|
"""Backend that doesn't create unknown users."""
|
|
|
|
create_unknown_user = False
|
|
|
|
|
|
|
|
|
|
|
|
class RemoteUserNoCreateTest(RemoteUserTest):
|
|
|
|
"""
|
|
|
|
Contains the same tests as RemoteUserTest, but using a custom auth backend
|
|
|
|
class that doesn't create unknown users.
|
|
|
|
"""
|
|
|
|
|
2015-02-10 15:17:08 +01:00
|
|
|
backend = 'auth_tests.test_remote_user.RemoteUserNoCreateBackend'
|
2009-03-15 06:54:28 +01:00
|
|
|
|
|
|
|
def test_unknown_user(self):
|
|
|
|
num_users = User.objects.count()
|
2013-12-07 20:03:11 +01:00
|
|
|
response = self.client.get('/remote_user/', **{self.header: 'newuser'})
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertTrue(response.context['user'].is_anonymous)
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(User.objects.count(), num_users)
|
|
|
|
|
|
|
|
|
2016-02-05 15:46:19 +01:00
|
|
|
class AllowAllUsersRemoteUserBackendTest(RemoteUserTest):
|
|
|
|
"""Backend that allows inactive users."""
|
|
|
|
backend = 'django.contrib.auth.backends.AllowAllUsersRemoteUserBackend'
|
|
|
|
|
|
|
|
def test_inactive_user(self):
|
|
|
|
user = User.objects.create(username='knownuser', is_active=False)
|
|
|
|
response = self.client.get('/remote_user/', **{self.header: self.known_user})
|
|
|
|
self.assertEqual(response.context['user'].username, user.username)
|
|
|
|
|
|
|
|
|
2009-03-15 06:54:28 +01:00
|
|
|
class CustomRemoteUserBackend(RemoteUserBackend):
|
|
|
|
"""
|
|
|
|
Backend that overrides RemoteUserBackend methods.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def clean_username(self, username):
|
|
|
|
"""
|
|
|
|
Grabs username before the @ character.
|
|
|
|
"""
|
|
|
|
return username.split('@')[0]
|
|
|
|
|
2018-12-13 17:14:03 +01:00
|
|
|
def configure_user(self, request, user):
|
2009-03-15 06:54:28 +01:00
|
|
|
"""
|
2018-12-13 17:14:03 +01:00
|
|
|
Sets user's email address using the email specified in an HTTP header.
|
2009-03-15 06:54:28 +01:00
|
|
|
"""
|
2018-12-13 17:14:03 +01:00
|
|
|
user.email = request.META.get(RemoteUserTest.email_header, '')
|
2009-03-15 06:54:28 +01:00
|
|
|
user.save()
|
|
|
|
return user
|
|
|
|
|
|
|
|
|
|
|
|
class RemoteUserCustomTest(RemoteUserTest):
|
|
|
|
"""
|
|
|
|
Tests a custom RemoteUserBackend subclass that overrides the clean_username
|
|
|
|
and configure_user methods.
|
|
|
|
"""
|
|
|
|
|
2015-02-10 15:17:08 +01:00
|
|
|
backend = 'auth_tests.test_remote_user.CustomRemoteUserBackend'
|
2011-04-01 18:10:22 +02:00
|
|
|
# REMOTE_USER strings with email addresses for the custom backend to
|
2009-03-15 06:54:28 +01:00
|
|
|
# clean.
|
|
|
|
known_user = 'knownuser@example.com'
|
|
|
|
known_user2 = 'knownuser2@example.com'
|
|
|
|
|
|
|
|
def test_known_user(self):
|
|
|
|
"""
|
|
|
|
The strings passed in REMOTE_USER should be cleaned and the known users
|
|
|
|
should not have been configured with an email address.
|
|
|
|
"""
|
2017-01-21 14:13:44 +01:00
|
|
|
super().test_known_user()
|
2009-03-15 06:54:28 +01:00
|
|
|
self.assertEqual(User.objects.get(username='knownuser').email, '')
|
|
|
|
self.assertEqual(User.objects.get(username='knownuser2').email, '')
|
|
|
|
|
|
|
|
def test_unknown_user(self):
|
|
|
|
"""
|
2018-12-13 17:14:03 +01:00
|
|
|
The unknown user created should be configured with an email address
|
|
|
|
provided in the request header.
|
2009-03-15 06:54:28 +01:00
|
|
|
"""
|
2018-12-13 17:14:03 +01:00
|
|
|
num_users = User.objects.count()
|
|
|
|
response = self.client.get('/remote_user/', **{
|
|
|
|
self.header: 'newuser',
|
|
|
|
self.email_header: 'user@example.com',
|
|
|
|
})
|
|
|
|
self.assertEqual(response.context['user'].username, 'newuser')
|
|
|
|
self.assertEqual(response.context['user'].email, 'user@example.com')
|
|
|
|
self.assertEqual(User.objects.count(), num_users + 1)
|
2009-03-15 06:54:28 +01:00
|
|
|
newuser = User.objects.get(username='newuser')
|
|
|
|
self.assertEqual(newuser.email, 'user@example.com')
|
2013-12-07 20:03:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
class CustomHeaderMiddleware(RemoteUserMiddleware):
|
|
|
|
"""
|
|
|
|
Middleware that overrides custom HTTP auth user header.
|
|
|
|
"""
|
|
|
|
header = 'HTTP_AUTHUSER'
|
|
|
|
|
|
|
|
|
|
|
|
class CustomHeaderRemoteUserTest(RemoteUserTest):
|
|
|
|
"""
|
|
|
|
Tests a custom RemoteUserMiddleware subclass with custom HTTP auth user
|
|
|
|
header.
|
|
|
|
"""
|
|
|
|
middleware = (
|
2015-02-10 15:17:08 +01:00
|
|
|
'auth_tests.test_remote_user.CustomHeaderMiddleware'
|
2013-12-07 20:03:11 +01:00
|
|
|
)
|
|
|
|
header = 'HTTP_AUTHUSER'
|
2015-06-26 20:59:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
class PersistentRemoteUserTest(RemoteUserTest):
|
|
|
|
"""
|
|
|
|
PersistentRemoteUserMiddleware keeps the user logged in even if the
|
|
|
|
subsequent calls do not contain the header value.
|
|
|
|
"""
|
|
|
|
middleware = 'django.contrib.auth.middleware.PersistentRemoteUserMiddleware'
|
|
|
|
require_header = False
|
|
|
|
|
|
|
|
def test_header_disappears(self):
|
|
|
|
"""
|
|
|
|
A logged in user is kept logged in even if the REMOTE_USER header
|
|
|
|
disappears during the same browser session.
|
|
|
|
"""
|
|
|
|
User.objects.create(username='knownuser')
|
|
|
|
# Known user authenticates
|
|
|
|
response = self.client.get('/remote_user/', **{self.header: self.known_user})
|
|
|
|
self.assertEqual(response.context['user'].username, 'knownuser')
|
|
|
|
# Should stay logged in if the REMOTE_USER header disappears.
|
|
|
|
response = self.client.get('/remote_user/')
|
2016-04-02 13:18:26 +02:00
|
|
|
self.assertFalse(response.context['user'].is_anonymous)
|
2015-06-26 20:59:57 +02:00
|
|
|
self.assertEqual(response.context['user'].username, 'knownuser')
|