I have a user directory in an openldap server under the base DN ou=users,dc=mydomain
. The user entries are uid=user1,ou=users,dc=mydomain
My current ACLs are:
{5}to * by self write by dn="cn=admin,dc=mydomain" write by * none
{4}to dn.subtree="ou=users,dc=mydomain" by self read
{3}to dn.exact="dc=mydomain" attrs=entry by users search by * none
{2}to dn.base="" by * read
{1}to dn.subtree="dc=something,dc=mydomain" by dn="uid=someone,ou=users,dc=mydomain" write by * read
{0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=mydomain" write by * none
Those allow a user to query and find his own entry. But if the authentictated user user1 searches for the base DN with the filter (uid=user1)
set, no entries are found. In other words: ldapsearch -H ldap://myserver -b "ou=users,dc=mydomain" -D "uid=user1,ou=users,dc=mydomain" -x -W
return nothing while ldapsearch -H ldap://myserver -b "uid=user1,ou=users,dc=mydomain" -D "uid=user1,ou=users,dc=mydomain" -x -W
returns the entry for user1
.
How do I need to tweak the ACLs so that the user can find his own entry (and only his own) with a filtered search on the base DN?
Defining OpenLDAP ACLs is tricky.
Most times ACL issues are caused by processing order of ACLs themselves and their who-clauses (by ...) and that ACLs implicitly end with
by * none
stopping the control flow.You're using dynamic configuration with cn=config and this uses X-ORDERED extension in the schema to preserve order of some attribute values like olcAccess.
So the order in which your ACLs are processed is:
So let's look at your ACLs in detail:
{0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=mydomain" write by * none
This is an rule probably taken from some default configuration. It's ok to begin with it but I'd suggest some re-factoring:
write
access, use write-only privilege=w
instead.Better use:
{0}to attrs=userPassword by group="cn=admins,ou=groups,dc=mydomain" =w by self =w by anonymous auth by * none
{1}to dn.subtree="dc=something,dc=mydomain" by dn="uid=someone,ou=users,dc=mydomain" write by * read
If
dc=mydomain
is your database suffix this ACL does not make much sense to me in the context of your question. Maybe a left-over from some tests?{2}to dn.base="" by * read
This is not effective if placed in your database config entry. This must be added to front-end config entry cn=config (formerly in static config slapd.conf before any database section).
{3}to dn.exact="dc=mydomain" attrs=entry by users search by * none
I'd place an ACL like this at the end of the ACL list so it won't stop access to entry at this point. And if you want to use arbitrary search bases like ou=users,dc=mydomain you would have to grant this search right to the whole sub-tree.
So place this at the end of your ACLs and remove {3} or...
{6}to dn.subtree="dc=mydomain" attrs=entry by users search by * none
...move this rule:
{4}to dn.subtree="ou=users,dc=mydomain" by self read
Should probably be last ACL and replace {3}. See comment for {5}.
{5}to * by self write by dn="cn=admin,dc=mydomain" write by * none
Issues here:
by self write
will never be reach because of theby self read
in {4}.by * none
in {4}.Better:
{5}to * by group="cn=admins,ou=groups,dc=mydomain" write by self write by * none
One of the most important debugging option is to start slapd with logging its ACL processing:
/usr/sbin/slapd … -d config,stats,stats2,acl
So these hints should get you a started to work on your ACLs solving your problems yourself. Always think twice. It seems to me you have little bit of contradictive assumptions in your rules.
But there's obviously no way around deep-diving into the docs:
If you want to understand the ACLs in detail you should consult various on-line documentation:
This answer gace me the deciding clue. The only thing I had to alter was the suggested search acl from the top dn (dc=mydomain) to the user dn (ou=users,dc=mydomain)
Changing the ACLs to this does the trick:
{5}to * by self write by dn="cn=admin,dc=mydomain" write by * none {4}to dn.subtree="ou=users,dc=mydomain" by self read {3}to dn.exact="ou=users,dc=mydomain" attrs=entry by users search by * none {2}to dn.base="" by * read {1}to dn.subtree="dc=something,dc=mydomain" by dn="uid=someone,ou=users,dc=mydomain" write by * read {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=mydomain" write by * none