Enforce Unique LDAP Attributes in 389 Directory Server

Configure 389 Directory Server Attribute Uniqueness with dsconf to reject duplicate mail, uid, and employeeNumber values across subtrees.

Published

Updated

Read time 15 min read

Reviewed byDeepak Prasad

389 Directory Server Attribute Uniqueness plug-in rejecting a duplicate mail value during an LDAP add operation

LDAP directories often require unique mail, uid, employeeNumber, or custom attribute values across part of the tree. Without enforcement, provisioning scripts, imports, and parallel administrators can create collisions that break login, HR sync, or license assignment.

This guide covers the complete duplicate-value prevention workflow in 389 Directory Server:

  • Enforcing uniqueness for one attribute in one subtree or across several branches
  • Excluding test or staging areas
  • Restricting checks by entry object class
  • Defining independent domains with a parent object class
  • Testing adds, modifications, and moves into enforced scopes
  • Auditing existing duplicates
  • Equality indexes for performance
  • Multi-supplier replication limitations

Before you start:

IMPORTANT
This guide configures duplicate-value rejection only. It does not generate numeric IDs (DNA plug-in), define attribute syntax (custom schema), configure full database chaining, or resolve replication conflicts. The plug-in is disabled by default until an individual configuration entry is enabled.

Tested on: Rocky Linux 10.2; 389 Directory Server 3.2.0.

The lab instance is ldap1 on ldap1.example.com with LDAP port 389.


How the Attribute Uniqueness plug-in works

text
LDAP add, modify, rename, or move request
        |
        v
Attribute Uniqueness plug-in checks:
attribute + scope + object-class conditions
        |
        +--> No matching value: operation continues
        |
        +--> Matching value exists: operation is rejected
Component Purpose
Configuration entry Names one or more attributes and the search scope
uniqueness-subtrees Subtrees searched for existing values
uniqueness-across-all-subtrees Whether listed subtrees share one namespace
uniqueness-exclude-subtrees Branches skipped during the search
uniqueness-subtree-entries-oc Entry object class required for the check
uniqueness-top-entry-oc Parent object class that defines an independent domain

The plug-in runs as a pre-operation validation step. It prevents a local write from completing when the configured value already exists in the effective search scope. The plug-in uses the listed uniqueness attributes to build searches and relies on their equality matching behavior when finding duplicates.

text
Existing user:
mail: [email protected]

New user attempts:
mail: [email protected]

Result:
The operation is rejected

On my lab host, a rejected add returned:

output
ldap_add: Constraint violation (19)
	additional info: Another entry with the same attribute value already exists (attribute: "mail ")

Directory Server supports multiple Attribute Uniqueness configuration entries. Each enabled configuration is evaluated independently. Use separate entries when attributes or business rules need independent scopes, exclusions, or object-class conditions. Do not create overlapping rules for the same attribute and subtree unless you intend every configuration to apply.


Feature Purpose
Attribute Uniqueness Rejects duplicate attribute values
DNA plug-in Generates numeric values from managed ranges
Schema SINGLE-VALUE Limits an entry to one value, not one value across the directory
Naming attribute Makes an RDN unique below one parent
Database index Improves lookup performance but does not enforce uniqueness

When uid is the entry RDN, LDAP already prevents two identical uid=<value> RDNs below the same parent. Attribute Uniqueness is still useful when uid must be unique across multiple organizational units, when uid is not always the naming attribute, or when several independent containers use object-class-based domains.

text
SINGLE-VALUE means:
One value per entry

Attribute Uniqueness means:
The same value cannot appear on another matching entry

DNA and Attribute Uniqueness complement each other:

text
DNA generates uidNumber values
        +
Attribute Uniqueness rejects manually introduced duplicates

Plan uniqueness rules and audit existing duplicates

Use one continuous lab tree under dc=example,dc=com:

text
dc=example,dc=com
├── ou=Sales
│   ├── uid=sales1
│   └── ou=Internal
│       └── uid=internal1
├── ou=Accounting
│   └── uid=acct1
└── ou=ServiceAccounts
    └── uid=service1

Plan these rules:

Attribute Required uniqueness
mail Across Sales and Accounting
employeeNumber Within each listed subtree independently
uid Within each independently managed container
uidNumber Across POSIX users where required

Each business rule should normally use a clearly named, independent configuration entry such as Employee Mail Uniqueness or Employee Number Uniqueness.

The lab users have the following attributes before the Attribute Uniqueness rules are created:

