Graham Dumpleton
graham.dumpleton at gmail.com
Mon Sep 24 07:21:46 EDT 2007
On 24/09/2007, Arnar Birgisson <arnarbi at gmail.com> wrote: > On 9/24/07, Graham Dumpleton <graham.dumpleton at gmail.com> wrote: > > On 24/09/2007, Arnar Birgisson <arnarbi at gmail.com> wrote: > > > Can I return apache.HTTP_UNAUTHORIZED from a fixup-handler to make the > > > browser request username/passwd? > > > > Technically you can. The issue will be that if you have defined > > AuthType etc then the earlier auth handler phase may result in it not > > getting that far. > > Would I perform the authentication in the fixup handler as well? > Basically just do it all there? > > 1. find project name > 2. lookup project in db > 3. if anon access allowed - apache.OK > 4. call req.get_basic_auth_pw() > 5. lookup user - apache.HTTP_UNAUTHORIZED if not found > 6. check passwd - apache.HTTP_UNAUTHORIZED if no match > 7. check for user access - apache.OK if allowed > 8. apache.HTTP_UNAUTHORIZED otherwise But then you may as well do it all in the authentication handler and just use 'Require valid-user' as the authorisation phase, although you are strictly mixing up the intent of what the phases are all about. Thus we get back to perhaps just doing it properly in the first place but where mod_python doesn't exactly make it easy. This is partly because of the incomplete exposure of the Apache APIs through mod_python, partly because mod_python Basic authentication handling is historically incorrect and has encouraged sloppy practices and partly because of incomplete documentation for mod_python on how to do it properly. :-( At this point I scream and wish I had finished the auth/authz support in mod_wsgi which will just make this all so much easier. :-) For example, in mod_wsgi (unreleased 2.0), your Apache configuration would be something (names of things still changing) like: <AuthnProviderAlias wsgi django> WSGIAuthenticationGroup django AuthWSGIUserScript /usr/local/django/mysite/apache/auth.wsgi </AuthnProviderAlias> WSGIScriptAlias / /usr/local/django/mysite/apache/django.wsgi <Directory /usr/local/django/mysite/apache> Order deny,allow Allow from all WSGIApplicationGroup django AuthType Basic AuthName "Django Site" AuthBasicProvider django Require valid-user </Directory> Here the authentication is being applied to the Django site itself, but could also be applied to Trac /login URL or Subversion access. The auth script would then just be: import os, sys sys.path.append('/usr/local/django') os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' import apache.mod_auth from django.contrib.auth.models import User from django import db def check_password(environ, user, password): db.reset_queries() kwargs = {'username': req.user, 'is_active': True} try: try: user = User.objects.get(**kwargs) except User.DoesNotExist: return apache.mod_auth.AUTH_USER_NOT_FOUND if user.check_password(password): return apache.mod_auth.AUTH_GRANTED else: return apache.mod_auth.AUTH_DENIED finally: db.connection.close() None of the worrying about HTTP headers, password decoding etc as Apache does that all for you. You only have to worry about the password check. The bit I am still working on is the authorisation, ie., group access bits. Using your example, this would be something like: Require wsgi-group svn_read <LimitExcept GET PROPFIND OPTIONS REPORT> Require wsgi-group svn_write </LimitExcept> Am looking at couple of different options at present as to how this would be checked on Python side. One is to have: def groups_for_user(environ, user): db.reset_queries() kwargs = {'username': req.user, 'is_active': True} try: try: user = User.objects.get(**kwargs) except User.DoesNotExist: return None return user.groups finally: db.connection.close() The mod_wsgi module would then process the Require directives and deliver the necessary response. In the greater scheme of things, all much simpler. In the mean time, I'll try and find some time to explain how to do it for mod_python properly. Cant promise anything though as starting to get backlogged as it is. Graham
|