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:
{0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=mydomain" write by * none
{1}to dn.subtree="dc=something,dc=mydomain" by dn="uid=someone,ou=users,dc=mydomain" write by * read
{2}to dn.base="" by * read
{3}to dn.exact="dc=mydomain" attrs=entry by users search by * none
{4}to dn.subtree="ou=users,dc=mydomain" by self read
{5}to * by self write by dn="cn=admin,dc=mydomain" write by * none
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:
- Omit attribute shadowLastChange since LDAP shadow is broken concept. And if you do use shadow maps, this ACL would allow the user to circumvent shadow password expiry.
- Change the order of who-clauses. In general follow the rule to mention the "higher" privileges first.
- If cn=admin,dc=mydomain is already the rootdn of this database you don't need that who-clause.
- Better grant password admin rights to a group.
- Do not grant read privilege at all by inclusive
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:
- The
by self write
will never be reach because of the by self read
in {4}.
- This ACL is not reached at all because of the implicit
by * none
in {4}.
- again: granting access to rootdn is not necessary
- again: better grant admin access to a group
- use better order of who-clauses
- reconsider if you really want to grant write access to all attributes to the user himself
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: