Archive for August, 2005

Open Directory: Pretending to use another schema for OpenCMS.

Thursday, August 18th, 2005

So we've been investigating a new CMS for the COFA website, and we've settled upon OpenCMS, which is quite excellent so far, especially considering the fact that it's open source and free.

The only problem is that we needed LDAP authentication which isn't a part of the distribution.

Ah, but there is a commerical LDAP plugin available from Alkacon, as part of their OpenCMS Enterprise Extensions package, which also includes a Transaction Manager, an Accelerator and a VFS Doctor.

The problem I quickly ran into is that while user authentication works happily, groups are another matter. The solution .. ?

The LDAP Connector expects group records to look like:

dn: ou=cmsgroup,ou=groups,dc=mydomain,dc=edu,dc=au
cn: cmsgroup
objectClass: posixGroup
objectClass: extensibleObject
uniqueMember: uid=michelfoucault,cn=users,dc=mydomain,dc=edu,dc=au
uniqueMember: uid= gottlobfrege,cn=users,dc=mydomain,dc=edu,dc=au

which isn't how Apple do it with Open Directory, for their groups look like:

dn: cn=cmsgroup,cn=groups,dc=mydomain,dc=edu,dc=au
cn: cmsgroup
objectClass: posixGroup
objectClass: extensibleObject
memberUid: michelfoucault
memberUid: gottlobfrege

It's not the container types (cn, ou) that are the problem, for the LDAP connector is more than capable of allowing that, it's the way that members are specified that is the problem.

Apple simply list the username as a 'memberUid' property, but the LDAP connector expects things to be the way lots of other LDAP implementations do it, where the full user record is specified, not just the username.

After I was thinking about this for a while, Mike Bartosh suggested something that I really should have picked up straight away.

Simply construct another branch of group records in my LDAP directory that contains the information in the appropriate format.

I decided to take all Open Directory groups whose name started with “cms” and construct the appropriate group record in another branch.

So this script will take all such groups, and construct the appropriate group records in “ou=cmsgroups,dc=mydomain,dc=edu,dc=au”. I made them as ou rather than cn, mainly because I've gotten into the habit of making non-Apple LDAP data as that container type. It's much of a muchness really.

#!/bin/sh

ldiffile="/tmp/cmsgroups.ldif"
odserver="odserver.mydomain.edu.au"
odadmin="myodadmin"
odpass="myodpassword"
ldapdomain="dc=mydomain,dc=edu,dc=au"

rm -f $ldiffile

groups=$(/usr/bin/ldapsearch -LLL -w $odpass -h $odserver \
        -D "cn=$odadmin,$ldapdomain" \
        -b "cn=groups,$ldapdomain" \
        '(&(objectclass=posixgroup)(cn=cms*))' \
        cn 2> /dev/null | /usr/bin/grep "cn:" | /usr/bin/sed 's|cn: ||g'  )

echo "dn: ou=cmsgroups,$ldapdomain" >> $ldiffile
echo "objectClass: top" >> $ldiffile
echo "objectclass: organizationalUnit" >> $ldiffile
echo "ou: cmsgroups" >> $ldiffile
echo "" >> $ldiffile

for group in $groups
do
        echo "dn: ou=$group,ou=cmsgroups,$ldapdomain" >> $ldiffile
        echo "cn: $group" >> $ldiffile
        echo "objectClass: posixGroup" >> $ldiffile
        echo "objectClass: extensibleObject" >> $ldiffile
        members=$(/usr/bin/ldapsearch -LLL -w $odpass -h $odserver \
                          -D "cn=$odadmin,$ldapdomain" \
                          -b "cn=$group,cn=groups,$ldapdomain" \
                          memberUid 2> /dev/null | /usr/bin/grep memberUid | /usr/bin/sed 's|memberUid: ||g');
        for member in $members
        do
                echo "uniqueMember: uid=$member,cn=users,$ldapdomain" >> $ldiffile
        done
        echo "" >> $ldiffile
done

/usr/bin/ldapdelete -w $odpass -D "cn=$odadmin,$ldapdomain" -r "ou=cmsgroups,$ldapdomain"
/usr/bin/ldapadd -w $odpass -D "cn=$odadmin,$ldapdomain" -f $ldiffile

So with this running each half an hour, my other support staff can still use Workgroup Manager to create groups with the prefix “cms”, add users to them, and the script will create the necessary groups and membership info as required, in a completely separate LDAP branch.

The OpenCMS LDAP connector can then happily read the data out of the “ou=cmsgroups” branch, rather than “cn=groups”. This is easily configured in the LDAP connector config setup.

Using this approach, we can maintain the integrity of group records the way Apple create and use them, and yet we can still get the flexibility of being able to use LDAP plugins that may expect group records to be in a different format.