Create Delegated Administrators with 389 Directory Server ACIs

Delegate 389 Directory Server administration with groupdn ACIs for read-only, help-desk, user, group, and department roles without sharing Directory Manager credentials.

Published

Updated

Read time 25 min read

Reviewed byDeepak Prasad

389 Directory Server delegated administrator groups connected through groupdn ACIs to scoped LDAP subtrees for help desk and user management

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:

IMPORTANT
This guide covers delegated administration for managing other users and groups. It does not cover permissions on a user's own entry.

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

text
Delegated administrator
        |
        | member of
        v
LDAP administrator group
        |
        | matched by groupdn ACI
        v
Permitted operations on one directory subtree

Delegation 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.

text
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-admins

Create the Administrators container, the Operators subtree, department OUs, and the application-group container. Save this combined structure LDIF:

bash
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
EOF

Apply the LDIF with ldapadd as Directory Manager:

bash
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/deleg-admin-structure.ldif

Sample output:

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:

bash
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!
EOF

Apply the operator LDIF with ldapadd:

bash
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/deleg-operators.ldif

Remove the operator LDIF immediately because it contains plaintext passwords:

bash
rm -f /tmp/deleg-operators.ldif

Store 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:

bash
printf '%s' 'DirReader1!' > /root/dirreader.pw
chmod 600 /root/dirreader.pw

Repeat 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:

bash
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
EOF

Apply the administrator-group LDIF with ldapadd:

bash
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/deleg-admin-groups.ldif

Add a disposable Finance user for cross-department denial tests. Save the user LDIF with umask 077 set:

bash
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!
EOF

Apply the Finance test-user LDIF with ldapadd:

bash
ldapadd -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/fin-user.ldif

Remove the LDIF file because it contains a plaintext password:

bash
rm -f /tmp/fin-user.ldif

The 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:

bash
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.ldif

Review 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:

bash
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";)
EOF

Apply the read-only ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-directory-readers.ldif

Bind as dirreader and read an existing user:

bash
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 cn

Sample output:

output
dn: uid=user1,ou=people,dc=example,dc=com
mail: [email protected]
cn: User One

Confirm the reader cannot modify entries:

bash
cat > /tmp/reader-denied.ldif <<'EOF'
dn: uid=user1,ou=people,dc=example,dc=com
changetype: modify
replace: description
description: reader attempt
EOF

Attempt the denied modify as dirreader to confirm write access is blocked:

bash
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.ldif

Sample output:

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.

bash
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";)
EOF

Apply both help-desk ACIs with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-helpdesk-admins.ldif

Four 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.

IMPORTANT
This lab changes 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:

bash
cat > /tmp/helpdesk-denied-mail.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: mail
mail: [email protected]
EOF

Attempt the denied mail change as helpdesk1:

bash
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.ldif

Sample output:

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:

bash
umask 077
cat > /tmp/helpdesk-reset-pw.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: HelpdeskReset1!
EOF

Apply the password reset as helpdesk1:

bash
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.ldif

Remove the LDIF because it contains the new password:

bash
rm -f /tmp/helpdesk-reset-pw.ldif

Sample output:

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:

bash
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:

bash
cat > /tmp/dm-lock-user2.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: nsAccountLock
nsAccountLock: true
EOF

Apply the lock with ldapmodify as Directory Manager:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/dm-lock-user2.ldif

Confirm the lock is present:

bash
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 nsAccountLock

Sample output:

output
dn: uid=user2,ou=people,dc=example,dc=com
nsAccountLock: true

Remove nsAccountLock as the help-desk identity:

bash
cat > /tmp/helpdesk-unlock.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
delete: nsAccountLock
EOF

Remove the lock as helpdesk1 to confirm account-lock management is delegated:

bash
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.ldif

Sample output:

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:

bash
cat > /tmp/helpdesk-denied-operator.ldif <<'EOF'
dn: uid=useradmin1,ou=Operators,ou=Administrators,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: Escalation1!
EOF

