Login

apache authentication via cookies

Author:
sean
Posted:
March 1, 2007
Language:
Python
Version:
Pre .96
Score:
7 (after 7 ratings)

Enables cookie based authentication with apache. I needed user authentication for some static files, but couldn't use the method described here as this prompts the user for his credentials, making him log in twice. There is some overhead in the code, because it runs all request middleware components (only session and auth would be needed). All arguments described in the link above are supported. I use it like this in the apache config:

<Location "/protected/location">
            PythonPath "['/path/to/proj/'] + sys.path"  
            PythonOption DJANGO_SETTINGS_MODULE myproj.settings
        PythonOption DjangoPermissionName '<permission.codename>'
        PythonAccessHandler my_proj.modpython #this should point to accesshandler
            SetHandler None
</Location>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import os
from mod_python import apache
from django.core.handlers.base import BaseHandler
from django.core.handlers.modpython import ModPythonRequest

class AccessHandler(BaseHandler):
    def __call__(self, req):
        from django.conf import settings
        # set up middleware
        if self._request_middleware is None:
            self.load_middleware()
	# populate the request object
	request = ModPythonRequest(req)
	# and apply the middleware to it
	# actually only session and auth middleware would be needed here
	for middleware_method in self._request_middleware:
		middleware_method(request)
	return request

def accesshandler(req):
    os.environ.update(req.subprocess_env)
    
    # check for PythonOptions
    _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes')

    options = req.get_options()
    permission_name = options.get('DjangoPermissionName', None)
    staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on"))
    superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off"))
    settings_module = options.get('DJANGO_SETTINGS_MODULE', None)
    
    if settings_module:
        os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
    
    request=AccessHandler()(req)
    if request.user.is_authenticated():
	if superuser_only and request.user.is_superuser:
	    return apache.OK
	elif staff_only and request.user.is_staff:
	    return apache.OK
	elif permission_name and request.user.has_perm(permission_name):
	    return apache.OK
    return apache.HTTP_UNAUTHORIZED

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

andybak (on May 7, 2007):

I assume this requires mod_python rather than say fcgi?

#

sean (on May 9, 2007):

Yes, wouldn't know how to do this with fcgi. It would probably depend on which server you are using, and how you are able to define handlers for that server.

#

cwurld (on May 19, 2007):

Hi - Thanks for the snippet! Its a real lifesaver for me.

I had to make a few modifications to get it to work for me. First "request.user.is_authenticated()" always comes back as true. I am using IE7 with cookies that expire when the browser window is closed. I solved the problem with the following code:

if request.user.is_anonymous(): return apache.HTTP_UNAUTHORIZED

added before request.user.is_authenticated().

Next, I am using the code to server static pages through apache. In my httpd.conf file, when I had the line:

SetHandler python-program

The requested ended up being served by django. I was able to get apache to serve the content by replacing that line with:

SetHandler None

My (noob) understanding of apache is that it processes the request in phases and the phase called "handler" is the last phase in the sequence. So the python access handler still gets called, but after that the request processing is completed by apache.

Chuck

#

wolf905 (on August 9, 2008):

Thanks for this snippet. I have one comment, which I hope will save some other folks time. Initially, I had this in my httpd.conf:

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE django.settings
    PythonPath "['/path/to/django/project/']  + sys.path"
</Location>
<Location "/media">
    SetHandler none
</Location>
<Location "/media/reciprocals">
        PythonPath "['/path/to/django/project/'] + sys.path"
        PythonOption DJANGO_SETTINGS_MODULE django.settings
        PythonOption DjangoPermissionName 'django.permission'
        PythonAccessHandler django.apache_accesshandler
        SetHandler None
</Location>

Which threw this lovely error:

    File "/usr/lib/python2.4/os.py", line 463, in __setitem__
    putenv(key, item)

TypeError: putenv() argument 2 must be string, not list

I figured out that somehow multiple req objects were being processed by the handler at the same time, thus giving me multiple copies of the environment. I fixed this by rearranging my Location directives as so:

<Location "/media/reciprocals">
        PythonPath "['/path/to/django/project/'] + sys.path"
        PythonOption DJANGO_SETTINGS_MODULE django.settings
        PythonOption DjangoPermissionName 'django.permission'
        PythonAccessHandler django.apache_accesshandler
        SetHandler None
</Location>
<Location "/media">
    SetHandler none
</Location>
<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE django.settings
    PythonPath "['/path/to/django/project/']  + sys.path"
</Location>

I'm not familiar at all with the way apache works with mod_python, but this reconfiguration of my httpd.conf makes things work as I expect them to. So I hope this helps save someone else some frustration.

-Pat

#

Please login first before commenting.