|
| 1 | +import logging |
| 2 | +from functools import wraps |
| 3 | +from django.contrib.auth import authenticate, login |
| 4 | +from django.http import HttpResponse, HttpResponseForbidden |
| 5 | +from django.conf import settings |
| 6 | +from base64 import b64decode |
| 7 | + |
| 8 | +__ALL__ = ['basic_auth_required'] |
| 9 | +logger = logging.getLogger(__name__) |
| 10 | + |
| 11 | + |
| 12 | +def basic_auth_required(realm='default'): |
| 13 | + def _helper(func): |
| 14 | + @wraps(func) |
| 15 | + def _decorator(request, *args, **kwargs): |
| 16 | + allowed = False |
| 17 | + logger.info('request is secure? {}'.format(request.is_secure())) |
| 18 | + if settings.ALLOW_ANONYMOUS_POST: |
| 19 | + logger.debug('allowing anonymous post') |
| 20 | + allowed = True |
| 21 | + elif hasattr(request, 'user') and request.user.is_authenticated(): |
| 22 | + allowed = True |
| 23 | + elif 'HTTP_AUTHORIZATION' in request.META: |
| 24 | + logger.debug('checking for http authorization header') |
| 25 | + if settings.REQUIRE_SECURE_AUTH and not request.is_secure(): |
| 26 | + return insecure_connection_response() |
| 27 | + http_auth = request.META['HTTP_AUTHORIZATION'] |
| 28 | + authmeth, auth = http_auth.split(' ', 1) |
| 29 | + if authmeth.lower() == 'basic': |
| 30 | + username, password = decode_basic_auth(auth) |
| 31 | + user = authenticate(username=username, password=password) |
| 32 | + if user is not None and user.is_active: |
| 33 | + logger.info( |
| 34 | + 'Authentication succeeded for {}'.format(username)) |
| 35 | + login(request, user) |
| 36 | + allowed = True |
| 37 | + else: |
| 38 | + logger.info( |
| 39 | + 'Failed auth for {}'.format(username)) |
| 40 | + return HttpResponseForbidden() |
| 41 | + if allowed: |
| 42 | + return func(request, *args, **kwargs) |
| 43 | + |
| 44 | + if settings.REQUIRE_SECURE_AUTH and not request.is_secure(): |
| 45 | + logger.debug('not requesting auth over an insecure channel') |
| 46 | + return insecure_connection_response() |
| 47 | + else: |
| 48 | + res = HttpResponse() |
| 49 | + res.status_code = 401 |
| 50 | + res.reason_phrase = 'Unauthorized' |
| 51 | + res['WWW-Authenticate'] = 'Basic realm="{}"'.format(realm) |
| 52 | + return res |
| 53 | + return _decorator |
| 54 | + |
| 55 | + return _helper |
| 56 | + |
| 57 | + |
| 58 | +def insecure_connection_response(): |
| 59 | + return HttpResponseForbidden('Secure connection required') |
| 60 | + |
| 61 | + |
| 62 | +def decode_basic_auth(auth): |
| 63 | + authb = b64decode(auth.strip()) |
| 64 | + auth = authb.decode() |
| 65 | + return auth.split(':', 1) |
0 commit comments