Commit e80b78d1 by Alexander Dutton Committed by Tom Christie

RemoteUserAuthentication, docs, and tests (#5306)

RemoteUserAuthentication, docs, and tests
parent 9b5a6bea
...@@ -239,6 +239,28 @@ If you're using an AJAX style API with SessionAuthentication, you'll need to mak ...@@ -239,6 +239,28 @@ If you're using an AJAX style API with SessionAuthentication, you'll need to mak
CSRF validation in REST framework works slightly differently to standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens. This behaviour is not suitable for login views, which should always have CSRF validation applied. CSRF validation in REST framework works slightly differently to standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens. This behaviour is not suitable for login views, which should always have CSRF validation applied.
## RemoteUserAuthentication
This authentication scheme allows you to delegate authentication to your web server, which sets the `REMOTE_USER`
environment variable.
To use it, you must have `django.contrib.auth.backends.RemoteUserBackend` (or a subclass) in your
`AUTHENTICATION_BACKENDS` setting. By default, `RemoteUserBackend` creates `User` objects for usernames that don't
already exist. To change this and other behaviour, consult the
[Django documentation](https://docs.djangoproject.com/en/stable/howto/auth-remote-user/).
If successfully authenticated, `RemoteUserAuthentication` provides the following credentials:
* `request.user` will be a Django `User` instance.
* `request.auth` will be `None`.
Consult your web server's documentation for information about configuring an authentication method, e.g.:
* [Apache Authentication How-To](https://httpd.apache.org/docs/2.4/howto/auth.html)
* [NGINX (Restricting Access)](https://www.nginx.com/resources/admin-guide/#restricting_access)
# Custom authentication # Custom authentication
To implement a custom authentication scheme, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. To implement a custom authentication scheme, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
......
...@@ -201,3 +201,24 @@ class TokenAuthentication(BaseAuthentication): ...@@ -201,3 +201,24 @@ class TokenAuthentication(BaseAuthentication):
def authenticate_header(self, request): def authenticate_header(self, request):
return self.keyword return self.keyword
class RemoteUserAuthentication(BaseAuthentication):
"""
REMOTE_USER authentication.
To use this, set up your web server to perform authentication, which will
set the REMOTE_USER environment variable. You will need to have
'django.contrib.auth.backends.RemoteUserBackend in your
AUTHENTICATION_BACKENDS setting
"""
# Name of request header to grab username from. This will be the key as
# used in the request.META dictionary, i.e. the normalization of headers to
# all uppercase and the addition of "HTTP_" prefix apply.
header = "REMOTE_USER"
def authenticate(self, request):
user = authenticate(remote_user=request.META.get(self.header))
if user and user.is_active:
return (user, None)
...@@ -16,9 +16,8 @@ from rest_framework import ( ...@@ -16,9 +16,8 @@ from rest_framework import (
HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status
) )
from rest_framework.authentication import ( from rest_framework.authentication import (
BaseAuthentication, BasicAuthentication, SessionAuthentication, BaseAuthentication, BasicAuthentication, RemoteUserAuthentication, SessionAuthentication,
TokenAuthentication TokenAuthentication)
)
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import obtain_auth_token from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.compat import is_authenticated from rest_framework.compat import is_authenticated
...@@ -65,6 +64,10 @@ urlpatterns = [ ...@@ -65,6 +64,10 @@ urlpatterns = [
MockView.as_view(authentication_classes=[BasicAuthentication]) MockView.as_view(authentication_classes=[BasicAuthentication])
), ),
url( url(
r'^remote-user/$',
MockView.as_view(authentication_classes=[RemoteUserAuthentication])
),
url(
r'^token/$', r'^token/$',
MockView.as_view(authentication_classes=[TokenAuthentication]) MockView.as_view(authentication_classes=[TokenAuthentication])
), ),
...@@ -523,3 +526,20 @@ class BasicAuthenticationUnitTests(TestCase): ...@@ -523,3 +526,20 @@ class BasicAuthenticationUnitTests(TestCase):
auth.authenticate_credentials('foo', 'bar') auth.authenticate_credentials('foo', 'bar')
assert 'User inactive or deleted.' in str(error) assert 'User inactive or deleted.' in str(error)
authentication.authenticate = old_authenticate authentication.authenticate = old_authenticate
@override_settings(ROOT_URLCONF='tests.test_authentication',
AUTHENTICATION_BACKENDS=('django.contrib.auth.backends.RemoteUserBackend',))
class RemoteUserAuthenticationUnitTests(TestCase):
def setUp(self):
self.username = 'john'
self.email = 'lennon@thebeatles.com'
self.password = 'password'
self.user = User.objects.create_user(
self.username, self.email, self.password
)
def test_remote_user_works(self):
response = self.client.post('/remote-user/',
REMOTE_USER=self.username)
self.assertEqual(response.status_code, status.HTTP_200_OK)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment