|
| 1 | +from django.contrib import auth |
| 2 | +from django.contrib.auth.middleware import RemoteUserMiddleware |
| 3 | +from django.core.exceptions import ImproperlyConfigured |
| 4 | +from django.conf import settings |
| 5 | +from django.core.cache import caches |
| 6 | +import gzip |
| 7 | +import base64 |
| 8 | +import jwt |
| 9 | +import requests |
| 10 | +import hashlib |
| 11 | +from requests import HTTPError |
| 12 | + |
| 13 | + |
| 14 | +class VouchProxyMiddleware(RemoteUserMiddleware): |
| 15 | + def __init__(self, *args, **kwargs): |
| 16 | + self.cookie_name = getattr(settings, 'VOUCH_PROXY_COOKIE_NAME', 'VouchCookie') |
| 17 | + self.cache_prefix = format(getattr(settings, 'VOUCH_PROXY_CACHE_PREFIX', '{}_'.format(self.cookie_name))) |
| 18 | + self.expiry_time = getattr(settings, 'VOUCH_PROXY_CACHE_TIMEOUT', 300) |
| 19 | + self.cache = caches[getattr(settings, 'VOUCH_PROXY_CACHE_BACKEND', 'default')] |
| 20 | + self.force_logout_if_no_cookie = getattr(settings, 'VOUCH_PROXY_FORCE_LOGOUT_IF_NO_COOKIE', False) |
| 21 | + |
| 22 | + super(VouchProxyMiddleware).__init__(*args, **kwargs) |
| 23 | + |
| 24 | + def process_request(self, request): |
| 25 | + if request.path in getattr(settings, 'VOUCH_PROXY_DISABLED_PATHS', []): |
| 26 | + return |
| 27 | + |
| 28 | + if not hasattr(request, 'user'): |
| 29 | + raise ImproperlyConfigured( |
| 30 | + "The Django Vouch Proxy auth middleware requires the" |
| 31 | + " authentication middleware to be installed. Edit your" |
| 32 | + " MIDDLEWARE setting to insert" |
| 33 | + " 'django.contrib.auth.middleware.AuthenticationMiddleware'" |
| 34 | + " before the VouchProxyMiddleware class.") |
| 35 | + if not hasattr(settings, 'VOUCH_PROXY_VALIDATE_ENDPOINT'): |
| 36 | + raise ImproperlyConfigured( |
| 37 | + "You must provide a valid URL in VOUCH_PROXY_VALIDATE_ENDPOINT" |
| 38 | + " for the Vouch Proxy validation endpoint in your Django settings.") |
| 39 | + try: |
| 40 | + cookie = request.COOKIES[self.cookie_name] |
| 41 | + |
| 42 | + cache_key = '{}{}'.format(self.cache_prefix, hashlib.sha256(cookie.encode('ascii')).hexdigest()) |
| 43 | + username = self.cache.get(cache_key) |
| 44 | + if not username: |
| 45 | + validate = requests.get(settings.VOUCH_PROXY_VALIDATE_ENDPOINT, cookies={self.cookie_name: cookie}) |
| 46 | + validate.raise_for_status() |
| 47 | + |
| 48 | + # Vouch cookie is URL-safe Base64 encoded Gzipped data |
| 49 | + decompressed = gzip.decompress(base64.urlsafe_b64decode(cookie)) |
| 50 | + payload = jwt.decode(decompressed, options={'verify_signature': False}) |
| 51 | + username = payload['username'] |
| 52 | + self.cache.set(cache_key, username, self.expiry_time) |
| 53 | + except (KeyError, HTTPError): |
| 54 | + # If specified header doesn't exist then remove any existing |
| 55 | + # authenticated remote-user, or return (leaving request.user set to |
| 56 | + # AnonymousUser by the AuthenticationMiddleware). |
| 57 | + if self.force_logout_if_no_cookie and request.user.is_authenticated: |
| 58 | + self._remove_invalid_user(request) |
| 59 | + return |
| 60 | + |
| 61 | + # If the user is already authenticated and that user is the user we are |
| 62 | + # getting passed in the headers, then the correct user is already |
| 63 | + # persisted in the session and we don't need to continue. |
| 64 | + if request.user.is_authenticated: |
| 65 | + if request.user.get_username() == self.clean_username(username, request): |
| 66 | + return |
| 67 | + else: |
| 68 | + # An authenticated user is associated with the request, but |
| 69 | + # it does not match the authorized user in the header. |
| 70 | + self._remove_invalid_user(request) |
| 71 | + |
| 72 | + # We are seeing this user for the first time in this session, attempt |
| 73 | + # to authenticate the user. |
| 74 | + user = auth.authenticate(request, remote_user=username) |
| 75 | + if user: |
| 76 | + # User is valid. Set request.user and persist user in the session |
| 77 | + # by logging the user in. |
| 78 | + request.user = user |
| 79 | + auth.login(request, user) |
0 commit comments