From 9720b4be0e132207d55bb6f3d8999bff94574d20 Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Fri, 4 Aug 2017 10:54:03 +0100 Subject: [PATCH 1/2] RemoteUserAuthentication, docs, and tests Support for delegating authentication to a web server. --- docs/api-guide/authentication.md | 22 ++++++++++++++++++++++ rest_framework/authentication.py | 21 +++++++++++++++++++++ tests/test_authentication.py | 25 +++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 05b8523f89..a1c24f954b 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -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. + +## 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 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. diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index cb9608a3c9..606f572ab3 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -201,3 +201,24 @@ def authenticate_credentials(self, key): def authenticate_header(self, request): 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) diff --git a/tests/test_authentication.py b/tests/test_authentication.py index f2e0fb424b..d1cba81bea 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -17,8 +17,8 @@ ) from rest_framework.authentication import ( BaseAuthentication, BasicAuthentication, SessionAuthentication, - TokenAuthentication -) + TokenAuthentication, + RemoteUserAuthentication) from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import obtain_auth_token from rest_framework.compat import is_authenticated @@ -64,6 +64,10 @@ def put(self, request): r'^basic/$', MockView.as_view(authentication_classes=[BasicAuthentication]) ), + url( + r'^remote-user/$', + MockView.as_view(authentication_classes=[RemoteUserAuthentication]) + ), url( r'^token/$', MockView.as_view(authentication_classes=[TokenAuthentication]) @@ -523,3 +527,20 @@ class MockUser(object): auth.authenticate_credentials('foo', 'bar') assert 'User inactive or deleted.' in str(error) 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) From ab56369e98dfd65e62d93da85729796d8b92ea30 Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Fri, 4 Aug 2017 11:15:37 +0100 Subject: [PATCH 2/2] Reorder imports to appease the linting gods --- tests/test_authentication.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_authentication.py b/tests/test_authentication.py index d1cba81bea..fdbc28a2a7 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -16,9 +16,8 @@ HTTP_HEADER_ENCODING, exceptions, permissions, renderers, status ) from rest_framework.authentication import ( - BaseAuthentication, BasicAuthentication, SessionAuthentication, - TokenAuthentication, - RemoteUserAuthentication) + BaseAuthentication, BasicAuthentication, RemoteUserAuthentication, SessionAuthentication, + TokenAuthentication) from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import obtain_auth_token from rest_framework.compat import is_authenticated pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy