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
|