member/memberOf and samldb.c

tridge at tridge at
Mon Aug 29 00:00:34 GMT 2005

Simo and Andrew,

I'm looking at what we need in the backend to do the user management
code right for the web interface (and command line interface too for
that matter).

Currently our provision code sets up both the 'member' attribute of
the user and the memberOf attribute of the group separately. The
problem with this approach is it is all too easy for the two to get
out of sync, plus its not what windows ldap tools will expect. 

A common symptom is that deleting the user still leaves the member
fields of some groups pointing at a now invalid DN. Then when we
re-add the user the 2nd part of the add fails, as modifying the group
to add the users DN gives an error that the attribute already exists.
All of this is really just a symptom of a deeper problem that we don't
tie attributes in different records together correctly.

Experimenting with a w2k3 ldap server shows that:

 - to add a user to a group you just add the 'member' field of the
   group, pointing at the users DN. The corresponding 'memberOf' field
   is automatically created.

 - any attempts to modify the 'memberOf' field directly fail with a

 - the users primary group does not show up as a memberOf field of the
   user, and also does not show up as a 'member' field on the group,
   it only shows up as the primaryGroupID field of the user record

 - when you change primary groups for a user the old group gets the
   'member' field added, and the new group gets the 'member' field
   removed automatically. So 3 records are changed from one ldap
   modify (the ldap modify is on the primaryGroupID attribute)

So I've been thinking about how we should handle this type of detail
in our ldb code. I can see two workable approaches:

 1) we could not store the memberOf fields in the user record in ldb
    at all, and instead generate them on the fly in the samldb.c ldb
    module. This could be made efficient using an indexing trick (more
    on that below).

 2) alternatively, we could store the memberOf fields as we do now,
    but add hooks on modify/delete/rename in samldb.c to try to
    enforce the above rules, and auto-update everything, hoping we
    never get out of sync (or perhaps adding some vacuuming code to
    periodically check for consistency).

We've discussed the possibility of (1) before, and I think we decided
against it due to the search costs of searching for all group records
that have member=$USERS_DN every time we want to look at a user
record. For example, right now if we do:

  ldbsearch (member=CN=testuser,CN=Users,DC=vnet3,DC=tridgell,DC=net) dn

and the user is a member of 1000 groups then the ldb_tdb backend will
load 1000 records. That would make auto-generating the memberOf fields
pretty expensive! 

We could avoid this though due to the way the ldb_tdb indexing
works. The index records just store DNs, so in fact the backend could
answer the above query by loading a single record. We'd just need to
teach ldb_tdb/ldb_index.c that when a query is only looking for the
'dn' attribute that it can stop after loading the index record and
just return that list, without loading the records themselves.

That would mean we could get perfect coherence between the member and
memberOf attributes by changing samldb.c to generate the memberOf
attributes on searches for the user record by doing the above search,
and adding it to the users record as memberOf values.

Any comments on which approach you prefer? Any other approaches?

Note that with either approach we'd still need special case code to
handle the primaryGroupID attribute, although it will be simpler with
approach (1) than approach (2), as we won't need to mess with memberOf

as a side note, this is exactly the sort of problem where we could
benefit a lot from transaction capabilities in a ldb backend. If
derrell does manage to make the sqlite3 stuff fast enough then it
could be a big win for this sort of tied attribute management.

Cheers, Tridge

More information about the samba-technical mailing list