User Location Attributes and notes
sales1 ou=Sales mail: [email protected], employeeNumber: E1001
acct1 ou=Accounting mail: [email protected], employeeNumber: E2001
internal1 ou=Internal,ou=Sales mail: [email protected]
service1 ou=ServiceAccounts service account from the prerequisite article

Create the organizational units and inetOrgPerson entries first, following Manage users and groups. Then assign the test attribute values. Save add-employee-attributes.ldif:

ldif
dn: uid=sales1,ou=Sales,dc=example,dc=com
changetype: modify
replace: mail
mail: [email protected]
-
replace: employeeNumber
employeeNumber: E1001

dn: uid=acct1,ou=Accounting,dc=example,dc=com
changetype: modify
replace: mail
mail: [email protected]
-
replace: employeeNumber
employeeNumber: E2001

dn: uid=internal1,ou=Internal,ou=Sales,dc=example,dc=com
changetype: modify
replace: mail
mail: [email protected]

Apply it:

bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f add-employee-attributes.ldif
output
modifying entry "uid=sales1,ou=Sales,dc=example,dc=com"
modifying entry "uid=acct1,ou=Accounting,dc=example,dc=com"
modifying entry "uid=internal1,ou=Internal,ou=Sales,dc=example,dc=com"

Before enabling strict rules, export the relevant attributes and look for duplicates:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(mail=*)" dn mail

For simple single-line values such as mail, list duplicate plain-text values outside the directory:

bash
ldapsearch -LLL -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(mail=*)" mail | awk '/^mail: / {print tolower(substr($0, 7))}' | sort | uniq -d

No output means no duplicate case-normalized, plain-text mail values were found. This simple pipeline approximates the case-insensitive mail comparison but is not a universal matching-rule-aware LDIF parser. Custom attributes must be audited according to their configured equality matching rule.

Repeat the export and duplicate check for uid, employeeNumber, and uidNumber. Take care not to expose sensitive directory data in shared logs.

Enabling the plug-in prevents future duplicate local writes. It does not automatically remove historical duplicates. Resolve existing collisions before you enable strict enforcement.


Check existing Attribute Uniqueness configurations

List configuration entries:

bash
dsconf ldap1 plugin attr-uniq list

Sample output on a fresh instance:

output
attribute uniqueness

After creating rules, the same command lists each configuration name:

output
attribute uniqueness
Employee Mail Uniqueness
Employee Number Uniqueness

Inspect one configuration:

bash
dsconf ldap1 plugin attr-uniq show "Employee Mail Uniqueness"

Review these attributes:

text
nsslapd-pluginEnabled
uniqueness-attribute-name
uniqueness-subtrees
uniqueness-across-all-subtrees
uniqueness-exclude-subtrees
uniqueness-subtree-entries-oc
uniqueness-top-entry-oc

Check for an existing UID or mail uniqueness configuration before you create another overlapping rule for the same attribute and subtree.


Enforce unique mail in one subtree

Use one continuous Employee Mail Uniqueness configuration and expand it as the lab progresses. Do not create a second overlapping mail rule for ou=Sales.

Create the initial Sales-only rule:

bash
dsconf ldap1 plugin attr-uniq add "Employee Mail Uniqueness" --attr-name mail --subtree "ou=Sales,dc=example,dc=com"
output
Successfully created the cn=Employee Mail Uniqueness,cn=plugins,cn=config

Enable it:

bash
dsconf ldap1 plugin attr-uniq enable "Employee Mail Uniqueness"
output
Successfully enabled the cn=Employee Mail Uniqueness,cn=plugins,cn=config

Restart the instance after enabling or changing an Attribute Uniqueness configuration:

bash
dsctl ldap1 restart

On my Rocky Linux 10.2 lab, enforcement did not begin until after that restart.

Verify the configuration:

bash
dsconf ldap1 plugin attr-uniq show "Employee Mail Uniqueness"

Sample output:

output
nsslapd-pluginEnabled: on
uniqueness-attribute-name: mail
uniqueness-subtrees: ou=Sales,dc=example,dc=com

This rule checks duplicate mail values only among entries inside ou=Sales,dc=example,dc=com. A matching value under Accounting can still be accepted until Accounting is added to the same configuration.


Cross-subtree rules, exclusions, and object-class scope

Expand the same Employee Mail Uniqueness configuration to cover Accounting and enable cross-subtree validation:

bash
dsconf ldap1 plugin attr-uniq set "Employee Mail Uniqueness" --subtree "ou=Sales,dc=example,dc=com" "ou=Accounting,dc=example,dc=com" --across-all-subtrees on
bash
dsctl ldap1 restart
Setting Behavior
uniqueness-across-all-subtrees: off Value must be unique only within its own listed subtree
uniqueness-across-all-subtrees: on Value must be unique across every listed subtree