Attempt the operator-password reset as helpdesk1 to confirm help desk cannot reach ou=Operators:

bash
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.ldif

Remove the escalation-test LDIF:

bash
rm -f /tmp/helpdesk-denied-operator.ldif

Sample output:

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 40004999 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:

text
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:

bash
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";)
EOF

Apply the user-creation ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-user-admins-add.ldif

Add the lookup and write ACIs:

bash
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";)
EOF

Apply the lookup and write ACIs with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-user-admins-modify.ldif

Create a user as useradmin1:

bash
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
EOF

Apply the LDIF with ldapadd as useradmin1:

bash
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.ldif

Sample output:

output
adding new entry "uid=deleguser1,ou=people,dc=example,dc=com"

An add with a disallowed object class is rejected by targattrfilters:

bash
cat > /tmp/useradmin-denied-class.ldif <<'EOF'
dn: uid=baduser1,ou=people,dc=example,dc=com
objectClass: top
objectClass: account
uid: baduser1
EOF

Attempt the denied add as useradmin1:

bash
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.ldif

Sample output:

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:

bash
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
EOF

Attempt the out-of-range UID add as useradmin1:

bash
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.ldif

Sample output:

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.

bash
cat > /tmp/useradmin-denied-people-ou.ldif <<'EOF'
dn: ou=people,dc=example,dc=com
changetype: modify
replace: description
description: hack ou
EOF

Attempt the parent-OU modify as useradmin1:

bash
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.ldif

Sample output:

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:

bash
cat > /tmp/useradmin-denied-pw.ldif <<'EOF'
dn: uid=user2,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: UserAdminHack1!
EOF

Attempt the password reset as useradmin1 to confirm user administrators lack userPassword write:

bash
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.ldif

Remove the password LDIF:

bash
rm -f /tmp/useradmin-denied-pw.ldif

Sample output:

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:

bash
cat > /tmp/useradmin-denied-uidnumber.ldif <<'EOF'
dn: uid=deleguser1,ou=people,dc=example,dc=com
changetype: modify
replace: uidNumber
uidNumber: 9999
EOF

Attempt the uidNumber change as useradmin1:

bash
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.ldif

Sample output:

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:

bash
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
EOF

Apply the allowed profile update as useradmin1:

bash
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.ldif

Sample output:

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:

bash
cat > /tmp/useradmin-denied-admin-ou.ldif <<'EOF'
dn: ou=EscalationTest,ou=Administrators,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: EscalationTest
EOF

Attempt the escalation OU add as useradmin1:

bash
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.ldif

Sample output:

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:

bash
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:

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:

bash
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";)
EOF

Apply the delete ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-user-admins-delete.ldif

Delete a disposable test user as useradmin1:

bash
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:

bash
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";)
EOF

Apply the group-creation ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-group-admins-add.ldif

Write the lookup and ACIs for application groups:

bash
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";)
EOF

Apply the group lookup and write ACIs with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-group-admins-modify.ldif

Grant delete on group entries only, not the container itself:

bash
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";)
EOF

Apply the group-delete ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-group-admins-delete.ldif

Create an application group as groupadmin1:

bash
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
EOF

Apply the LDIF with ldapadd as groupadmin1:

bash
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.ldif

Add a second member to the new group:

bash
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
EOF

Add the second member with ldapmodify as groupadmin1:

bash
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.ldif

Modifying cn=helpdesk-admins under ou=Administrators fails because no delegate ACI grants write access there:

bash
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
EOF

Attempt the administrator-group modify as groupadmin1:

bash
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.ldif

Sample output:

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:

bash
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";)
EOF

Apply the Engineering add ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-engineering-admins-add.ldif

Write the Engineering lookup and ACIs:

bash
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";)
EOF

Apply the Engineering lookup and write ACIs with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-engineering-admins-modify.ldif

Write the Engineering delete ACI:

bash
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";)
EOF

Apply the Engineering delete ACI with ldapmodify:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/aci-engineering-admins-delete.ldif

