Routine directory work does not require sharing Directory Manager credentials. Delegated administration assigns narrow LDAP rights to ordinary users through administrator groups and groupdn ACIs scoped to specific subtrees, attributes, and operations.
This guide is a scenario guide, not another ACI syntax primer. We'll create administrator groups, separate read, password, user, and group responsibilities, block privilege escalation through the aci attribute, and verify allowed and denied operations as real delegated accounts.
Before you start:
- ACI examples — syntax, rights,
target,targetattr, andldapmodifyworkflow - Manage users and groups —
dsidmuser and group lifecycle - Advanced ACI —
targetfilter,target_from,target_to, and compound bind rules - Macro ACIs — repeated multi-tenant trees with
($dn),[$dn], and attribute substitution - Password policy — password administrators configured with
--pwdadmin - Self-service password and profile ACIs — permissions on a user's own entry
- Rename and move entries — Modify DN operations paired with move targets
Tested on: Rocky Linux 10.2; 389 Directory Server 3.2.0.
These examples use ldap://localhost:389 on the Directory Server host for a local lab. For remote or production administration, use LDAPS or StartTLS so bind credentials, password changes, and directory data are not transmitted over an unencrypted connection.
How delegated administration works
Delegated administrator
|
| member of
v
LDAP administrator group
|
| matched by groupdn ACI
v
Permitted operations on one directory subtreeDelegation normally combines four pieces:
| Piece | Purpose |
|---|---|
| Administrator group | Holds member DNs for each role |
groupdn ACI |
Grants rights to every current group member |
target / targetattr |
Limits entries, attributes, and operations |
| Placement | Stores the ACI on a container that inherits to the intended subtree |
Grant rights through groups, not individual userdn rules, so onboarding and offboarding changes group membership only.
Keep delegated operator accounts outside every subtree you delegate to them. If help-desk or user-administrator ACIs target ou=people, operator binds must not live under ou=people. Otherwise one delegated role can reset or delete another operator's account.
Plan the delegated administration roles
| Role | Scope | Permitted operations |
|---|---|---|
| User reader | ou=people |
read, search, compare on profile attributes |
| Help desk | ou=people |
Locate users; reset userPassword; manage account lock state |
| User administrator | All uid entries below ou=people |
Create, modify, and delete user entries |
| Group administrator | ou=ApplicationGroups,ou=Groups |
Manage application groups and member values |
| Department administrator | One departmental OU | Manage users only below that OU |
This lab implements the first three roles in full, a shorter group-administrator scenario, and an Engineering department administrator. Password-policy password administrators stay in the password policy chapter. More advanced attribute-value policies and compound filter designs stay in advanced ACI.
Prepare administrator users and groups
The lab uses instance ldap1, suffix dc=example,dc=com, and a protected layout under ou=Administrators. Operator accounts bind from ou=Operators; role groups sit alongside them. End-user data stays under ou=people and application groups under ou=ApplicationGroups,ou=Groups.
ou=Administrators,dc=example,dc=com
├── ou=Operators
│ ├── uid=dirreader
│ ├── uid=helpdesk1
│ ├── uid=useradmin1
│ ├── uid=useradmin2
│ ├── uid=groupadmin1
│ └── uid=engadmin1
├── cn=directory-readers
├── cn=helpdesk-admins
├── cn=user-admins
├── cn=group-admins
└── cn=engineering-adminsCreate the Administrators container, the Operators subtree, department OUs, and the application-group container. Save this combined structure LDIF:
cat > /tmp/deleg-admin-structure.ldif <<'EOF'
dn: ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Administrators
description: Delegated administrator groups and operators
dn: ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Operators
description: Delegated operator accounts
dn: ou=Engineering,ou=people,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Engineering
description: Engineering department users
dn: ou=Finance,ou=people,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Finance
description: Finance department users
dn: ou=ApplicationGroups,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: ApplicationGroups
description: Delegated application groups
EOFApply the LDIF with ldapadd as Directory Manager:
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/deleg-admin-structure.ldifSample output:
adding new entry "ou=Administrators,dc=example,dc=com"
adding new entry "ou=Operators,ou=Administrators,dc=example,dc=com"
adding new entry "ou=Engineering,ou=people,dc=example,dc=com"
adding new entry "ou=Finance,ou=people,dc=example,dc=com"
adding new entry "ou=ApplicationGroups,ou=Groups,dc=example,dc=com"This combined LDIF assumes none of these entries already exists. ldapadd stops on the first error by default. If ou=Engineering already exists, remove that record from the LDIF or create the remaining containers in separate operations.
Create operator accounts under ou=Operators with LDIF. dsidm user create defaults to the normal user subtree, so use explicit DNs here. I'll set umask 077 first so any temporary LDIF I create is not world-readable:
umask 077
cat > /tmp/deleg-operators.ldif <<'EOF'
dn: uid=dirreader,ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Dir Reader
sn: Reader
uid: dirreader
uidNumber: 4001
gidNumber: 4001
homeDirectory: /home/dirreader
userPassword: DirReader1!
dn: uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Help Desk One
sn: One
uid: helpdesk1
uidNumber: 4002
gidNumber: 4002
homeDirectory: /home/helpdesk1
userPassword: Helpdesk1!
dn: uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: User Admin One
sn: One
uid: useradmin1
uidNumber: 4003
gidNumber: 4003
homeDirectory: /home/useradmin1
userPassword: UserAdmin1!
dn: uid=useradmin2,ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: User Admin Two
sn: Two
uid: useradmin2
uidNumber: 4004
gidNumber: 4004
homeDirectory: /home/useradmin2
userPassword: UserAdmin2!
dn: uid=groupadmin1,ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Group Admin One
sn: One
uid: groupadmin1
uidNumber: 4005
gidNumber: 4005
homeDirectory: /home/groupadmin1
userPassword: GroupAdmin1!
dn: uid=engadmin1,ou=Operators,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Eng Admin One
sn: One
uid: engadmin1
uidNumber: 4006
gidNumber: 4006
homeDirectory: /home/engadmin1
userPassword: EngAdmin1!
EOFApply the operator LDIF with ldapadd:
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/deleg-operators.ldifRemove the operator LDIF immediately because it contains plaintext passwords:
rm -f /tmp/deleg-operators.ldifStore bind passwords for lab testing. Use printf '%s' without a trailing newline so ldapwhoami -y reads the password correctly, then set mode 600 with the chmod command:
printf '%s' 'DirReader1!' > /root/dirreader.pw
chmod 600 /root/dirreader.pwRepeat for helpdesk1, useradmin1, useradmin2, groupadmin1, and engadmin1.
I'll create administrator groups with full operator DNs in each member value. groupOfNames requires at least one member at creation time. I'll seed cn=user-admins with useradmin1 only. useradmin2 is added later for the membership demonstration. Save the group LDIF:
cat > /tmp/deleg-admin-groups.ldif <<'EOF'
dn: cn=directory-readers,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: directory-readers
description: Read-only delegated directory access
member: uid=dirreader,ou=Operators,ou=Administrators,dc=example,dc=com
dn: cn=helpdesk-admins,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: helpdesk-admins
description: Password reset and account lock management
member: uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com
dn: cn=user-admins,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: user-admins
description: User lifecycle administration
member: uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com
dn: cn=group-admins,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: group-admins
description: Group membership administration
member: uid=groupadmin1,ou=Operators,ou=Administrators,dc=example,dc=com
dn: cn=engineering-admins,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: engineering-admins
description: Engineering department administrators
member: uid=engadmin1,ou=Operators,ou=Administrators,dc=example,dc=com
EOFApply the administrator-group LDIF with ldapadd:
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/deleg-admin-groups.ldifAdd a disposable Finance user for cross-department denial tests. Save the user LDIF with umask 077 set:
umask 077
cat > /tmp/fin-user.ldif <<'EOF'
dn: uid=finuser1,ou=Finance,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Fin User One
sn: One
uid: finuser1
uidNumber: 4020
gidNumber: 4020
homeDirectory: /home/finuser1
userPassword: FinUser1!
EOFApply the Finance test-user LDIF with ldapadd:
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/fin-user.ldifRemove the LDIF file because it contains a plaintext password:
rm -f /tmp/fin-user.ldifThe layout separates operators from delegated data before any ACIs are added.
Review existing ACIs
Before creating delegation ACIs, capture every ACI already stored under the suffix:
ldapsearch -LLL -o ldif-wrap=no -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -b "dc=example,dc=com" -s sub "(aci=*)" dn aci > /root/delegation-acis-before.ldifReview this snapshot for existing rules that apply to ou=people, ou=Groups, department OUs, or the delegated operator groups. The results in this lab assume that no pre-existing allow rule grants additional access and no pre-existing deny rule blocks one of the intended operations.
This snapshot also gives you a reliable baseline for rollback and prevents you from deleting pre-existing ACIs accidentally.
Delegate read-only directory access
Grant cn=directory-readers read access to ordinary profile fields under ou=people. Exclude userPassword, aci, nsAccountLock, and other privileged attributes by listing only safe targetattr values, then apply the rule with ldapmodify:
cat > /tmp/aci-directory-readers.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="uid || cn || sn || mail || telephoneNumber || description")(target="ldap:///ou=people,dc=example,dc=com")(version 3.0; acl "Delegated directory readers"; allow (read,search,compare) groupdn="ldap:///cn=directory-readers,ou=Administrators,dc=example,dc=com";)
EOFApply the read-only ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-directory-readers.ldifBind as dirreader and read an existing user:
ldapsearch -LLL -x -H ldap://localhost:389 -D "uid=dirreader,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/dirreader.pw -b "uid=user1,ou=people,dc=example,dc=com" -s base "(uid=user1)" mail cnSample output:
dn: uid=user1,ou=people,dc=example,dc=com
mail: [email protected]
cn: User OneConfirm the reader cannot modify entries:
cat > /tmp/reader-denied.ldif <<'EOF'
dn: uid=user1,ou=people,dc=example,dc=com
changetype: modify
replace: description
description: reader attempt
EOFAttempt the denied modify as dirreader to confirm write access is blocked:
ldapmodify -x -H ldap://localhost:389 -D "uid=dirreader,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/dirreader.pw -f /tmp/reader-denied.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'description' attribute of entry 'uid=user1,ou=people,dc=example,dc=com'.Read-only delegation exposes profile data without write rights.
Delegate help-desk password resets and lock management
Help-desk operators need to find accounts, reset passwords, and manage account lock state. They should not edit arbitrary profile fields or ACIs. Split lookup and write into two ACIs so write does not apply to uid, cn, sn, or mail. The write ACI on nsAccountLock permits adding, replacing, or removing the attribute, so help-desk members can lock or unlock accounts, not only clear an existing lock.
cat > /tmp/aci-helpdesk-admins.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="uid || cn || sn || mail || nsAccountLock")(target="ldap:///ou=people,dc=example,dc=com")(version 3.0; acl "Help desk user lookup"; allow (read,search,compare) groupdn="ldap:///cn=helpdesk-admins,ou=Administrators,dc=example,dc=com";)
-
add: aci
aci: (targetattr="userPassword || nsAccountLock")(target="ldap:///ou=people,dc=example,dc=com")(version 3.0; acl "Help desk password reset and lock management"; allow (write) groupdn="ldap:///cn=helpdesk-admins,ou=Administrators,dc=example,dc=com";)
EOFApply both help-desk ACIs with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-helpdesk-admins.ldifFour password-administration scenarios differ in scope:
| Scenario | Who | Typical mechanism |
|---|---|---|
| Self-service | The user on their own entry | userdn="ldap:///self" or entry-level self ACI (self-service ACI) |
| Help-desk reset | Help-desk group on other users | Narrow userPassword write ACI through groupdn |
| Password administrator | Group configured in password policy | dsconf … pwpolicy set --pwdadmin=… bypasses policy checks (password policy) |
| Directory Manager | Break-glass full access | Hard-coded privileges outside ordinary ACIs |
This lab grants an ordinary delegated ACI for userPassword write. It does not configure the RHDS password-administrator role with --pwdadmin. That special role bypasses password-policy verification during administrative resets. Configure it only when you need that behavior and keep the group narrower than general user administrators.
user2's password and account-lock state. Use a disposable test account, or ensure you know its original password and whether nsAccountLock was present before continuing.
Confirm help-desk operators cannot modify profile attributes:
cat > /tmp/helpdesk-denied-mail.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: mail
mail: [email protected]
EOFAttempt the denied mail change as helpdesk1:
ldapmodify -x -H ldap://localhost:389 -D "uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/helpdesk1.pw -f /tmp/helpdesk-denied-mail.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'mail' attribute of entry 'uid=user2,ou=people,dc=example,dc=com'.Reset another user's password as helpdesk1. Restrict the LDIF file permissions and remove it immediately after the test:
umask 077
cat > /tmp/helpdesk-reset-pw.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: HelpdeskReset1!
EOFApply the password reset as helpdesk1:
ldapmodify -x -H ldap://localhost:389 -D "uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/helpdesk1.pw -f /tmp/helpdesk-reset-pw.ldifRemove the LDIF because it contains the new password:
rm -f /tmp/helpdesk-reset-pw.ldifSample output:
modifying entry "uid=user2,ou=people,dc=example,dc=com"Lock the account as Directory Manager before the unlock test. Prefer dsidm account lock when your instance supports it:
dsidm ldap1 account lock "uid=user2,ou=people,dc=example,dc=com"If dsidm account lock reports a database extension error on your instance, set the lock with ldapmodify instead:
cat > /tmp/dm-lock-user2.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: nsAccountLock
nsAccountLock: true
EOFApply the lock with ldapmodify as Directory Manager:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/dm-lock-user2.ldifConfirm the lock is present:
ldapsearch -LLL -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -b "uid=user2,ou=people,dc=example,dc=com" -s base nsAccountLockSample output:
dn: uid=user2,ou=people,dc=example,dc=com
nsAccountLock: trueRemove nsAccountLock as the help-desk identity:
cat > /tmp/helpdesk-unlock.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
delete: nsAccountLock
EOFRemove the lock as helpdesk1 to confirm account-lock management is delegated:
ldapmodify -x -H ldap://localhost:389 -D "uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/helpdesk1.pw -f /tmp/helpdesk-unlock.ldifSample output:
modifying entry "uid=user2,ou=people,dc=example,dc=com"Because operators live outside ou=people, help-desk members cannot reset another operator's password:
cat > /tmp/helpdesk-denied-operator.ldif <<'EOF'
dn: uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: Escalation1!
EOFAttempt the operator-password reset as helpdesk1 to confirm help desk cannot reach ou=Operators:
ldapmodify -x -H ldap://localhost:389 -D "uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/helpdesk1.pw -f /tmp/helpdesk-denied-operator.ldifRemove the escalation-test LDIF:
rm -f /tmp/helpdesk-denied-operator.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'userPassword' attribute of entry 'uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com'.Delegate user creation and modification
Grant cn=user-admins lifecycle rights on uid entries throughout the ou=people subtree. The wildcard target excludes entries whose RDN does not start with uid=, such as the People and department OU containers, but it can also match user entries inside nested department OUs.
This is the broad user-administrator role. Use the department-specific role later in this guide when an operator must be restricted to one department, and do not also place that operator in the global user-admins group.
Split creation and modification into separate ACIs. The creation rule grants add, uses targattrfilters to permit only approved object classes, and restricts initial uidNumber and gidNumber values to the 4000–4999 range reserved for this lab. On 389 DS 3.2, combining add=objectclass with add=uidNumber or add=gidNumber filters in one targattrfilters value fails ACL syntax validation, so the lab enforces the numeric ranges with targetfilter on the entry being created instead of:
add=uidNumber:(&(uidNumber>=4000)(uidNumber<=4999)) && add=gidNumber:(&(gidNumber>=4000)(gidNumber<=4999))An ACI value filter does not enforce uniqueness. Pair the range with an allocation process so delegates cannot assign duplicate IDs. On systems using LDAP through SSSD or NSS, UID and GID collisions can map the wrong identity. UID 0 represents the root identity.
The modification rules split lookup from write. Lookup ACIs grant read, search, and compare on identifying attributes such as uid. Write ACIs grant write only on mutable profile attributes. That excludes uid, objectClass, uidNumber, and gidNumber so delegates cannot rename entries or reassign POSIX IDs after creation. Red Hat Directory Server documents that write applies to both LDAP modify and Modify DN operations, so naming attributes must stay out of general write ACIs.
Add the creation ACI:
cat > /tmp/aci-user-admins-add.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="uid || cn || sn || mail || telephoneNumber || description || objectClass || gidNumber || uidNumber || homeDirectory || loginShell")(targetfilter="(&(|(objectclass=inetorgperson)(objectclass=posixaccount))(uidNumber>=4000)(uidNumber<=4999)(gidNumber>=4000)(gidNumber<=4999))")(targattrfilters="add=objectclass:(|(objectclass=top)(objectclass=person)(objectclass=organizationalPerson)(objectclass=inetorgperson)(objectclass=posixaccount))")(target="ldap:///uid=*,ou=people,dc=example,dc=com")(version 3.0; acl "User admins add users"; allow (add) groupdn="ldap:///cn=user-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the user-creation ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-user-admins-add.ldifAdd the lookup and write ACIs:
cat > /tmp/aci-user-admins-modify.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="uid || cn || sn || mail || telephoneNumber || description || homeDirectory || loginShell")(targetfilter="(|(objectclass=inetorgperson)(objectclass=posixaccount))")(target="ldap:///uid=*,ou=people,dc=example,dc=com")(version 3.0; acl "User admins lookup users"; allow (read,search,compare) groupdn="ldap:///cn=user-admins,ou=Administrators,dc=example,dc=com";)
-
add: aci
aci: (targetattr="cn || sn || mail || telephoneNumber || description || homeDirectory || loginShell")(targetfilter="(|(objectclass=inetorgperson)(objectclass=posixaccount))")(target="ldap:///uid=*,ou=people,dc=example,dc=com")(version 3.0; acl "User admins write users"; allow (write) groupdn="ldap:///cn=user-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the lookup and write ACIs with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-user-admins-modify.ldifCreate a user as useradmin1:
cat > /tmp/deleg-newuser.ldif <<'EOF'
dn: uid=deleguser1,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Deleg User
sn: User
uid: deleguser1
uidNumber: 4010
gidNumber: 4010
homeDirectory: /home/deleguser1
EOFApply the LDIF with ldapadd as useradmin1:
ldapadd -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/deleg-newuser.ldifSample output:
adding new entry "uid=deleguser1,ou=people,dc=example,dc=com"An add with a disallowed object class is rejected by targattrfilters:
cat > /tmp/useradmin-denied-class.ldif <<'EOF'
dn: uid=baduser1,ou=people,dc=example,dc=com
objectClass: top
objectClass: account
uid: baduser1
EOFAttempt the denied add as useradmin1:
ldapadd -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-class.ldifSample output:
ldap_add: Insufficient access (50)
additional info: Insufficient 'add' privilege to add the entry 'uid=baduser1,ou=people,dc=example,dc=com'.An add with a uidNumber or gidNumber outside the reserved range is rejected by targetfilter:
cat > /tmp/useradmin-denied-uidnumber-add.ldif <<'EOF'
dn: uid=baduid1,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Bad UID
sn: Bad
uid: baduid1
uidNumber: 100
gidNumber: 4010
homeDirectory: /home/baduid1
EOFAttempt the out-of-range UID add as useradmin1:
ldapadd -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-uidnumber-add.ldifSample output:
ldap_add: Insufficient access (50)
additional info: Insufficient 'add' privilege to add the entry 'uid=baduid1,ou=people,dc=example,dc=com'.The wildcard target excludes the parent OU itself. useradmin1 cannot modify ou=people. The same uid=*,ou=people,... pattern also covers uid entries in nested department OUs such as ou=Engineering.
cat > /tmp/useradmin-denied-people-ou.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
replace: description
description: hack ou
EOFAttempt the parent-OU modify as useradmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-people-ou.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'description' attribute of entry 'ou=people,dc=example,dc=com'.User administrators cannot reset passwords without a separate grant:
cat > /tmp/useradmin-denied-pw.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: UserAdminHack1!
EOFAttempt the password reset as useradmin1 to confirm user administrators lack userPassword write:
ldapmodify -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-pw.ldifRemove the password LDIF:
rm -f /tmp/useradmin-denied-pw.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'userPassword' attribute of entry 'uid=user2,ou=people,dc=example,dc=com'.uidNumber is available only during entry creation within the reserved range. The write ACI excludes it, and later changes are denied:
cat > /tmp/useradmin-denied-uidnumber.ldif <<'EOF'
dn: uid=deleguser1,ou=people,dc=example,dc=com
changetype: modify
replace: uidNumber
uidNumber: 9999
EOFAttempt the uidNumber change as useradmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-uidnumber.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'uidNumber' attribute of entry 'uid=deleguser1,ou=people,dc=example,dc=com'.Confirm the write ACI allows an intended profile update before testing deletion:
cat > /tmp/useradmin-allowed-description.ldif <<'EOF'
dn: uid=deleguser1,ou=people,dc=example,dc=com
changetype: modify
add: description
description: Updated by delegated user administrator
EOFApply the allowed profile update as useradmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-allowed-description.ldifSample output:
modifying entry "uid=deleguser1,ou=people,dc=example,dc=com"An add under ou=Administrators is denied because no ACI grants user-admins rights there:
cat > /tmp/useradmin-denied-admin-ou.ldif <<'EOF'
dn: ou=EscalationTest,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: EscalationTest
EOFAttempt the escalation OU add as useradmin1:
ldapadd -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-admin-ou.ldifSample output:
ldap_add: Insufficient access (50)
additional info: Insufficient 'add' privilege to add the entry 'ou=EscalationTest,ou=Administrators,dc=example,dc=com'.User administrators also cannot delete operator accounts under ou=Operators. Attempt to delete helpdesk1 as useradmin1:
ldapdelete -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw "uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com"Sample output:
ldap_delete: Insufficient access (50)
additional info: Insufficient 'delete' privilege to delete the entry 'uid=helpdesk1,ou=Operators,ou=Administrators,dc=example,dc=com'.Delegate user deletion and rename separately
Delete and Modify DN are more sensitive than attribute updates. Grant them in separate ACIs so user administrators can maintain entries without automatically receiving rename or move rights.
Add a narrow delete grant using the same uid=*,ou=people,... target so global user administrators can delete uid entries anywhere below ou=people, but not the People or department OU containers:
cat > /tmp/aci-user-admins-delete.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (target="ldap:///uid=*,ou=people,dc=example,dc=com")(version 3.0; acl "User admins delete users"; allow (delete) groupdn="ldap:///cn=user-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the delete ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-user-admins-delete.ldifDelete a disposable test user as useradmin1:
ldapdelete -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw "uid=deleguser1,ou=people,dc=example,dc=com"The command exits silently when the entry is removed.
Keep naming attributes out of general write ACIs. Use dedicated rename permissions for RDN changes, and use moddn with target_from and target_to to constrain cross-container moves. See advanced ACI and rename and move entries for tested move patterns.
Delegate group administration
Allow cn=group-admins to manage application groups under ou=ApplicationGroups,ou=Groups only. Pre-existing groups directly under ou=Groups and administrator groups under ou=Administrators stay outside the delegated scope.
Split creation and modification the same way as user administrators. The creation ACI permits only top and groupOfNames through targattrfilters. The lookup ACI grants read, search, and compare on cn, description, and member. The write ACI grants write on description and member only. That excludes cn and objectClass so delegates cannot rename group entries or add unexpected classes:
cat > /tmp/aci-group-admins-add.ldif <<'EOF'
dn: ou=ApplicationGroups,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="cn || description || member || objectClass")(targattrfilters="add=objectclass:(|(objectclass=top)(objectclass=groupofnames))")(target="ldap:///cn=*,ou=ApplicationGroups,ou=Groups,dc=example,dc=com")(version 3.0; acl "Group admins add application groups"; allow (add) groupdn="ldap:///cn=group-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the group-creation ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-group-admins-add.ldifWrite the lookup and ACIs for application groups:
cat > /tmp/aci-group-admins-modify.ldif <<'EOF'
dn: ou=ApplicationGroups,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="cn || description || member")(targetfilter="(objectclass=groupofnames)")(target="ldap:///cn=*,ou=ApplicationGroups,ou=Groups,dc=example,dc=com")(version 3.0; acl "Group admins lookup application groups"; allow (read,search,compare) groupdn="ldap:///cn=group-admins,ou=Administrators,dc=example,dc=com";)
-
add: aci
aci: (targetattr="description || member")(targetfilter="(objectclass=groupofnames)")(target="ldap:///cn=*,ou=ApplicationGroups,ou=Groups,dc=example,dc=com")(version 3.0; acl "Group admins write application groups"; allow (write) groupdn="ldap:///cn=group-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the group lookup and write ACIs with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-group-admins-modify.ldifGrant delete on group entries only, not the container itself:
cat > /tmp/aci-group-admins-delete.ldif <<'EOF'
dn: ou=ApplicationGroups,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (target="ldap:///cn=*,ou=ApplicationGroups,ou=Groups,dc=example,dc=com")(version 3.0; acl "Group admins delete application groups"; allow (delete) groupdn="ldap:///cn=group-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the group-delete ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-group-admins-delete.ldifCreate an application group as groupadmin1:
cat > /tmp/deleg-newgroup.ldif <<'EOF'
dn: cn=app-team,ou=ApplicationGroups,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: app-team
description: Application team
member: uid=user1,ou=people,dc=example,dc=com
EOFApply the LDIF with ldapadd as groupadmin1:
ldapadd -x -H ldap://localhost:389 -D "uid=groupadmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/groupadmin1.pw -f /tmp/deleg-newgroup.ldifAdd a second member to the new group:
cat > /tmp/groupadmin-add-member.ldif <<'EOF'
dn: cn=app-team,ou=ApplicationGroups,ou=Groups,dc=example,dc=com
changetype: modify
add: member
member: uid=user2,ou=people,dc=example,dc=com
EOFAdd the second member with ldapmodify as groupadmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=groupadmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/groupadmin1.pw -f /tmp/groupadmin-add-member.ldifModifying cn=helpdesk-admins under ou=Administrators fails because no delegate ACI grants write access there:
cat > /tmp/groupadmin-denied.ldif <<'EOF'
dn: cn=helpdesk-admins,ou=Administrators,dc=example,dc=com
changetype: modify
add: member
member: uid=groupadmin1,ou=Operators,ou=Administrators,dc=example,dc=com
EOFAttempt the administrator-group modify as groupadmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=groupadmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/groupadmin1.pw -f /tmp/groupadmin-denied.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'member' attribute of entry 'cn=helpdesk-admins,ou=Administrators,dc=example,dc=com'.Restrict administrators to one department
Scope Engineering administrators to ou=Engineering,ou=People,dc=example,dc=com only. Split add, lookup, write, and delete ACIs the same way as the global user-administrator role:
cat > /tmp/aci-engineering-admins-add.ldif <<'EOF'
dn: ou=Engineering,ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="uid || cn || sn || mail || telephoneNumber || description || objectClass || gidNumber || uidNumber || homeDirectory")(targetfilter="(&(|(objectclass=inetorgperson)(objectclass=posixaccount))(uidNumber>=4000)(uidNumber<=4999)(gidNumber>=4000)(gidNumber<=4999))")(targattrfilters="add=objectclass:(|(objectclass=top)(objectclass=person)(objectclass=organizationalPerson)(objectclass=inetorgperson)(objectclass=posixaccount))")(target="ldap:///uid=*,ou=Engineering,ou=people,dc=example,dc=com")(version 3.0; acl "Engineering admins add users"; allow (add) groupdn="ldap:///cn=engineering-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the Engineering add ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-engineering-admins-add.ldifWrite the Engineering lookup and ACIs:
cat > /tmp/aci-engineering-admins-modify.ldif <<'EOF'
dn: ou=Engineering,ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="uid || cn || sn || mail || telephoneNumber || description || homeDirectory")(targetfilter="(|(objectclass=inetorgperson)(objectclass=posixaccount))")(target="ldap:///uid=*,ou=Engineering,ou=people,dc=example,dc=com")(version 3.0; acl "Engineering admins lookup users"; allow (read,search,compare) groupdn="ldap:///cn=engineering-admins,ou=Administrators,dc=example,dc=com";)
-
add: aci
aci: (targetattr="cn || sn || mail || telephoneNumber || description || homeDirectory")(targetfilter="(|(objectclass=inetorgperson)(objectclass=posixaccount))")(target="ldap:///uid=*,ou=Engineering,ou=people,dc=example,dc=com")(version 3.0; acl "Engineering admins write users"; allow (write) groupdn="ldap:///cn=engineering-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the Engineering lookup and write ACIs with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-engineering-admins-modify.ldifWrite the Engineering delete ACI:
cat > /tmp/aci-engineering-admins-delete.ldif <<'EOF'
dn: ou=Engineering,ou=People,dc=example,dc=com
changetype: modify
add: aci
aci: (target="ldap:///uid=*,ou=Engineering,ou=people,dc=example,dc=com")(version 3.0; acl "Engineering admins delete Engineering users"; allow (delete) groupdn="ldap:///cn=engineering-admins,ou=Administrators,dc=example,dc=com";)
EOFApply the Engineering delete ACI with ldapmodify:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-engineering-admins-delete.ldifengadmin1 can add a user under Engineering:
cat > /tmp/eng-newuser.ldif <<'EOF'
dn: uid=enguser2,ou=Engineering,ou=People,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
objectClass: posixAccount
cn: Eng User Two
sn: Two
uid: enguser2
uidNumber: 4011
gidNumber: 4011
homeDirectory: /home/enguser2
EOFApply the LDIF with ldapadd as engadmin1:
ldapadd -x -H ldap://localhost:389 -D "uid=engadmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/engadmin1.pw -f /tmp/eng-newuser.ldifSample output:
adding new entry "uid=enguser2,ou=Engineering,ou=People,dc=example,dc=com"Confirm the departmental write ACI allows an intended update inside Engineering:
cat > /tmp/engadmin-allowed-description.ldif <<'EOF'
dn: uid=enguser2,ou=Engineering,ou=People,dc=example,dc=com
changetype: modify
add: description
description: Updated by Engineering administrator
EOFApply the allowed description update as engadmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=engadmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/engadmin1.pw -f /tmp/engadmin-allowed-description.ldifSample output:
modifying entry "uid=enguser2,ou=Engineering,ou=People,dc=example,dc=com"The same identity cannot modify users under ou=Finance:
cat > /tmp/eng-denied-finance.ldif <<'EOF'
dn: uid=finuser1,ou=Finance,ou=people,dc=example,dc=com
changetype: modify
replace: description
description: cross department
EOFAttempt the cross-department modify as engadmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=engadmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/engadmin1.pw -f /tmp/eng-denied-finance.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'description' attribute of entry 'uid=finuser1,ou=Finance,ou=people,dc=example,dc=com'.Relationship-based and attribute-filter rules for department boundaries belong in advanced ACI.
Prevent privilege escalation
Delegated administrators must not modify:
aci— broadwritewithtargetattr="*"can let a user add a newacivalue and grant themselves additional rights- Administrator-group
membervalues — self-membership incn=user-adminswould expand authorization scope nsRoleDNand privileged operational attributes not required for the role- Directory Manager or
cn=configentries
Prefer narrow allow rules with explicit targetattr lists. Do not rely on broad write followed by many deny rules.
Protect ou=Administrators by placement, not by a userdn="ldap:///cn=Directory Manager" ACI. Directory Manager privileges are hard-coded. Ordinary ACIs cannot restrict or grant Directory Manager access. Restrictions on Directory Manager binds require the RootDN Access Control plug-in.
Instead:
- Store operators and role groups under
ou=Administrators, outside every delegatedtarget - Never grant delegate
write,add, ordeleteACIs onou=Administratorsorou=Operators - Test that delegated identities receive result code
50on administrator-group and operator entries
Verify a user administrator cannot add an aci value:
cat > /tmp/useradmin-denied-aci.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (version 3.0; acl "escalation"; allow (all) userdn="ldap:///anyone";)
EOFAttempt the aci escalation as useradmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-aci.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'aci' attribute of entry 'ou=people,dc=example,dc=com'.Self-adding to cn=user-admins is also denied:
cat > /tmp/useradmin-denied-member.ldif <<'EOF'
dn: cn=user-admins,ou=Administrators,dc=example,dc=com
changetype: modify
add: member
member: uid=hacker,ou=people,dc=example,dc=com
EOFAttempt the self-membership add as useradmin1:
ldapmodify -x -H ldap://localhost:389 -D "uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin1.pw -f /tmp/useradmin-denied-member.ldifSample output:
ldap_modify: Insufficient access (50)
additional info: Insufficient 'write' privilege to the 'member' attribute of entry 'cn=user-admins,ou=Administrators,dc=example,dc=com'.Narrow targetattr lists, operator placement outside delegated subtrees, and no delegate write path to ou=Administrators block the usual privilege-escalation paths.
Manage administrators through group membership
useradmin2 is not a member of cn=user-admins yet. Attempt the same user add as useradmin2 before membership changes:
ldapadd -x -H ldap://localhost:389 -D "uid=useradmin2,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin2.pw -f /tmp/deleg-newuser.ldifSample output:
ldap_add: Insufficient access (50)
additional info: Insufficient 'add' privilege to add the entry 'uid=deleguser1,ou=people,dc=example,dc=com'.Add useradmin2 to the administrator group with the full group DN:
cat > /tmp/add-useradmin2-member.ldif <<'EOF'
dn: cn=user-admins,ou=Administrators,dc=example,dc=com
changetype: modify
add: member
member: uid=useradmin2,ou=Operators,ou=Administrators,dc=example,dc=com
EOFAdd useradmin2 to the group with ldapmodify as Directory Manager:
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/add-useradmin2-member.ldifRetry the same ldapadd as useradmin2 after membership changes:
ldapadd -x -H ldap://localhost:389 -D "uid=useradmin2,ou=Operators,ou=Administrators,dc=example,dc=com" -y /root/useradmin2.pw -f /tmp/deleg-newuser.ldifSample output:
adding new entry "uid=deleguser1,ou=people,dc=example,dc=com"Group-driven delegation means permission changes follow membership, not ACI edits.
dsidm group commands manage groups under the conventional ou=Groups subtree. For administrator groups stored directly under ou=Administrators, use ldapmodify with the complete group DN.
Test delegated permissions
Test each role as the delegated bind identity, not as Directory Manager.
| Operation | Reader | Help desk | User admin | Group admin |
|---|---|---|---|---|
| Search users | Allow | Allow | Allow | Not granted in this lab |
| Modify profile | Deny | Deny | Allow | Deny |
Reset userPassword |
Deny | Allow | Deny | Deny |
| Manage account lock state | Deny | Allow | Deny | Deny |
| Create user | Deny | Deny | Allow | Deny |
| Delete user | Deny | Deny | Allow (with delete ACI) | Deny |
Manage group member |
Deny | Deny | Deny | Allow |
Modify aci |
Deny | Deny | Deny | Deny |
For attribute-level permission inspection beyond allow/deny spot checks, use Get Effective Rights in the effective rights chapter.
Remove or revise delegated permissions
- List exact
acivalues withldapsearchon each container you modified. - Delete or replace one
acivalue at a time withldapmodify. Never clear the entire attribute unless intended. - Retest each administrator group with allowed and denied operations.
- Remove obsolete
membervalues from administrator groups. - Confirm ordinary user access outside the delegated scopes is unchanged.
Record ACIs before production changes. A mistyped delete: aci line can remove the wrong rule.
Troubleshoot delegated administration
| Symptom | Likely cause | Fix |
|---|---|---|
| Administrator can search but cannot create users | The target does not match the new DN, add is missing, or targetattr/targattrfilters rejects an attribute or value |
Verify the target matches the new entry DN, grant add, and ensure every attribute and value in the LDIF is permitted |
| Password reset fails | userPassword not in targetattr, or wrong group |
Add a help-desk-only write ACI on userPassword; verify groupdn and membership |
Help desk can modify mail or cn |
write granted on lookup attributes |
Split lookup (read,search,compare) and password (write on userPassword only) into separate ACIs |
| Administrator manages the wrong subtree | ACI target too broad |
Narrow target to the departmental or service OU |
| Delegated roles affect each other | Operator accounts live inside delegated subtree | Move operators to ou=Operators,ou=Administrators outside ou=people |
| Group membership changes have no effect | Wrong group DN or short member value |
Use full DNs in groupdn and member attributes |
Administrator can modify aci |
aci included in targetattr or wildcard write |
Remove aci from delegate rules; use explicit attribute lists |
| Rename succeeds but move fails | Missing target_to / destination rights |
Add move ACIs from the advanced and rename chapters |
dsidm group cannot find administrator group |
Default base is ou=Groups |
Use ldapmodify with the full administrator-group DN |
What's Next
- Macro ACIs — dynamic target DNs for delegated groups
- Get effective rights — audit what delegates can actually do
- Password policy — password-administrator responsibilities
References
- Red Hat Directory Server 13 — Managing access control — Chapter 2; Section 2.1.10 (
groupdnand bind rules) - Red Hat Directory Server 13 — Assigning password administrator permissions — Section 3.7; global and local password-administrator configuration
- 389 Directory Server documentation
Summary
- Place operator accounts under
ou=Operators,ou=Administrators, outside every subtree you delegate. - Scope every role to the smallest practical
targetand explicittargetattrlist. - Separate read, password, user, group, delete, and rename permissions. Split add from modify, then split lookup from write, when
objectClass, naming attributes, or POSIX identity attributes must stay controlled. - Never grant delegated administrators
writeonacior administrator-group membership. - Test allowed and prohibited operations as each delegated bind identity.
- Add and remove operators through group membership instead of rewriting ACIs.