On my lab host, an Accounting add with mail: [email protected] was rejected after sales1 already held that address in Sales:

output
ldap_add: Constraint violation (19)
	additional info: Another entry with the same attribute value already exists (attribute: "mail ")

Exclude a subtree from uniqueness checks

Exclude the internal test area on the same configuration entry:

bash
dsconf ldap1 plugin attr-uniq set "Employee Mail Uniqueness" --exclude-subtree "ou=Internal,ou=Sales,dc=example,dc=com"
bash
dsctl ldap1 restart

Verify the full configuration:

bash
dsconf ldap1 plugin attr-uniq show "Employee Mail Uniqueness"

Add a duplicate mail value inside the excluded subtree so the move test later has a concrete example:

ldif
dn: uid=internal2,ou=Internal,ou=Sales,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Internal Two
sn: Two
uid: internal2
mail: [email protected]
bash
ldapadd -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f internal2-dup-mail.ldif

The add succeeds because ou=Internal,ou=Sales,dc=example,dc=com is excluded. Excluded entries can still contain values duplicated elsewhere, so an application searching the complete directory can receive multiple matches.

Limit uniqueness to selected entry object classes

Require unique mail values only for inetOrgPerson entries on the same configuration:

bash
dsconf ldap1 plugin attr-uniq set "Employee Mail Uniqueness" --subtree-entries-oc inetOrgPerson
bash
dsctl ldap1 restart

An entry whose structural object class was not inetOrgPerson, and which used nsContainer with extensibleObject to permit the mail attribute, was accepted with the same value. Save shared-alias.ldif:

ldif
dn: cn=shared-alias,ou=Accounting,dc=example,dc=com
objectClass: top
objectClass: nsContainer
objectClass: extensibleObject
cn: shared-alias
mail: [email protected]
bash
ldapadd -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f shared-alias.ldif
output
adding new entry "cn=shared-alias,ou=Accounting,dc=example,dc=com"

A duplicate inetOrgPerson add with mail: [email protected] remained rejected.

Test two cases after enabling object-class scope:

  • An inetOrgPerson entry with a duplicate value
  • A different structural object class using the same attribute

Define independent uniqueness domains with a parent object class

Use object-class-based scoping when several organizational containers should each have their own uniqueness domain.

Example layout:

ldif
dn: cn=Tenant1,dc=example,dc=com
objectClass: top
objectClass: nsContainer
cn: Tenant1

dn: cn=Tenant2,dc=example,dc=com
objectClass: top
objectClass: nsContainer
cn: Tenant2

Object-class mode requires a top-marker class and a class identifying the entries that participate. Directory Server searches upward for the nearest parent carrying the top-entry class and treats that parent's subtree as the uniqueness domain.

Create a configuration:

bash
dsconf ldap1 plugin attr-uniq add "Tenant Mail Uniqueness" --attr-name mail --top-entry-oc nsContainer --subtree-entries-oc inetOrgPerson

Enable it in its own subsection when you use this pattern in production:

bash
dsconf ldap1 plugin attr-uniq enable "Tenant Mail Uniqueness"
bash
dsctl ldap1 restart

The lookup works like this:

  1. A user entry is added or modified.
  2. Directory Server searches upward for a parent containing uniqueness-top-entry-oc.
  3. That parent defines the subtree in which uniqueness is checked.
  4. Only entries matching uniqueness-subtree-entries-oc participate.

Use this approach for separate tenants, independent customer branches, departments allowed to reuse identifiers, or hosted directory domains. Keep explicit subtree-based configuration as the main example in production unless parent-based domains are required.


Enforce multiple unique attributes and create equality indexes

Directory Server allows uniqueness-attribute-name to contain multiple attributes in one configuration entry. When several attribute names are listed in one configuration, the plug-in enforces uniqueness across the combined set of those attributes, not merely within each attribute independently. A value already used in mail is also rejected when the same value is supplied through mailAlternateAddress.

Multiple configuration entries are supported, but every enabled entry is evaluated independently. Multi-valued uniqueness-attribute-name applies the check across the listed attribute set.

For unrelated attributes such as mail and employeeNumber, use separate configuration entries with independent scopes, exclusions, and object-class conditions.

The following is an alternative configuration for deployments that require one namespace across mail and mailAlternateAddress. Do not apply it alongside the current Employee Mail Uniqueness lab rule unless overlapping enforcement is intentional:

bash
dsconf ldap1 plugin attr-uniq add "Mail Address Uniqueness" --attr-name mail mailAlternateAddress --subtree "ou=Sales,dc=example,dc=com"
bash
dsconf ldap1 plugin attr-uniq enable "Mail Address Uniqueness"
bash
dsctl ldap1 restart

For employeeNumber with independent per-subtree checks, create and enable a separate rule:

bash
dsconf ldap1 plugin attr-uniq add "Employee Number Uniqueness" --attr-name employeeNumber --subtree "ou=Sales,dc=example,dc=com" "ou=Accounting,dc=example,dc=com" --across-all-subtrees off
bash
dsconf ldap1 plugin attr-uniq enable "Employee Number Uniqueness"
bash
dsctl ldap1 restart

With uniqueness-across-all-subtrees: off, the same employeeNumber value was accepted in Accounting while sales1 already used E1001 in Sales. A second Sales entry with E1001 was rejected.

Create an equality index for every attribute used in a broad uniqueness search, such as mail, employeeNumber, or uidNumber. The plug-in searches for existing matching values before allowing the write. An equality index helps Directory Server locate exact matches without scanning the entire configured subtree.

Full index creation, reindexing, and index-status checks are covered in index design and maintenance. An index improves the search the plug-in performs; it does not enforce uniqueness by itself.


Test duplicate add, modify, and move operations

Reject a duplicate add

sales1 already contains mail: [email protected]. Attempt another Sales user with the same address:

ldif
dn: uid=sales4,ou=Sales,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Sales Four
sn: Four
uid: sales4
mail: [email protected]
bash
ldapadd -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f dup-sales-mail.ldif
output
ldap_add: Constraint violation (19)
	additional info: Another entry with the same attribute value already exists (attribute: "mail ")

Confirm that the second entry was not created.

Reject a duplicate modify

Attempt to change acct1 to the shared address:

ldif
dn: uid=acct1,ou=Accounting,dc=example,dc=com
changetype: modify
replace: mail
mail: [email protected]
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f mod-dup-mail.ldif
output
ldap_modify: Constraint violation (19)
	additional info: Another entry with the same attribute value already exists (attribute: "mail ")

The original mail value remains unchanged after the rejected modify.

Multi-valued attributes and schema matching

For a multi-valued add such as mail: [email protected] and mail: [email protected], one duplicate value caused the entire LDAP add to fail on my lab host.

Test case-equivalent values against the attribute schema. For mail, [email protected] was treated as a duplicate of [email protected] because mail uses case-insensitive equality matching. Do not assume every custom attribute is case-insensitive; verify the equality matching rule in custom schema.

Reject a move into an enforced scope

389 Directory Server 3.2.0 fixed a bug where Attribute Uniqueness was not enforced during ModRDN operations. Test a move from the excluded Internal subtree into enforced Sales when the entry carries a duplicate mail value.

internal2 already contains mail: [email protected] in the excluded area. Save move-internal2-to-sales.ldif:

ldif
dn: uid=internal2,ou=Internal,ou=Sales,dc=example,dc=com
changetype: modrdn
newrdn: uid=internal2
deleteoldrdn: 1
newsuperior: ou=Sales,dc=example,dc=com

Apply it:

bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f move-internal2-to-sales.ldif
output
ldap_rename: Constraint violation (19)
	additional info: Another entry with the same attribute value already exists (attribute: "mail ")

Verify that internal2 remains under ou=Internal,ou=Sales,dc=example,dc=com:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(uid=internal2)" dn
output
dn: uid=internal2,ou=Internal,ou=Sales,dc=example,dc=com

Chaining, replication limitations, and troubleshooting

Use Attribute Uniqueness with chained databases

When uniqueness checks must follow a database link, add the Attribute Uniqueness component to the chaining configuration:

bash
dsconf ldap1 chaining config-set --add-comp="cn=attribute uniqueness,cn=plugins,cn=config"

Restart the instance after changing chaining components:

bash
dsctl ldap1 restart

The chained identity needs read, search, and compare permissions to evaluate values in the remote directory. In configuration data, the same component appears on one line:

text
nsActiveChainingComponents: cn=attribute uniqueness,cn=plugins,cn=config

Keep credentials, security, and full link configuration in the dedicated database chaining article.

Multi-supplier replication limitations

IMPORTANT
The Attribute Uniqueness plug-in validates local client writes. It does not repeat uniqueness validation when an update arrives through replication.

Example race:

text
Supplier 1 accepts:
mail: [email protected]

Supplier 2 simultaneously accepts:
mail: [email protected]

Both local checks pass because the other change has not replicated yet.

After replication converges, both values can exist.

