All, Just a recipe for exUserFolder with PAM authentication that works for me. 1) Download the PyPAM module from http://www.pangalactic.org/PyPAM/PyPAM-0.4.2.tar.gz 2) Edit the PAMmodule.c file so it includes the "python2.1/Python.h" rather than the "python1.5/Python.h" 3) Compile it: gcc -DPACKAGE=1 -DVERSION=1 -I. -I. -I/usr/include/python2.1 -I/usr/lib/python2.1/config -fpic -g -O2 -c PAMmodule.c gcc -shared -o PAMmodule.so PAMmodule.o -lpam -lpam_misc 4) Make sure it works by running python2.1 and "import PAM" 5) Copy PAMmodule.so to /usr/local/Zope/lib/python 6) Create the file /usr/local/Zope/Extensions/passwd.py with the contents: import PAM class Checker: def __init__(self,username,password): self.username = username self.password = password def pam_conv(self, auth, query_list): resp = [] for query,type in query_list: if type == PAM.PAM_PROMPT_ECHO_ON: resp.append((self.username, 0)) elif type == PAM.PAM_PROMPT_ECHO_OFF: resp.append((self.password, 0)) elif type == PAM.PAM_PROMPT_ERROR_MSG or type == PAM.PAM_PROMPT_TEXT_INFO: resp.append(('', 0)); else: return None return resp def check(username,password): ckr = Checker(username,password) auth = PAM.pam() auth.start('zope') auth.set_item(PAM.PAM_USER, username) auth.set_item(PAM.PAM_CONV, ckr.pam_conv) try: auth.authenticate() except PAM.error, (resp, code): return (1, 'No: (%s)' % resp) except: return (1, 'Internal error') else: return (0, 'Yes') 7) Download and install exUserFolder>=0.7.2 8) cd /usr/local/Zope/lib/python/Products/exUserFolder/usAuthSource 9) vim usAuthSource.py <comment out line 134> <uncomment lines 136-139> <ESC>!wq 10) Create a test folder 11) Put an exUserFolder in it with a Null property source, a Null membership source, and a user-supplied auth source 12) In the managment interface, go into the exUserFolder, then contents, then usAuthSource 13) Create an external method called "check_password", module "password", function "check" 14) Create the following PythonScripts: x_usGetUsers: <no parameters> us = [] us.append({'username':'uname','password':'x','roles':['role1','role2']}) return us x_usListOneUser: username return [{'username':'uname','password':'x','roles':['role1','role2']},] # return [] for no such user x_usListUsers: <no parameters> return [ {'username':'uname',}, {'username':'uname2',}, {'username':'uname3',} ] x_usRemoteAuthMethod: username,password code,msg = container.check_password(username,password) if code: return None return 1 Clearly the data returned from these methods can be from SQL/LDAP queries or whatever. Beware - the usRemoteAuthMethod in particular will need a proxy role that can run the external method, since the logging in user doesn't have any roles... 14a) Give the appropriate proxy roles to each python script so it can be executed in the context of a logging-in user 15) Create the /etc/pam.d/zope file with the appropriate PAM configuration 16) Remove permissions for Anonymous on the folder so that you have to login 17) Copy your test folder (parent of the exUserFolder) to somewhere safe in case it's going to go wrong 18) Batch rename the x_* method to remove the leading x_ 19) Try logging in - watch /var/log/messages to see the output of the PAM module The one problem is that the usRemoteAuthMethod gets called on every page request, it isn't cached, which means for remote auth methods (e.g. Kerberos/LDAP/SMB PAM modules) the load might be quite high. Any ideas, let me know. Regards, Phil +------------------------------------------+ | Phil Mayers | | Network & Infrastructure Group | | Information & Communication Technologies | | Imperial College | +------------------------------------------+