[mod_python] PythonAuthenHandler Question SOLVED

Al Pacifico pacifico at drizzle.com
Sun Jun 5 13:32:10 EDT 2005


I answered my question posed yesterday moments after submitting it. The idea
of subclassing a handler occurred to me, but I realized it was inflexible
(if there were five groups and there were various required combinations I'd
need 5! handlers). The PythonOption directive was what I needed (rather than
the require method) and not thinking of it was a novice move!

I'm posting the handler and the corresponding httpd.conf section in case it
helps anyone. If you look it over and have any input, please post it. To
those who responded to my posts, thanks for your input.

-al

<--- authenticate.py --->

from mod_python import apache
import ldap,ldap.sasl
import example_Config
import re

# this dictionary "client_group_match" contains keys that should match the
first argument to
# PythonOption directives in the httpd.conf file. The values are python
regular expression object
# that determine if the argument to the method (a single string which
represents the distinguished
# methods name of the client) matches the corresponding directive. Calls to
these methods return a
# match object if there is a match and None if there is not.
client_group_match={}
client_group_match['clientadministrator'] = \
	
re.compile('cn=administrators,o=([^,]*),ou=clients,dc=example,dc=com',re.IGN
ORECASE).match
client_group_match['clientagent'] = \
	
re.compile('cn=agents,o=([^,]*),ou=clients,dc=example,dc=com',re.IGNORECASE)
.match
client_group_match['exampleadministrator'] = \
	
re.compile('cn=administrators,ou=groups,dc=example,dc=com',re.IGNORECASE).ma
tch

def authenhandler(req):

	# import our configuration file to find our LDAP server
	config = Config.Config()

	# show the password dialog, retrieve password and user
	pw = req.get_basic_auth_pw()
	email = req.user

	# get a sasl authentication object
	sasl_auth =
ldap.sasl.sasl({ldap.sasl.CB_AUTHNAME:email,ldap.sasl.CB_PASS:pw},'DIGEST-MD
5')
	
	# open a connection to our LDAP server
	# initialize LDAPObject with domain name / ip of our sever
	l = ldap.open(config["LDAP:server"])

	# attempt to bind to the LDAP server
	try:
		l.sasl_interactive_bind_s("",sasl_auth)
			
		try:
			# retrieve client's distinguished name (dn), remove
the prepended dn: that the
			# LDAPObject.whomai_s() method returns, and
normalize it to lower case
			client_dn = l.whoami_s().lower()[3:]
			
			# Access to this page is restricted to whom?
			restricted_to =
req.get_options().get('permitted',None)

			if restricted_to:
				# process arguments to the 'permitted'
PythonOption : split on commas, remove whitespace
				# at end and beginning, condense whitespace
between the words to a single space, and
				# normalize to all lower case
				restricted_to=['
'.join(perm_args.split(None)).lower() for perm_args in
restricted_to.split(',')]
				
				# determine which groups the client belongs
to by searching all the groups in the LDAP tree
				client_groups=[]
				# the following code is dependent on the
ldap directory structure
	
groups=l.search_s("dc=example,dc=com",ldap.SCOPE_SUBTREE,"objectclass=groupO
fNames",["member"])
				for (group_dn,group_attr) in groups:
					# lower case the distinguished names
of the members of the group to permit string
					# equality comparison with the
distinguished name returned by the LDAPObject.whoami_s()
					# method
					group_attr['member'] = [s.lower()
for s in group_attr['member']]
	
					# debugging
					req.log_error('%s contains %s' %
(group_dn,group_attr['member']))
				
					# if the client's dn is a member of
the group, add the group to our list
					if client_dn in
group_attr['member']: client_groups.append(group_dn.lower())

				# test if the dn of any member of client's
groups matches the dn's of the groups which are
				# permitted to access this request At this
point, client is authenticated and only possible
				# results are HTTP_FORBIDDEN and OK
				result = apache.HTTP_FORBIDDEN
				for client_role in client_groups:
					if result == apache.HTTP_FORBIDDEN:
						for role_permitted in
restricted_to:
							if
client_group_match.has_key(role_permitted) and \
	
client_group_match[role_permitted](client_role):
								result =
apache.OK
							else:
								# require
directive contains bad arguments - log this to the  error log
	
req.log_error('Bad arguments to require directive at %s.' % req.filename)
					else: break

				return result
			else:
				# restricted_to was None, so there are no
restrictions.
				return apache.OK
		finally:
			l.unbind()
	except ldap.SERVER_DOWN,e:
		# can't contact the LDAP server
		return apache.HTTP_INTERNAL_SERVER_ERROR
	except ldap.LDAPError,e:
		# client couldn't authenticate. Returning HTTP_UNAUTHORIZED
gives him an opportunity to try again
		return apache.HTTP_UNAUTHORIZED

httpd.conf section follows:

PythonPath "sys.path+['/usr/var/www/lib']"
# Following did not work as expected
# PythonImport PMHx_Config powell
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory "/usr/var/www/htdocs">
    AddHandler mod_python .psp
    PythonHandler mod_python.psp
    PythonDebug On
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>
<Directory "/usr/var/www/htdocs/administration">
    AddHandler mod_python .psp
    PythonHandler mod_python.psp
	PythonAuthenHandler authenticate
	PythonOption permitted ExampleAdministrator
	PythonDebug On
	AuthType Basic
	AuthName "Example Administration"
	require valid-user
</Directory>

Al Pacifico
Seattle, WA






More information about the Mod_python mailing list