Therefore:

  • Enable an equivalent configuration on every writable supplier.
  • Do not claim that this guarantees global real-time uniqueness.
  • Route writes for critical unique attributes through one authoritative supplier where possible.
  • Use application-level coordination or an external allocation process for strict distributed uniqueness.
  • Monitor for replication conflicts and duplicate values.
  • Do not enable the plug-in only on read-only consumers.

Periodically audit critical attributes across the replicated suffix:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(employeeNumber=*)" dn employeeNumber

Check for duplicates created by simultaneous supplier writes, imported pre-existing values, excluded-subtree values, configuration differences between suppliers, and values introduced while the plug-in was disabled.

Modify or remove a configuration

Update a configuration:

bash
dsconf ldap1 plugin attr-uniq set "Employee Mail Uniqueness" --exclude-subtree "ou=Internal,ou=Sales,dc=example,dc=com"

Disable it:

bash
dsconf ldap1 plugin attr-uniq disable "Employee Mail Uniqueness"

Remove it only after recording the configuration:

bash
dsconf ldap1 plugin attr-uniq delete "Employee Mail Uniqueness"

Restart the instance after disabling or deleting a configuration, then test whether duplicate writes are accepted so the operational effect is understood:

bash
dsctl ldap1 restart

Troubleshooting table

Symptom Likely cause Fix
Duplicate value is accepted Configuration disabled, missing restart, wrong attribute, subtree, exclusion, or object class; overlapping rule gap; write on another supplier; replicated update Verify dsconf ldap1 plugin attr-uniq show; restart after changes; confirm which supplier accepted the write
Unique value is rejected Case-equivalent value, multi-valued attribute, hidden entry, overlapping configuration, or value in a non-excluded subtree Search every configured subtree; review uniqueness-subtree-entries-oc and overlapping rules
Duplicate rejected in Sales but accepted in Accounting uniqueness-across-all-subtrees is off Enable cross-subtree checking when both branches must share one namespace
Exclusion or object-class change has no effect Another enabled configuration still covers the same attribute and subtree List every dsconf ldap1 plugin attr-uniq show output; remove overlapping rules
Entries outside the intended class are checked Missing or wrong uniqueness-subtree-entries-oc Set the object class explicitly; review overlapping configurations
Results differ between suppliers Different configuration under cn=plugins,cn=config Compare dsconf ldap1 plugin attr-uniq list and every show output on each writable supplier
Existing duplicates remain Historical data predates enforcement Audit and repair duplicates separately; the plug-in prevents future local writes only
DNA-generated values collide with manual values DNA does not reject manual duplicates Add an Attribute Uniqueness rule for the managed numeric attribute
Move into enforced scope succeeds with duplicate value Older release without ModRDN enforcement fix Upgrade to 389 DS 3.2.0 or later; retest ModRDN after restart

What's next

After you complete this guide, continue with:

Summary

  1. Audit existing attribute values before enabling enforcement.
  2. Define the attribute and effective uniqueness scope.
  3. Enable uniqueness-across-all-subtrees only where values must be shared across listed branches.
  4. Exclude intentional exception branches carefully.
  5. Restrict checks by object class when appropriate.
  6. Create equality indexes for performance.
  7. Test duplicate adds, replaces, multi-valued modifications, and moves into enforced scopes.
  8. Keep identical configurations on writable suppliers.
  9. Account for the replication race limitation.
  10. Audit historical and replicated data periodically.

References


Frequently Asked Questions

1. Does Attribute Uniqueness remove existing duplicate values?

No. The plug-in rejects future local add, modify, rename, and move operations that would introduce a duplicate value in the configured scope. Audit and repair historical duplicates before enabling strict enforcement.

2. What is the difference between SINGLE-VALUE and Attribute Uniqueness?

SINGLE-VALUE limits an entry to one value for the attribute. Attribute Uniqueness prevents the same value from appearing on another matching entry in the configured search scope.

3. Do two listed subtrees share one uniqueness namespace by default?

No. Listing multiple subtrees does not automatically enforce uniqueness across all of them. Enable uniqueness-across-all-subtrees when the same value must be unique in every listed subtree.

4. Does Attribute Uniqueness work on read-only consumers?

Configure equivalent rules on every writable supplier that accepts client writes. Replicated updates bypass the plug-in uniqueness check, so enabling the plug-in only on consumers does not protect the directory from duplicate values introduced on suppliers.

5. How does Attribute Uniqueness relate to the DNA plug-in?

DNA generates numeric values from configured ranges. Attribute Uniqueness rejects manually supplied duplicates. Use both when you want automatic allocation and duplicate rejection for the same attribute.
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 …