ActiveDirectory with read and write functionality
Software environment:
- CMFPlone 2.1.2
- LDAPUserFolder 2.6
- GroupUserFolder 3.5
- Windows Small Business Server 2003 for ActiveDirectory
Software
versions are for reference only. Your environment may be different but
there is a good chance that this howto still applies.
Setting up your acl_users
This applies to the acl_users of your Plone site, not the acl_users in Zope!
The
goal is to add a new User source (LDAP) and make it the only User
source, and replace the Groups source with LDAPGroupFolder.
- In the ZMI navidate to acl_users | Sources
- Select Add LDAPUserFolder and click Add. You will be redirected.
- Click the Schema tab and enter values as in the screenshot.

- Click the Configure tab and enter values as in the screenshot. Replace Users Base DN, Groups Base DN and Manager DN with your AD settings.

- Add an LDAP server (basically enter the IP address of your AD server)
- Type in the address bar of your browser $PLONE_URL/acl_users/manage_main. If you actually type $PLONE_URL then this howto is not for you. Add a PythonScript called gruf_ldap_required_fields with parameter login and with content
# For Active Directory
di = {}
di['userAccountControl'] = '66048'
di['sAMAccountName'] = login
di['accountExpires'] = '9223372036854775807'
# Extract FQDN from configuration
# context.groups_base is something similar to "ou=dha-sa,dc=Upfront,dc=local". We want Upfront@local
fqdn = ''.join(context.groups_base[context.groups_base.lower().find(',dc=')+1:])
fqdn = fqdn.replace('dc=', '')
fqdn = fqdn.replace('DC=', '')
fqdn = fqdn.replace(',', '.')
di['userPrincipalName'] = '%s@%s' % (login, fqdn)
di['pwdLastSet'] = '-1'
return di
- Navigate to acl_users | Sources
- Replace Groups Source with LDAPGroupFolder
- Navigate to acl_users | Sources
- Disable the original User Folder

- Go to the Management Console for AD on your Windows server. You probably already have an Organizational Unit where you want users and groups to be stored. In the screenshot my Organizational Unit is called dha-sa. Manually add new groups called Manager, Reviewer, Member, Administrators, Reviewers. The first three will be mapped to Zope roles, the last two to Plone groups. If you have other roles and groups add them to AD now. Even though LDAPUserFolder will map groups automatically in future we have to do the initial mapping.

- Click the new LDAP User Folder
- Click the Groups tab
- Some of the mappings in LDAP group to Zope role mappings may be wrong at this point. Delete the wrong ones and remap. The screenshot shows the correct setting.

Monkey patches
There are a few problem areas in the code which wreak havoc.Unpack ActiveDirectoryPatches-0.1.2.tar.gz to your Products directory and restart Zope. These patches address the problems and will hopefully surface in the standard distribution in future. Note: monkey patches don't modify any existing source code.Issues
Groups may have roles in Plone, and LDAPUserFolder does correctly store the roles for groups in ActiveDirectory, but it cannot retrieve these roles! I wrote a horrible hardcoded method to address this issue. Any help from expert LDAPUserFolder users (Jens...) is appreciated.This method lives in GroupUserFolder.GRUFUser on the GRUFGroup class. It overrides getRoles from GRUFUserAtom.
security.declarePublic('getRoles')
def getRoles(self):
"""
Overriding GRUFUserAtom.getRoles allows retrieval of Roles for Groups stored in LDAP.
Storage of Roles for Groups in LDAP already works.
"""
if self._all_roles is not None:
return self._all_roles
# xxx: Ugly hardcoded stuff - fix!!!
gruf = self.aq_parent
import zLOG
if gruf.haveLDAPGroupFolder():
import ldap
l = ldap.open("192.168.1.63", port=389)
l.simple_bind_s('administrator', 'password')
baseDN = "dc=Upfront,dc=local"
searchScope = ldap.SCOPE_SUBTREE
retrieveAttributes = ['memberOf']
ldap_result_id = l.search(baseDN, searchScope, '(&(cn=%s)(objectClass=group))' % self, retrieveAttributes)
res = []
while 1:
result_type, result_data = l.result(ldap_result_id, 0)
if (result_data == []):
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
try:
# Extract role
s = result_data[0][1]['memberOf'][0]
res.append(''.join(s[3:s.find(',')]))
except:
pass
return res
else:
return GRUFUserAtom.getRoles(self)
This approach may be wrong so use at your own discretion.
Author: