Ticket #3583: apach_auth.7.patch
File apach_auth.7.patch, 10.5 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.core.handlers.base import BaseHandler 6 from django.core.handlers.modpython import ModPythonRequest 7 from django.conf import settings 8 from django.contrib.auth import authenticate, REDIRECT_FIELD_NAME 9 from django.utils.encoding import iri_to_uri 3 10 4 def authenhandler(req, **kwargs): 11 _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes') 12 13 class ModPythonAuthOptions: 14 def __init__(self, req): 15 options = req.get_options() 16 self.permission_name = options.get('DjangoPermissionName', None) 17 self.staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on")) 18 self.superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off")) 19 self.raise_forbidden = _str_to_bool(options.get('DjangoRaiseForbidden', "off")) 20 self.settings_module = options.get('DJANGO_SETTINGS_MODULE', None) 21 22 def setup_environment(req, options): 5 23 """ 6 Authentication handler that checks against Django's auth database. 24 mod_python fakes the environ, and thus doesn't process SetEnv. This ensures 25 any future imports relying on settings will work. 7 26 """ 8 9 # mod_python fakes the environ, and thus doesn't process SetEnv. This fixes10 # that so that the following import works11 27 os.environ.update(req.subprocess_env) 28 if options.settings_module: 29 os.environ['DJANGO_SETTINGS_MODULE'] = options.settings_module 12 30 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() 31 def authenticate_user(user): 32 if user is None: 33 return False 34 if hasattr(user, 'is_authenticated') and not user.is_authenticated(): 35 return False 36 return True 16 37 17 # check for PythonOptions 18 _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes') 38 def validate_user(user, options): 39 if hasattr(user, 'is_active') and not user.is_active: 40 return False 41 if options.staff_only and not getattr(user, 'is_staff', None): 42 return False 43 if options.superuser_only and not getattr(user, 'is_superuser', None): 44 return False 45 # If a permission is required then user must have a has_perm function to 46 # validate. 47 if options.permission_name and (not hasattr(user, 'has_perm') or 48 not user.has_perm(options.permission_name)): 49 return False 50 return True 19 51 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 52 def redirect_to_login(req): 53 path = quote(req.uri) 54 if req.args: 55 path = '%s?%s' % (path, req.args) 56 path = quote(path) 57 iri = '%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, path) 58 uri = iri_to_uri(iri) 59 req.err_headers_out.add('Location', uri) 60 if req.proto_num >= 1001: 61 # Use HTTP Error 303 (see other) for HTTP/1.1 browsers. 62 raise apache.SERVER_RETURN, apache.HTTP_SEE_OTHER 63 else: 64 # Otherwise use HTTP Error 302 (moved temporarily). 65 raise apache.SERVER_RETURN, apache.HTTP_MOVED_TEMPORARILY 27 66 28 from django.contrib.auth.models import User 29 from django import db 30 db.reset_queries() 67 def authenhandler(req, **kwargs): 68 """ 69 mod_python authentication handler that checks against Django's auth 70 database. 71 """ 72 options = ModPythonAuthOptions(req) 73 setup_environment(req, options) 31 74 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 75 signals.request_started.send(None) 38 76 try: 39 try: 40 user = User.objects.get(**kwargs) 41 except User.DoesNotExist: 77 # This populates req.user too, so it's important to do first. 78 password = req.get_basic_auth_pw() 79 80 # Get the user from any of the installed backends. 81 user = authenticate(username=req.user, password=password) 82 83 if not authenticate_user(user): 84 # Raise unauthorized if the user doesn't authenticate to bring up a 85 # password dialog box to allow the user to authenticate. 42 86 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 87 88 # Validate the user 89 if validate_user(user, options): 90 return apache.OK 91 92 # mod_python docs say that HTTP_FORBIDDEN should be raised if the user 93 # authenticates but doesn't validate but Django provides it as an 94 # option, alternately raising HTTP_UNAUTHORIZED again to provide the 95 # option of logging in as an alternate user. 96 if options.raise_forbidden: 97 return apache.HTTP_FORBIDDEN 53 98 else: 54 return apache.HTTP_UNAUTHORIZED 99 return apache.HTTP_UNAUTHORIZED 55 100 finally: 56 db.connection.close() 101 signals.request_finished.send(None) 102 103 def accesshandler(req): 104 """ 105 mod_python access handler that uses the contrib.auth framework (with 106 sessions, therefore requiring a session cookie). 107 """ 108 options = ModPythonAuthOptions(req) 109 setup_environment(req, options) 110 111 # Set up middleware, now that settings works we can do it now. 112 base_handler = BaseHandler() 113 base_handler.load_middleware() 114 115 signals.request_started.send(None) 116 try: 117 request = ModPythonRequest(req) 118 119 # Apply request middleware 120 for middleware_method in base_handler._request_middleware: 121 response = middleware_method(request) 122 if response: 123 # If we get a response then there's no need to keep processing 124 # any remaining request middleware. 125 break 126 127 user = getattr(request, 'user', None) 128 if not authenticate_user(user): 129 # Rather than raising HTTP_UNAUTHORIZED (which the browser won't be 130 # able to handle since this isn't basic HTTP authentication), write 131 # a response which redirects to settings.LOGIN_URL 132 redirect_to_login(req) 133 134 if validate_user(user, options): 135 return apache.OK 136 137 # mod_python docs say that HTTP_FORBIDDEN should be raised if the user 138 # authenticates but doesn't validate but Django provides it as an 139 # option, alternately redirecting to login to provide the option of 140 # logging in as an alternate user. 141 if options.raise_forbidden: 142 return apache.HTTP_FORBIDDEN 143 else: 144 redirect_to_login(req) 145 146 finally: 147 signals.request_finished.send(None) -
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