engadmin1 can add a user under Engineering:

bash
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
EOF

Apply the LDIF with ldapadd as engadmin1:

bash
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.ldif

Sample output:

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:

bash
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
EOF

Apply the allowed description update as engadmin1:

bash
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.ldif

Sample output:

output
modifying entry "uid=enguser2,ou=Engineering,ou=People,dc=example,dc=com"

The same identity cannot modify users under ou=Finance:

bash
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
EOF

Attempt the cross-department modify as engadmin1:

bash
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.ldif

Sample output:

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 — broad write with targetattr="*" can let a user add a new aci value and grant themselves additional rights
  • Administrator-group member values — self-membership in cn=user-admins would expand authorization scope
  • nsRoleDN and privileged operational attributes not required for the role
  • Directory Manager or cn=config entries

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 delegated target
  • Never grant delegate write, add, or delete ACIs on ou=Administrators or ou=Operators
  • Test that delegated identities receive result code 50 on administrator-group and operator entries

Verify a user administrator cannot add an aci value:

bash
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";)
EOF

Attempt the aci escalation as useradmin1:

bash
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.ldif

Sample output:

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:

bash
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
EOF

Attempt the self-membership add as useradmin1:

bash
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.ldif

Sample output:

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:

bash
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.ldif

Sample output:

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:

bash
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
EOF

Add useradmin2 to the group with ldapmodify as Directory Manager:

bash
ldapmodify -x -H ldap://localhost:389 -D "cn=Directory Manager" -y /root/dm.pw -f /tmp/add-useradmin2-member.ldif

Retry the same ldapadd as useradmin2 after membership changes:

bash
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.ldif

Sample output:

output
adding new entry "uid=deleguser1,ou=people,dc=example,dc=com"

Group-driven delegation means permission changes follow membership, not ACI edits.

IMPORTANT
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

  1. List exact aci values with ldapsearch on each container you modified.
  2. Delete or replace one aci value at a time with ldapmodify. Never clear the entire attribute unless intended.
  3. Retest each administrator group with allowed and denied operations.
  4. Remove obsolete member values from administrator groups.
  5. 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


References


Summary

  1. Place operator accounts under ou=Operators,ou=Administrators, outside every subtree you delegate.
  2. Scope every role to the smallest practical target and explicit targetattr list.
  3. 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.
  4. Never grant delegated administrators write on aci or administrator-group membership.
  5. Test allowed and prohibited operations as each delegated bind identity.
  6. Add and remove operators through group membership instead of rewriting ACIs.

Frequently Asked Questions

1. Why use LDAP groups for delegated administration instead of userdn ACIs?

Group-driven delegation lets you grant or revoke administrator access by changing group membership. You avoid editing every ACI when the help-desk roster or user-administrator team changes.

2. Can a delegated administrator modify ACIs?

Not if you follow least privilege. Never include aci in targetattr for delegate grants, and keep administrator groups under a separate OU that no delegate ACI can write.

3. What is the difference between help-desk and user-administrator password rights?

Grant userPassword write only to the help-desk group through a narrow ACI. User administrators manage entry lifecycle attributes but should not reset passwords unless you deliberately add that right in a separate ACI.

4. How do I restrict an administrator to one department OU?

Store the ACI on the departmental OU, but target only the user entries below it, such as uid=*,ou=Engineering,ou=people,dc=example,dc=com, or use an appropriate targetfilter. Keep delete permission in a separate ACI so the administrator cannot delete the department container itself.

5. Do group membership changes take effect immediately?

Directory Server checks group membership when evaluating the groupdn ACI for an operation. Adding or removing a member changes which subsequent operations match the rule; the ACI itself does not need to be edited.

6. How do I debug what a delegated administrator can do?

Retest with ldapsearch, ldapadd, ldapmodify, and ldapdelete as the delegated bind DN. For attribute-level permission matrices, use Get Effective Rights in the dedicated effective-rights chapter.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …