|
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
|