Ticket #3583: apache_auth.6.patch
File apache_auth.6.patch, 10.6 KB (added by , 16 years ago) |
---|
-
django/contrib/auth/handlers/modpython.py
1 1 from mod_python import apache 2 2 import os 3 from urllib import quote 4 from django.core import signals 5 from django.dispatch import dispatcher 6 from django.core.handlers.base import BaseHandler 7 from django.core.handlers.modpython import ModPythonRequest 8 from django.conf import settings 9 from django.contrib.auth import authenticate, REDIRECT_FIELD_NAME 10 from django.utils.encoding import iri_to_uri 3 11 4 def authenhandler(req, **kwargs): 12 _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes') 13 14 class ModPythonAuthOptions: 15 def __init__(self, req): 16 options = req.get_options() 17 self.permission_name = options.get('DjangoPermissionName', None) 18 self.staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on")) 19 self.superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off")) 20 self.raise_forbidden = _str_to_bool(options.get('DjangoRaiseForbidden', "off")) 21 self.settings_module = options.get('DJANGO_SETTINGS_MODULE', None) 22 23 def setup_environment(req, options): 5 24 """ 6 Authentication handler that checks against Django's auth database. 25 mod_python fakes the environ, and thus doesn't process SetEnv. This ensures 26 any future imports relying on settings will work. 7 27 """ 8 9 # mod_python fakes the environ, and thus doesn't process SetEnv. This fixes10 # that so that the following import works11 28 os.environ.update(req.subprocess_env) 29 if options.settings_module: 30 os.environ['DJANGO_SETTINGS_MODULE'] = options.settings_module 12 31 13 # apache 2.2 requires a call to req.get_basic_auth_pw() before 14 # req.user and friends are available. 15 req.get_basic_auth_pw() 32 def authenticate_user(user): 33 if user is None: 34 return False 35 if hasattr(user, 'is_authenticated') and not user.is_authenticated(): 36 return False 37 return True 16 38 17 # check for PythonOptions 18 _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes') 39 def validate_user(user, options): 40 if hasattr(user, 'is_active') and not user.is_active: 41 return False 42 if options.staff_only and not getattr(user, 'is_staff', None): 43 return False 44 if options.superuser_only and not getattr(user, 'is_superuser', None): 45 return False 46 # If a permission is required then user must have a has_perm function to 47 # validate. 48 if options.permission_name and (not hasattr(user, 'has_perm') or 49 not user.has_perm(options.permission_name)): 50 return False 51 return True 19 52 20 options = req.get_options() 21 permission_name = options.get('DjangoPermissionName', None) 22 staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on")) 23 superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off")) 24 settings_module = options.get('DJANGO_SETTINGS_MODULE', None) 25 if settings_module: 26 os.environ['DJANGO_SETTINGS_MODULE'] = settings_module 53 def redirect_to_login(req): 54 path = quote(req.uri) 55 if req.args: 56 path = '%s?%s' % (path, req.args) 57 path = quote(path) 58 iri = '%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, path) 59 uri = iri_to_uri(iri) 60 req.err_headers_out.add('Location', uri) 61 if req.proto_num >= 1001: 62 # Use HTTP Error 303 (see other) for HTTP/1.1 browsers. 63 raise apache.SERVER_RETURN, apache.HTTP_SEE_OTHER 64 else: 65 # Otherwise use HTTP Error 302 (moved temporarily). 66 raise apache.SERVER_RETURN, apache.HTTP_MOVED_TEMPORARILY 27 67 28 from django.contrib.auth.models import User 29 from django import db 30 db.reset_queries() 68 def authenhandler(req, **kwargs): 69 """ 70 mod_python authentication handler that checks against Django's auth 71 database. 72 """ 73 options = ModPythonAuthOptions(req) 74 setup_environment(req, options) 31 75 32 # check that the username is valid 33 kwargs = {'username': req.user, 'is_active': True} 34 if staff_only: 35 kwargs['is_staff'] = True 36 if superuser_only: 37 kwargs['is_superuser'] = True 76 dispatcher.send(signal=signals.request_started) 38 77 try: 39 try: 40 user = User.objects.get(**kwargs) 41 except User.DoesNotExist: 78 # This populates req.user too, so it's important to do first. 79 password = req.get_basic_auth_pw() 80 81 # Get the user from any of the installed backends. 82 user = authenticate(username=req.user, password=password) 83 84 if not authenticate_user(user): 85 # Raise unauthorized if the user doesn't authenticate to bring up a 86 # password dialog box to allow the user to authenticate. 42 87 return apache.HTTP_UNAUTHORIZED 43 44 # check the password and any permission given 45 if user.check_password(req.get_basic_auth_pw()): 46 if permission_name: 47 if user.has_perm(permission_name): 48 return apache.OK 49 else: 50 return apache.HTTP_UNAUTHORIZED 51 else: 52 return apache.OK 88 89 # Validate the user 90 if validate_user(user, options): 91 return apache.OK 92 93 # mod_python docs say that HTTP_FORBIDDEN should be raised if the user 94 # authenticates but doesn't validate but Django provides it as an 95 # option, alternately raising HTTP_UNAUTHORIZED again to provide the 96 # option of logging in as an alternate user. 97 if options.raise_forbidden: 98 return apache.HTTP_FORBIDDEN 53 99 else: 54 return apache.HTTP_UNAUTHORIZED 100 return apache.HTTP_UNAUTHORIZED 55 101 finally: 56 db.connection.close() 102 dispatcher.send(signal=signals.request_finished) 103 104 def accesshandler(req): 105 """ 106 mod_python access handler that uses the contrib.auth framework (with 107 sessions, therefore requiring a session cookie). 108 """ 109 options = ModPythonAuthOptions(req) 110 setup_environment(req, options) 111 112 # Set up middleware, now that settings works we can do it now. 113 base_handler = BaseHandler() 114 base_handler.load_middleware() 115 116 dispatcher.send(signal=signals.request_started) 117 try: 118 request = ModPythonRequest(req) 119 120 # Apply request middleware 121 for middleware_method in base_handler._request_middleware: 122 response = middleware_method(request) 123 if response: 124 # If we get a response then there's no need to keep processing 125 # any remaining request middleware. 126 break 127 128 user = getattr(request, 'user', None) 129 if not authenticate_user(user): 130 # Rather than raising HTTP_UNAUTHORIZED (which the browser won't be 131 # able to handle since this isn't basic HTTP authentication), write 132 # a response which redirects to settings.LOGIN_URL 133 redirect_to_login(req) 134 135 if validate_user(user, options): 136 return apache.OK 137 138 # mod_python docs say that HTTP_FORBIDDEN should be raised if the user 139 # authenticates but doesn't validate but Django provides it as an 140 # option, alternately redirecting to login to provide the option of 141 # logging in as an alternate user. 142 if options.raise_forbidden: 143 return apache.HTTP_FORBIDDEN 144 else: 145 redirect_to_login(req) 146 147 finally: 148 dispatcher.send(signal=signals.request_finished) -
docs/howto/apache-auth.txt
22 22 Configuring Apache 23 23 ================== 24 24 25 To check against Django's authorization database from a Apache configuration 26 file, you'll need to use mod_python's ``PythonAuthenHandler`` directive along 27 with the standard ``Auth*`` and ``Require`` directives: 25 To check against Django's authorization database from an Apache configuration 26 file, you can either use mod_python's ``PythonAccessHandler`` directive or 27 the ``PythonAuthenHandler`` directive along with the standard ``Auth*`` and 28 ``Require`` directives. 28 29 30 The ``PythonAccessHandler`` directive validates using the built-in 31 ``contrib.auth`` authentication, which uses the session cookie and redirects to 32 the ``settings.LOGIN_URL`` if authentication is required:: 33 29 34 .. code-block:: apache 30 35 31 36 <Location /example/> 37 SetEnv DJANGO_SETTINGS_MODULE mysite.settings 38 PythonAccessHandler django.contrib.auth.handlers.modpython 39 </Location> 40 41 The ``PythonAuthenHandler`` directive just uses basic HTTP authentication:: 42 43 .. code-block:: apache 44 45 <Location /example/> 32 46 AuthType Basic 33 47 AuthName "example.com" 34 48 Require valid-user … … 110 124 111 125 By default no specific permission will be 112 126 required. 127 128 ``DjangoRaiseForbidden`` If the user authenticates but does not 129 have the valid credentials, raise HTTP 130 Error 403 (forbidden) rather providing a 131 login prompt again. 132 133 Defaults to ``off``. 113 134 ================================ ========================================= 114 135 136 You may also want to make Apache pass the correct headers to stop proxy servers 137 (and local browser caches) from caching your protected resources. Assuming that 138 the ``mod_expires`` and ``mod_headers`` modules are enabled on your Apache 139 server, you can add the following directives to your ``Location`` block:: 140 141 # Stop resources from being cached 142 ExpiresActive On 143 ExpiresDefault A0 144 Header append Cache-Control: "no-cache, no-store, must-revalidate, private" 145 115 146 Note that sometimes ``SetEnv`` doesn't play well in this mod_python 116 147 configuration, for reasons unknown. If you're having problems getting 117 148 mod_python to recognize your ``DJANGO_SETTINGS_MODULE``, you can set it using … … 120 151 121 152 SetEnv DJANGO_SETTINGS_MODULE mysite.settings 122 153 PythonOption DJANGO_SETTINGS_MODULE mysite.settings 154 155 .. _authentication system: ../authentication/ 156 .. _Subversion: http://subversion.tigris.org/ 157 .. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html 158 .. _custom permissions: ../authentication/#custom-permissions 159