PingDS 8.0.0

Start here

Use these pages to get a quick, hands-on look at what PingDS software can do. You will download, install, and use DS on your local computer.

Estimated time to complete: 30-45 minutes

Install DS

DS software has no GUI. Instead, DS software is bundled with command-line tools.

Because LDAP is standard, you can use third-party GUI tools to view and edit directory data. For a short list, refer to Try third-party tools.

Prepare for installation

  1. To evaluate DS software, make sure you have 10 GB free disk space for the software and for sample data.

  2. Verify that you have a supported Java version installed on your local computer.

    For details, check the supported Java versions.

  3. If you plan to Learn HDAP, make sure the curl command is available.

    For details, refer to the curl site.

Download DS software

  1. If you do not have an account on Ping Identity Backstage, sign up for one.

  2. Sign in to Ping Identity Backstage.

  3. Find and download the latest PingDS ZIP distribution.

Install a directory server

  1. Unzip the .zip file into the file system directory where you want to install the server.

    The documentation shows the installation file system directory as /path/to/opendj.

    For example:

    • Bash

    • PowerShell

    • Zsh

    $ unzip ~/Downloads/DS-8.0.0.zip -d /path/to
    Expand-Archive DS-8.0.0.zip C:\path\to

    This example installs DS files with the cross-platform zip. When using the native installer, refer to Use the Windows MSI.

    $ unzip ~/Downloads/DS-8.0.0.zip -d /path/to
  2. Generate and save a deployment ID using the deployment ID password of your choice.

    Use this ID and its password when setting up DS servers in your deployment. The DS server uses the two together when generating other keys to protect shared secret keys and secure connections to other DS servers:

    • Bash

    • PowerShell

    • Zsh

    $ /path/to/opendj/bin/dskeymgr create-deployment-id --deploymentIdPassword password
    $ export DEPLOYMENT_ID=<deployment-id>
    C:\path\to\opendj\bat\dskeymgr.bat create-deployment-id --deploymentIdPassword password
    set DEPLOYMENT_ID=<deployment-id>
    $ /path/to/opendj/bin/dskeymgr create-deployment-id --deploymentIdPassword password
    $ export DEPLOYMENT_ID=<deployment-id>
  3. Use the setup command to set up a server with the ds-evaluation profile. The evaluation profile includes Example.com sample data, more lenient access control, and some other features.

    You must have write access to the folder where you install DS.

    The following example runs the command non-interactively. Use the same settings shown here to copy and paste the commands shown in these pages:

    • Bash

    • PowerShell

    • Zsh

    $ /path/to/opendj/setup \
     --serverId first-ds \
     --deploymentId $DEPLOYMENT_ID \
     --deploymentIdPassword password \
     --rootUserDn uid=admin \
     --rootUserPassword password \
     --monitorUserPassword password \
     --hostname localhost \
     --ldapPort 1389 \
     --ldapsPort 1636 \
     --httpsPort 8443 \
     --adminConnectorPort 4444 \
     --replicationPort 8989 \
     --profile ds-evaluation \
     --start \
     --acceptLicense
    Show output
    Validating parameters..... Done
    Configuring certificates..... Done
    Configuring server... Done
    Configuring profile DS evaluation..................... Done
    Starting directory server............... Done
    
    To see basic server status and configuration, you can launch
    editable:dsBasePath[/path/to/opendj]/bin/status
    C:\path\to\opendj\setup.bat `
     --serverId first-ds `
     --deploymentId <deployment-id> `
     --deploymentIdPassword password `
     --rootUserDn uid=admin `
     --rootUserPassword password `
     --monitorUserPassword password `
     --hostname localhost `
     --ldapPort 1389 `
     --ldapsPort 1636 `
     --httpsPort 8443 `
     --adminConnectorPort 4444 `
     --replicationPort 8989 `
     --profile ds-evaluation `
     --start `
     --acceptLicense
    Show output
    Validating parameters..... Done
    Configuring certificates..... Done
    Configuring server..... Done
    Configuring profile DS evaluation..................... Done
    Starting directory server............... Done
    
    To see basic server status and configuration, you can launch
    editable:dsWindowsBasePath[C:\path\to\opendj]\bat\status
    $ /path/to/opendj/setup \
     --serverId first-ds \
     --deploymentId $DEPLOYMENT_ID \
     --deploymentIdPassword password \
     --rootUserDn uid=admin \
     --rootUserPassword password \
     --monitorUserPassword password \
     --hostname localhost \
     --ldapPort 1389 \
     --ldapsPort 1636 \
     --httpsPort 8443 \
     --adminConnectorPort 4444 \
     --replicationPort 8989 \
     --profile ds-evaluation \
     --start \
     --acceptLicense
    Show output
    Validating parameters..... Done
    Configuring certificates..... Done
    Configuring server... Done
    Configuring profile DS evaluation..................... Done
    Starting directory server............... Done
    
    To see basic server status and configuration, you can launch
    editable:dsBasePath[/path/to/opendj]/bin/status
    More about setup options

    The setup command shown here has the following options:

    --serverId first-ds

    A server identifier string that’s unique across servers in your deployment.

    --deploymentId <deployment-id>

    The deployment ID is a random string generated using the dskeymgr command. It’s paired with a deployment ID password, which is a random string that you choose, and that you must keep secret.

    Together, the deployment ID and password serve to generate the shared master key that DS servers in the deployment require for protecting shared encryption secrets. By default, they also serve to generate a private CA and keys for TLS to protect communication between DS servers.

    When you deploy multiple servers together, reuse the same deployment ID and password for each server installation.

    --deploymentIdPassword password

    This is a random string that you choose, and that you must keep secret. It is paired with the deployment ID.

    --rootUserDn uid=admin

    These options set the credentials for the directory superuser. This user has privileges to perform all administrative operations and isn’t subject to access control. It’s called the root user due to the similarity to the Linux root user.

    The root user distinguished name (DN) identifies the directory superuser. In LDAP, a DN is the fully qualified name for a directory entry. The default name is uid=admin.

    --monitorUserPassword password

    The monitor user has the privilege to read monitoring data. This example doesn’t set the --monitorUserDn option, so the DN defaults to uid=Monitor.

    --hostname localhost

    The server uses the fully qualified domain name for identification between replicated servers.

    Using localhost is a shortcut suitable only for evaluation on your local computer. In production, set this to the fully qualified domain name, such as ds.example.com.

    --ldapPort 1389

    The reserved port for LDAP is 389. Use StartTLS to secure connections to this port. The connections aren’t secure by default.

    Examples in the documentation use 1389, which is accessible to non-privileged users.

    --ldapsPort 1636

    The reserved port for LDAPS is 636. Secure connections to this port with TLS.

    Examples in the documentation use 1636, which is accessible to non-privileged users.

    --httpsPort 8443

    The reserved port for HTTPS is 443.

    HTTP client applications access directory data and monitoring information on this port.

    Examples in the documentation use 8443, which is accessible to non-privileged users.

    --adminConnectorPort 4444

    This is the service port used to configure the server and to run tasks. Secure connections to this port with TLS.

    The port used in the documentation is 4444, which is the initial port suggested during interactive setup.

    --replicationPort 8989

    This is the service port used for replication messages.

    The port used in the documentation is 8989, which is the initial port suggested during interactive setup.

    --profile ds-evaluation

    The setup profile adds hard-coded entries for users like Babs Jensen, and groups like Directory Administrators. It also generates 100,000 sample LDAP user entries. All generated users have the same password, literally password. The generated user accounts are helpful for performance testing.

    This profile adds entries under the base DN dc=example,dc=com. A base DN is the suffix shared by all DNs in a set of directory data.

    A directory arranges LDAP entries hierarchically. The hierarchical organization resembles a file system on a PC or a web server, often visualized as an upside down tree structure, or a pyramid. In the same way that a full path uniquely identifies each file or folder in a file system, a DN uniquely identifies each LDAP entry.

    Each DN consists of components separated by commas, such as uid=bjensen,ou=People,dc=example,dc=com. The base DN matches the final components of each DN in that branch of the directory. A DN’s components reflect the hierarchy of directory entries. The user entry with DN uid=bjensen,ou=People,dc=example,dc=com is under the organizational unit entry ou=People,dc=example,dc=com, which in turn is under dc=example,dc=com.

    Basic components have the form attribute-name=attribute-value, such as dc=com. In the example dc=com, the attribute dc (DNS domain component) has the value com. The DN dc=example,dc=com reflects the DNs domain name example.com.

    --start

    By default, the setup command doesn’t start the server. This lets you complete any necessary configuration steps before starting the server for the first time, which may start the replication process.

    In this case, you have no further configuration to do. This option causes the server to start immediately.

    --acceptLicense

    Remove this option to read the license and then accept it interactively.

    You can also run the setup command interactively by starting it without options.

  4. Add the DS tools to your PATH to avoid having to specify the full path for each command:

    • Bash

    • PowerShell

    • Zsh

    $ export PATH=/path/to/opendj/bin:${PATH}
    $env:PATH += ";C:\path\to\opendj\bat"
    $ export PATH=/path/to/opendj/bin:${PATH}
  5. Run the status command:

    • Bash

    • PowerShell

    • Zsh

    $ status \
     --bindDn uid=admin \
     --bindPassword password \
     --hostname localhost \
     --port 4444 \
     --usePkcs12TrustStore /path/to/opendj/config/keystore \
     --trustStorePassword:file /path/to/opendj/config/keystore.pin
    status.bat `
     --bindDn uid=admin `
     --bindPassword password `
     --hostname localhost `
     --port 4444 `
     --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
     --trustStorePassword:file C:\path\to\opendj\config\keystore.pin
    $ status \
     --bindDn uid=admin \
     --bindPassword password \
     --hostname localhost \
     --port 4444 \
     --usePkcs12TrustStore /path/to/opendj/config/keystore \
     --trustStorePassword:file /path/to/opendj/config/keystore.pin

    The status command uses a secure connection to the administration port. To trust the server’s certificate, the command uses the server’s own truststore.

    Read the output that the status command displays.

Learn LDAP

Lightweight Directory Access Protocol (LDAP) is a standard internet protocol. The following examples show you how to use bundled DS command-line tools to send LDAP requests.

Before you try the examples, set up a server, as described in Install DS. Make sure you added the command-line tools to your PATH:

  • Bash

  • PowerShell

  • Zsh

$ export PATH=/path/to/opendj/bin:${PATH}
$env:PATH += ";C:\path\to\opendj\bat"
$ export PATH=/path/to/opendj/bin:${PATH}

Search

Searching the directory is like looking for someone’s phone number when all you know is their name. You use the value of an attribute you know—​in this case, their name—​to find their profile. Their profile—​in LDAP, their entry—​has other attributes of interest like their phone number or email address.

When looking up a person’s entry by their name, more information helps narrow down your search. If two people with the same name live in Los Angeles and New York, you need to know where they live to choose the right person. In an LDAP directory, you need to know at least the base DN for the search.

For this example, assume you know a user’s full name, Babs Jensen, and that Babs Jensen’s entry is under the base DN dc=example,dc=com. You want to look up Babs Jensen’s email and office location. The following command sends an appropriate LDAP search request to the server you installed:

  • Bash

  • PowerShell

  • Zsh

$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 cn mail street l
Show output
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Barbara Jensen
cn: Babs Jensen
l: San Francisco
mail: bjensen@example.com
street: 201 Mission Street Suite 2900
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword hifalutin `
 --baseDn dc=example,dc=com `
 "(cn=Babs Jensen)" `
 cn mail street l
Show output
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Barbara Jensen
cn: Babs Jensen
l: San Francisco
mail: bjensen@example.com
street: 201 Mission Street Suite 2900
$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 cn mail street l
Show output
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Barbara Jensen
cn: Babs Jensen
l: San Francisco
mail: bjensen@example.com
street: 201 Mission Street Suite 2900
More about the search example

Notice the following characteristics of the search:

  • The command makes a secure connection to the server using secure LDAP (LDAPS).

    The command relies on the server’s truststore to trust the CA certificate used to sign the server certificate.

  • The base DN option, --baseDn dc=example,dc=com, tells the server where to look for Babs Jensen’s entry. Servers can hold data for multiple base DNs, so this is important information.

    It is possible to restrict the scope of the search, but the default is to search the entire subtree under the base DN.

  • The command uses a search filter, "(cn=Babs Jensen)", which tells the server, "Find entries whose cn attribute exactly matches the string Babs Jensen without regard to case."

    The cn (commonName) attribute is a standard attribute for full names.

    Internally, the directory server has an equality index for the cn attribute. The directory uses the index to quickly find matches for babs jensen. The default behavior in LDAP is to ignore case, so "(cn=Babs Jensen)", "(cn=babs jensen)", and "(CN=BABS JENSEN)" are equivalent.

    If more than one entry matches the filter, the server returns multiple entries.

  • The filter is followed by a list of LDAP attributes, cn mail street l. This tells the server to return only the specified attributes in the search result entries. By default, if you do not specify the attributes to return, the server returns all the user attributes that you have the right to read.

  • The result shows attributes from a single entry. Notice that an LDAP entry, represented here in the standard LDIF format, has a flat structure with no nesting.

    The DN that uniquely identifies the entry is uid=bjensen,ou=People,dc=example,dc=com. Multiple entries can have the same attribute values, but each must have a unique DN. This is the same as saying that the leading relative distinguished name (RDN) value must be unique at this level in the hierarchy. Only one entry directly under ou=People,dc=example,dc=com has the RDN uid=bjensen.

    The mail, street, l (location), and uid attributes are all standard LDAP attributes like cn.

For additional examples, refer to LDAP search.

Modify

You installed the server with the ds-evaluation profile. That profile grants access to search Example.com data without authenticating to the directory. When modifying directory data, however, you must authenticate first. LDAP servers must know who you are to determine what you have access to.

In the following example Babs Jensen modifies the description on her own entry:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin <<EOF
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: New description
EOF
Show output
# MODIFY operation successful for DN uid=bjensen,ou=People,dc=example,dc=com
New-Item -Path . -Name "description.ldif" -ItemType "file" -Value @"
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: New description
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword hifalutin `
 description.ldif
Show output
# MODIFY operation successful for DN uid=bjensen,ou=People,dc=example,dc=com
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin <<EOF
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: New description
EOF
Show output
# MODIFY operation successful for DN uid=bjensen,ou=People,dc=example,dc=com
More about the modify example
  • Babs Jensen’s authentication credentials are provided with the --bindDn and --bindPassword options. Notice that the user identifier is Babs Jensen’s DN.

    Authentication operations bind an LDAP identity to a connection. In LDAP, a client application connects to the server, then binds an identity to the connection. An LDAP client application keeps its connection open until it finishes performing its operations. The server uses the identity bound to the connection to make authorization decisions for subsequent operations, such as search and modify requests.

    If no credentials are provided, then the identity for the connection is that of an anonymous user. As a directory administrator, you can configure access controls for anonymous users just as you configure access controls for other users.

    A simple bind involving a DN and a password is just one of several supported authentication mechanisms. The documentation frequently shows simple binds in examples because this kind of authentication is so familiar. Alternatives include authenticating with a digital certificate, or using Kerberos.

  • The modification is expressed in standard LDAP Data Interchange Format (LDIF).

    The LDIF specifies the DN of the target entry to modify. It then indicates that the change to perform is an LDAP modify, and that the value New description is to replace existing values of the description attribute.

  • Notice that the result is a comment indicating success. The command’s return code—​0, but not shown in the example—​also indicates success.

    The scripts and applications that you write should use and trust LDAP return codes.

For additional examples, refer to LDAP updates and Passwords and accounts.

Add

Authorized users can modify attributes, and can also add and delete directory entries.

The following example adds a new user entry to the directory:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=admin \
 --bindPassword password <<EOF
dn: uid=newuser,ou=People,dc=example,dc=com
uid: newuser
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
cn: New User
sn: User
ou: People
mail: newuser@example.com
userPassword: chngthspwd
EOF
Show output
# ADD operation successful for DN uid=newuser,ou=People,dc=example,dc=com
New-Item -Path . -Name "user.ldif" -ItemType "file" -Value @"
dn: uid=newuser,ou=People,dc=example,dc=com
uid: newuser
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
cn: New User
sn: User
ou: People
mail: newuser@example.com
userPassword: chngthspwd
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=admin `
 --bindPassword password `
 user.ldif
Show output
# ADD operation successful for DN uid=newuser,ou=People,dc=example,dc=com
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=admin \
 --bindPassword password <<EOF
dn: uid=newuser,ou=People,dc=example,dc=com
uid: newuser
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
cn: New User
sn: User
ou: People
mail: newuser@example.com
userPassword: chngthspwd
EOF
Show output
# ADD operation successful for DN uid=newuser,ou=People,dc=example,dc=com
More about the add example
  • The bind DN for the user requesting the add is uid=admin. It is also possible to authorize regular users to add entries.

  • The entry to add is expressed in standard LDIF.

For additional examples, refer to LDAP updates.

Delete

The following example deletes the user added in Add:

  • Bash

  • PowerShell

  • Zsh

$ ldapdelete \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=admin \
 --bindPassword password \
 uid=newuser,ou=People,dc=example,dc=com
Show output
# DELETE operation successful for DN uid=newuser,ou=People,dc=example,dc=com
ldapdelete.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=admin `
 --bindPassword password `
 uid=newuser,ou=People,dc=example,dc=com
Show output
# DELETE operation successful for DN uid=newuser,ou=People,dc=example,dc=com
$ ldapdelete \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=admin \
 --bindPassword password \
 uid=newuser,ou=People,dc=example,dc=com
Show output
# DELETE operation successful for DN uid=newuser,ou=People,dc=example,dc=com

Notice that the ldapdelete command specifies the entry to delete by its DN.

For additional examples, refer to LDAP updates.

Learn HDAP

PingDS let you access LDAP data over HTTP using HTTP Directory Access Protocol (HDAP) APIs that transform HTTP operations into LDAP operations.

Before you try the examples, follow the instructions in Install DS.

Prepare

Get the deployment CA certificate to trust the server:

  • Bash

  • PowerShell

  • Zsh

$ dskeymgr \
 export-ca-cert \
 --deploymentId $DEPLOYMENT_ID \
 --deploymentIdPassword password \
 --outputFile ca-cert.pem

Configure Windows to trust the deployment CA certificate. Import the deployment CA from the server truststore using Microsoft Management Console (MMC):

  1. Run Microsoft Management Console (mmc.exe).

  2. Add the certificates snap-in to import the deployment CA certificate:

    • In the console, select File > Add/Remove Snap-in, then Add.

    • Select Certificates from the list of snap-ins and click Add.

    • Finish the wizard.

  3. Import the deployment CA certificate using the snap-in:

    • Select Console Root > Trusted Root Certification Authorities > Certificates.

    • In the Action menu, select Import to open the wizard.

    • Use the wizard to import the deployment CA certificate from the server truststore file, C:\path\to\opendj\config\keystore.

      The truststore password is the text in the file C:\path\to\opendj\config\keystore.pin.

$ dskeymgr \
 export-ca-cert \
 --deploymentId $DEPLOYMENT_ID \
 --deploymentIdPassword password \
 --outputFile ca-cert.pem

Create

Use HDAP to create a user resource:

  • Bash

  • JavaScript

  • PowerShell

  • Python

  • Ruby

  • Zsh

$ curl \
--request POST \
--cacert ca-cert.pem \
--user uid=admin:password \
--header 'Content-Type: application/json' \
--data '{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "objectClass" : [ "person", "inetOrgPerson", "organizationalPerson", "top" ],
  "cn" : [ "New User" ],
  "givenName" : [ "New" ],
  "mail" : [ "newuser@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 408 555 1212" ],
  "uid" : [ "newuser" ]
}' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "objectClass" : [ "person", "inetOrgPerson", "organizationalPerson", "top" ],
  "cn" : [ "New User" ],
  "givenName" : [ "New" ],
  "mail" : [ "newuser@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 408 555 1212" ],
  "uid" : [ "newuser" ]
}
const { doRequest, getOptions } = require('./utils')

const options = getOptions({
    path: '/hdap/dc=com/dc=example/ou=People?_action=create',
    credentials: 'uid=admin:password',
    method: 'POST',
    body: {
        "_id": "dc=com/dc=example/ou=People/uid=newuser",
        "objectClass": ["person", "inetOrgPerson", "organizationalPerson", "top"],
        "cn": ["New User"],
        "givenName": ["New"],
        "mail": ["newuser@example.com"],
        "manager": ["dc=com/dc=example/ou=People/uid=bjensen"],
        "sn": ["User"],
        "telephoneNumber": ["+1 408 555 1212"],
        "uid": ["newuser"]
    }
})

doRequest('HDAP: create with POST', options)
    .then(response => { console.log(response) })
    .catch(error => { console.error(error) })

Source files for this sample: create-newuser.js, utils.js

$Credentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("uid=admin:password"))
$Headers = @{
    Authorization = "Basic $Credentials"
}
Invoke-RestMethod `
 -Uri https://localhost:8443/hdap/dc=com/dc=example/ou=People `
 -Method Post `
 -Headers $Headers `
 -ContentType application/json `
 -Body @"
 {
    "_id" : "dc=com/dc=example/ou=People/uid=newuser",
    "objectClass" : [ "person", "inetOrgPerson", "organizationalPerson", "top" ],
    "cn" : [ "New User" ],
    "givenName" : [ "New" ],
    "mail" : [ "newuser@example.com" ],
    "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
    "sn" : [ "User" ],
    "telephoneNumber" : [ "+1 408 555 1212" ],
    "uid" : [ "newuser" ]
 }
"@ | ConvertTo-JSON
Show output
{
    "_id" : "dc=com/dc=example/ou=People/uid=newuser",
    "objectClass" : [ "person", "inetOrgPerson", "organizationalPerson", "top" ],
    "cn" : [ "New User" ],
    "givenName" : [ "New" ],
    "mail" : [ "newuser@example.com" ],
    "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
    "sn" : [ "User" ],
    "telephoneNumber" : [ "+1 408 555 1212" ],
    "uid" : [ "newuser" ]
}
#!/usr/bin/env python3

import requests
from requests.auth import HTTPBasicAuth
import utils

body = {
    '_id': 'dc=com/dc=example/ou=People/uid=newuser',
    'objectClass': ['person', 'inetOrgPerson', 'organizationalPerson', 'top'],
    'cn': ['New User'],
    'givenName': ['New'],
    'mail': ['newuser@example.com'],
    'manager': ['dc=com/dc=example/ou=People/uid=bjensen'],
    'sn': ['User'],
    'telephoneNumber': ['+1 408 555 1212'],
    'uid': ['newuser']
}
headers = { 'Content-Type': 'application/json' }
response = requests.post(
    f'https://{utils.host}:{utils.port}/hdap/dc=com/dc=example/ou=People',
    auth=HTTPBasicAuth('uid=admin', 'password'),
    headers=headers,
    json=body,
    verify=utils.ca_pem)
print('Status code: %d\nJSON: %s' % (response.status_code, response.json()))

Source files for this sample: utils.py, create-newuser.py

require_relative 'utils.rb'
require 'faraday'
require 'json'

utils = Utils.new('', '')
options = { ca_file: utils.ca_pem }
hdap = Faraday.new(url: "https://#{utils.host}:#{utils.port}/hdap/", ssl: options) do |f|
    f.headers['Content-Type'] = 'application/json'
    f.request :authorization, :basic, 'uid=admin', 'password'
end
body = {
    '_id' => "dc=com/dc=example/ou=People/uid=newuser",
    'objectClass' => ["person", "inetOrgPerson", "organizationalPerson", "top"],
    'cn' => ["New User"],
    'givenName' => ["New"],
    'mail' => ["newuser@example.com"],
    'manager' => ["dc=com/dc=example/ou=People/uid=bjensen"],
    'sn' => ["User"],
    'telephoneNumber' => ["+1 408 555 1212"],
    'uid' => ["newuser"]
}
response = hdap.post do |h|
    h.path = 'dc=com/dc=example/ou=People'
    h.body = JSON.generate(body)
end

puts "Status code: #{response.status}\nJSON: #{response.body}"

Source files for this sample: utils.rb, create-newuser.rb

HDAP Ruby examples require Ruby 3.2 and the faraday and json gems.

$ curl \
--request POST \
--cacert ca-cert.pem \
--user uid=admin:password \
--header 'Content-Type: application/json' \
--data '{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "objectClass" : [ "person", "inetOrgPerson", "organizationalPerson", "top" ],
  "cn" : [ "New User" ],
  "givenName" : [ "New" ],
  "mail" : [ "newuser@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 408 555 1212" ],
  "uid" : [ "newuser" ]
}' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "objectClass" : [ "person", "inetOrgPerson", "organizationalPerson", "top" ],
  "cn" : [ "New User" ],
  "givenName" : [ "New" ],
  "mail" : [ "newuser@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 408 555 1212" ],
  "uid" : [ "newuser" ]
}
  • The command makes a secure connection to the server using HTTPS.

  • The user performing the HTTP POST is the directory superuser.

    The default authorization mechanism for HTTP access is HTTP Basic authentication. The superuser’s HTTP user ID, admin, is mapped to the LDAP DN, uid=admin. HDAP uses the DN and password to perform a simple LDAP bind for authentication. The directory uses its LDAP-based access control mechanisms to authorize the operation.

  • The successful response is the JSON resource that the command created.

    Fields names starting with an underscore like _id are reserved. For details, refer to HDAP API reference.

For additional details, refer to HDAP API reference and Create.

Read

Use HDAP to read a user resource:

  • Bash

  • JavaScript

  • PowerShell

  • Python

  • Ruby

  • Zsh

$ curl \
--request GET \
--cacert ca-cert.pem \
--user dc=com/dc=example/ou=People/uid=bjensen:hifalutin \
--header 'Content-Type: application/json' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "_rev" : "<revision>",
  "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "cn" : [ "New User" ],
  "givenName" : [ "New" ],
  "mail" : [ "newuser@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 408 555 1212" ],
  "uid" : [ "newuser" ]
}
const { doRequest, getOptions } = require('./utils')

const options = getOptions({
    path: '/hdap/dc=com/dc=example/ou=People/uid=newuser'
})

doRequest('HDAP: read with GET', options)
    .then(response => { console.log(response) })
    .catch(error => { console.error(error) })

Source files for this sample: read-newuser.js, utils.js

$Credentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("dc=com/dc=example/ou=People/uid=bjensen:hifalutin"))
$Headers = @{
    Authorization = "Basic $Credentials"
}
Invoke-RestMethod `
 -Uri https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser `
 -Method Get `
 -Headers $Headers `
 -ContentType application/json | ConvertTo-JSON
Show output
{
    "_id" : "dc=com/dc=example/ou=People/uid=newuser",
    "_rev" : "<revision>",
    "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
    "cn" : [ "New User" ],
    "givenName" : [ "New" ],
    "mail" : [ "newuser@example.com" ],
    "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
    "sn" : [ "User" ],
    "telephoneNumber" : [ "+1 408 555 1212" ],
    "uid" : [ "newuser" ]
}
#!/usr/bin/env python3

import requests
from requests.auth import HTTPBasicAuth
import utils

response = requests.get(
    f'https://{utils.host}:{utils.port}/hdap/dc=com/dc=example/ou=People/uid=newuser',
    auth=HTTPBasicAuth('dc=com/dc=example/ou=People/uid=kvaughan', 'bribery'),
    verify=utils.ca_pem)
print('Status code: %d\nJSON: %s' % (response.status_code, response.json()))

Source files for this sample: utils.py, read-newuser.py

require_relative 'utils.rb'
require 'faraday'

utils = Utils.new('', '')
options = { ca_file: utils.ca_pem }
hdap = Faraday.new(url: "https://#{utils.host}:#{utils.port}/hdap/", ssl: options) do |f|
    f.headers['Content-Type'] = 'application/json'
    f.request :authorization, :basic, 'dc=com/dc=example/ou=People/uid=bjensen', 'hifalutin'
end
response = hdap.get('dc=com/dc=example/ou=People/uid=newuser')

puts "Status code: #{response.status}\nJSON: #{response.body}"

Source files for this sample: utils.rb, read-newuser.rb

HDAP Ruby examples require Ruby 3.2 and the faraday and json gems.

$ curl \
--request GET \
--cacert ca-cert.pem \
--user dc=com/dc=example/ou=People/uid=bjensen:hifalutin \
--header 'Content-Type: application/json' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "_rev" : "<revision>",
  "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "cn" : [ "New User" ],
  "givenName" : [ "New" ],
  "mail" : [ "newuser@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 408 555 1212" ],
  "uid" : [ "newuser" ]
}

Authenticate when making this HTTP GET request. If no credentials are specified, the response is the HTTP 401 Unauthorized:

{"code":401,"reason":"Unauthorized","message":"Invalid Credentials"}

In other words, the HTTP Basic authorization mechanism requires authentication even for read operations.

For additional details, refer to HDAP API reference and Read. You can also query collections of resources, as described in Query.

Update

Use HDAP to update a user resource:

  • Bash

  • JavaScript

  • PowerShell

  • Python

  • Ruby

  • Zsh

$ curl \
--request PUT \
--cacert ca-cert.pem \
--user uid=admin:password \
--header 'Content-Type: application/json' \
--header "If-Match: *" \
--data '{
  "cn" : [ "Updated User" ],
  "givenName" : [ "Updated" ],
  "mail" : [ "updated.user@example.com" ],
  "telephoneNumber" : [ "+1 234 567 8910" ]
}' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "cn" : [ "Updated User" ],
  "givenName" : [ "Updated" ],
  "mail" : [ "updated.user@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 234 567 8910" ],
  "uid" : [ "newuser" ]
}
const { doRequest, getOptions } = require('./utils')

const options = getOptions({
    path: '/hdap/dc=com/dc=example/ou=People/uid=newuser',
    credentials: 'uid=admin:password',
    method: 'PUT',
    body: {
        "cn": ["Updated User"],
        "givenName": ["Updated"],
        "mail": ["updated.user@example.com"],
        "telephoneNumber": ["+1 234 567 8910"]
    }
})

doRequest('HDAP: update newuser', options)
    .then(response => { console.log(response) })
    .catch(error => { console.error(error) })

Source files for this sample: update-newuser.js, utils.js

$Credentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("uid=admin:password"))
$Headers = @{
    "Authorization" = "Basic $Credentials"
    "If-Match"      = "*"
}
Invoke-RestMethod `
 -Uri https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser `
 -Method Put `
 -Headers $Headers `
 -ContentType application/json `
 -Body @"
 {
    "cn" : [ "Updated User" ],
    "givenName" : [ "Updated" ],
    "mail" : [ "updated.user@example.com" ],
    "telephoneNumber" : [ "+1 234 567 8910" ]
 }
"@ | ConvertTo-JSON
Show output
{
    "_id" : "dc=com/dc=example/ou=People/uid=newuser",
    "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
    "cn" : [ "Updated User" ],
    "givenName" : [ "Updated" ],
    "mail" : [ "updated.user@example.com" ],
    "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
    "sn" : [ "User" ],
    "telephoneNumber" : [ "+1 234 567 8910" ],
    "uid" : [ "newuser" ]
}
#!/usr/bin/env python3

import requests
from requests.auth import HTTPBasicAuth
import utils

body = {
    'cn': ['Updated User'],
    'givenName': ['Updated'],
    'mail': ['updated.user@example.com'],
    'telephoneNumber': ['+1 234 567 8910']
}
headers = { 'Content-Type': 'application/json' }
response = requests.put(
    f'https://{utils.host}:{utils.port}/hdap/dc=com/dc=example/ou=People/uid=newuser',
    auth=HTTPBasicAuth('uid=admin', 'password'),
    headers=headers,
    json=body,
    verify=utils.ca_pem)
print('Status code: %d\nJSON: %s' % (response.status_code, response.json()))

Source files for this sample: utils.py, update-newuser.py

require_relative 'utils.rb'
require 'faraday'
require 'json'

utils = Utils.new('', '')
options = { ca_file: utils.ca_pem }
fields = { '_fields': 'telephoneNumber' }
hdap = Faraday.new(url: "https://#{utils.host}:#{utils.port}/hdap/", params: fields, ssl: options) do |f|
    f.headers['Content-Type'] = 'application/json'
    f.request :authorization, :basic, 'uid=admin', 'password'
end
body = {
    "cn" => ["Updated User"],
    "givenName" => ["Updated"],
    "mail" => ["updated.user@example.com"],
    "telephoneNumber" => ["+1 234 567 8910"]
}
response = hdap.put do |h|
    h.path = 'dc=com/dc=example/ou=People/uid=newuser'
    h.body = JSON.generate(body)
end

puts "Status code: #{response.status}\nJSON: #{response.body}"

Source files for this sample: utils.rb, update-newuser.rb

HDAP Ruby examples require Ruby 3.2 and the faraday and json gems.

$ curl \
--request PUT \
--cacert ca-cert.pem \
--user uid=admin:password \
--header 'Content-Type: application/json' \
--header "If-Match: *" \
--data '{
  "cn" : [ "Updated User" ],
  "givenName" : [ "Updated" ],
  "mail" : [ "updated.user@example.com" ],
  "telephoneNumber" : [ "+1 234 567 8910" ]
}' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "cn" : [ "Updated User" ],
  "givenName" : [ "Updated" ],
  "mail" : [ "updated.user@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 234 567 8910" ],
  "uid" : [ "newuser" ]
}

HDAP versions resources with revision numbers. A revision is specified in the resource’s _rev field.

The --header "If-Match: *" tells HDAP to replace the resource regardless of its revision. Alternatively, set --header "If-Match: revision" to replace the resource only if its revision matches.

For additional details, refer to HDAP API reference and Update. You can also patch resources instead of replacing them entirely. Refer to Patch.

Delete

Use HDAP to delete a user resource:

  • Bash

  • JavaScript

  • PowerShell

  • Python

  • Ruby

  • Zsh

$ curl \
--request DELETE \
--cacert ca-cert.pem \
--user uid=admin:password \
--header 'Content-Type: application/json' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "_rev" : "<revision>",
  "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "cn" : [ "Updated User" ],
  "givenName" : [ "Updated" ],
  "mail" : [ "updated.user@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 234 567 8910" ],
  "uid" : [ "newuser" ]
}
const { doRequest, getOptions } = require('./utils')

const options = getOptions({
    path: '/hdap/dc=com/dc=example/ou=People/uid=newuser',
    credentials: 'uid=admin:password',
    method: 'DELETE'
})

doRequest('HDAP: delete newuser', options)
    .then(response => { console.log(response) })
    .catch(error => { console.error(error) })

Source files for this sample: delete-newuser.js, utils.js

$Credentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("uid=admin:password"))
$Headers = @{
    Authorization = "Basic $Credentials"
}
Invoke-RestMethod `
 -Uri https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser `
 -Method Delete `
 -Headers $Headers `
 -ContentType application/json | ConvertTo-JSON
Show output
{
    "_id" : "dc=com/dc=example/ou=People/uid=newuser",
    "_rev" : "<revision>",
    "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
    "cn" : [ "Updated User" ],
    "givenName" : [ "Updated" ],
    "mail" : [ "updated.user@example.com" ],
    "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
    "sn" : [ "User" ],
    "telephoneNumber" : [ "+1 234 567 8910" ],
    "uid" : [ "newuser" ]
}
#!/usr/bin/env python3

import requests
from requests.auth import HTTPBasicAuth
import utils

response = requests.delete(
    f'https://{utils.host}:{utils.port}/hdap/dc=com/dc=example/ou=People/uid=newuser',
    auth=HTTPBasicAuth('uid=admin', 'password'),
    verify=utils.ca_pem)
print('Status code: %d\nJSON: %s' % (response.status_code, response.json()))

Source files for this sample: utils.py, delete-newuser.py

require_relative 'utils.rb'
require 'faraday'

utils = Utils.new('', '')
options = { ca_file: utils.ca_pem }
hdap = Faraday.new(url: "https://#{utils.host}:#{utils.port}/hdap/", ssl: options) do |f|
    f.headers['Content-Type'] = 'application/json'
    f.request :authorization, :basic, 'uid=admin', 'password'
end
response = hdap.delete('dc=com/dc=example/ou=People/uid=newuser')

puts "Status code: #{response.status}\nJSON: #{response.body}"

Source files for this sample: utils.rb, delete-newuser.rb

HDAP Ruby examples require Ruby 3.2 and the faraday and json gems.

$ curl \
--request DELETE \
--cacert ca-cert.pem \
--user uid=admin:password \
--header 'Content-Type: application/json' \
'https://localhost:8443/hdap/dc=com/dc=example/ou=People/uid=newuser?_prettyPrint=true'
Show output
{
  "_id" : "dc=com/dc=example/ou=People/uid=newuser",
  "_rev" : "<revision>",
  "objectClass" : [ "top", "person", "organizationalPerson", "inetOrgPerson" ],
  "cn" : [ "Updated User" ],
  "givenName" : [ "Updated" ],
  "mail" : [ "updated.user@example.com" ],
  "manager" : [ "dc=com/dc=example/ou=People/uid=bjensen" ],
  "sn" : [ "User" ],
  "telephoneNumber" : [ "+1 234 567 8910" ],
  "uid" : [ "newuser" ]
}

For additional details, refer to HDAP API reference and Delete.

Learn replication

Replication provides automatic data synchronization between directory servers. It ensures that all directory servers eventually share a consistent set of directory data.

More about replication

Replication requires two or more directory servers and additional configuration. This page takes you though the setup process quickly, providing commands that you can reuse. It does not explain each command in detail.

Two replicated DS servers with a client application using each server

For a full discussion of the subject, refer to Replication and the related pages.

Add a replica

High-level steps:

  1. Unpack the files for a second directory server in a different folder.

  2. Set up the new server as a replica of the first server using the generated <deployment-id> from Install DS.

The following example demonstrates the process:

  • Bash

  • PowerShell

  • Zsh

# Unpack files for a second, replica server in a different folder:
cd ~/Downloads && unzip ~/Downloads/DS-8.0.0.zip && mv opendj /path/to/replica

# Set up a second, replica server:
/path/to/replica/setup \
 --serverId second-ds \
 --deploymentId $DEPLOYMENT_ID \
 --deploymentIdPassword password \
 --rootUserDn uid=admin \
 --rootUserPassword password \
 --hostname localhost \
 --ldapPort 11389 \
 --ldapsPort 11636 \
 --adminConnectorPort 14444 \
 --replicationPort 18989 \
 --bootstrapReplicationServer localhost:8989 \
 --profile ds-evaluation \
 --start \
 --acceptLicense
# Unpack files for a second, replica server in a different folder:
Expand-Archive DS-8.0.0.zip C:\Temp
Rename-Item -Path C:\Temp\opendj -NewName C:\Temp\replica
Move-Item C:\Temp\replica C:\path\to

# Set up a second, replica server:
C:\path\to\replica\setup.bat `
 --serverId second-ds `
 --deploymentId <deployment-id> `
 --deploymentIdPassword password `
 --rootUserDn uid=admin `
 --rootUserPassword password `
 --hostname localhost `
 --ldapPort 11389 `
 --ldapsPort 11636 `
 --adminConnectorPort 14444 `
 --replicationPort 18989 \
 --bootstrapReplicationServer locahost:8989 \
 --profile ds-evaluation `
 --start `
 --acceptLicense
# Unpack files for a second, replica server in a different folder:
cd ~/Downloads && unzip ~/Downloads/DS-8.0.0.zip && mv opendj /path/to/replica

# Set up a second, replica server:
/path/to/replica/setup \
 --serverId second-ds \
 --deploymentId $DEPLOYMENT_ID \
 --deploymentIdPassword password \
 --rootUserDn uid=admin \
 --rootUserPassword password \
 --hostname localhost \
 --ldapPort 11389 \
 --ldapsPort 11636 \
 --adminConnectorPort 14444 \
 --replicationPort 18989 \
 --bootstrapReplicationServer localhost:8989 \
 --profile ds-evaluation \
 --start \
 --acceptLicense

Try replication

With the new replica set up and started, show that replication works:

  • Bash

  • PowerShell

  • Zsh

# Update a description on the first server:
ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin << EOF
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Replicate this
EOF

# On the first server, read the description to see the effects of your change:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description

# On the second server, read the description to see the change has been replicated:
ldapsearch \
 --hostname localhost \
 --port 11636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description
# Update a description on the first server:
New-Item -Path . -Name "mod-desc.ldif" -ItemType "file" -Value @"
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Replicate this
"@

ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword password `
 mod-desc.ldif

# On the first server, read the description to see the effects of your change:
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword hifalutin `
 --baseDn dc=example,dc=com `
 "(cn=Babs Jensen)" `
 description

# On the second server, read the description to see the change has been replicated:
ldapsearch.bat `
 --hostname localhost `
 --port 11636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword hifalutin `
 --baseDn dc=example,dc=com `
 "(cn=Babs Jensen)" `
 description
# Update a description on the first server:
ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin << EOF
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Replicate this
EOF

# On the first server, read the description to see the effects of your change:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description

# On the second server, read the description to see the change has been replicated:
ldapsearch \
 --hostname localhost \
 --port 11636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description

Show replication works despite crashes and network interruptions:

  • Bash

  • PowerShell

  • Zsh

# Stop the second server to simulate a network outage or server crash:
/path/to/replica/bin/stop-ds

# On the first server, update the description again:
ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin <<EOF
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Second server is stopped
EOF

# On the first server, read the description to see the change:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description

# Start the second server again to simulate recovery:
/path/to/replica/bin/start-ds

# On the second server, read the description to check that replication has resumed:
ldapsearch \
 --hostname localhost \
 --port 11636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description
# Stop the second server to simulate a network outage or server crash:
C:\path\to\replica\bat\stop-ds.bat

# On the first server, update the description again:
New-Item -Path . -Name "mod-desc2.ldif" -ItemType "file" -Value @"
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Second server is stopped
"@

ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword password `
mod-desc2.ldif

# On the first server, read the description to see the change:
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword hifalutin `
 --baseDn dc=example,dc=com `
 "(cn=Babs Jensen)" `
 description

# Start the second server again to simulate recovery:
C:\path\to\replica\bat\start-ds.bat

# On the second server, read the description to check that replication has resumed:
ldapsearch.bat `
 --hostname localhost `
 --port 11636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword hifalutin `
 --baseDn dc=example,dc=com `
 "(cn=Babs Jensen)" `
 description
# Stop the second server to simulate a network outage or server crash:
/path/to/replica/bin/stop-ds

# On the first server, update the description again:
ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin <<EOF
dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: modify
replace: description
description: Second server is stopped
EOF

# On the first server, read the description to see the change:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description

# Start the second server again to simulate recovery:
/path/to/replica/bin/start-ds

# On the second server, read the description to check that replication has resumed:
ldapsearch \
 --hostname localhost \
 --port 11636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --baseDn dc=example,dc=com \
 "(cn=Babs Jensen)" \
 description

Unlike some databases, DS replication does not operate in active-passive mode. Instead, you read and write on any running server. Replication replays your changes as soon as possible. Show this to check your understanding:

  1. Stop the first server.

    Hint

    Use the stop-ds command.

  2. Modify an entry on the second server.

    Hint

    Refer to Modify.

  3. Restart the first server.

    Hint

    Use the start-ds command.

  4. Search for the modified entry on the first server to check that replication replays the change.

    Hint

    Refer to Search.

Notifications

Some applications require notification when directory data updates occur. For example, IDM can sync directory data with another database. Other applications do more processing when certain updates occur.

Replicated DS directory servers publish an external change log over LDAP. This changelog lets authorized client applications read changes to directory data:

  • Bash

  • PowerShell

  • Zsh

$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=admin \
 --bindPassword password \
 --baseDN cn=changelog \
 --control "ecl:true" \
 "(&)" \
 changes changeLogCookie targetDN
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=admin `
 --bindPassword password `
 --baseDN cn=changelog `
 --control "ecl:true" `
 "(objectclass=*)" `
 changes changeLogCookie targetDN
$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=admin \
 --bindPassword password \
 --baseDN cn=changelog \
 --control "ecl:true" \
 "(&)" \
 changes changeLogCookie targetDN

When looking at the output of the command (not shown here), notice that the changes values are base64-encoded in LDIF because they include line breaks. You can use the DS base64 command to decode them. For details, refer to Changelog for notifications.

Measure performance

DS directory servers offer high throughput and low response times for most operations. DS software includes the following command-line tools for measuring performance of common LDAP operations:

Before trying the examples that follow, work through the previous examples. You should have two directory server replicas running on your local computer, as described in Learn replication:

Two replicated DS servers with a client application running a performance tool

The following examples show how to measure and verify basic directory performance. For a deeper dive into the subject, read Performance tuning.

Measure modification rates

In deployment, you can expect many directory client applications to change directory data in parallel. The directory has to serve all these requests with high throughput (lots of requests) and low latency (quick responses), so all the client applications can get their work done quickly.

As a first step towards tuning your directory service performance, get a sense of the throughput and response times you can expect by measuring the LDAP modification rate with the modrate command:

  • Bash

  • PowerShell

  • Zsh

# Run modrate for 10 seconds against the first server:
modrate \
 --maxDuration 10 \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --noRebind \
 --numConnections 4 \
 --numConcurrentRequests 4 \
 --targetDn "uid=user.{1},ou=people,dc=example,dc=com" \
 --argument "rand(0,100000)" \
 --argument "randstr(16)" \
 "description:{2}"

# Read number of modify requests on the LDAPS port:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=monitor \
 --bindPassword password \
 --baseDN "cn=LDAPS,cn=connection handlers,cn=monitor" \
 "(&)" \
 ds-mon-requests-modify
# Run modrate for 10 seconds against the first server, and observe the performance numbers:
modrate.bat `
 --maxDuration 10 `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword password `
 --noRebind `
 --numConnections 4 `
 --numConcurrentRequests 4 `
 --targetDn "uid=user.{1},ou=people,dc=example,dc=com" `
 --argument "rand(0,100000)" `
 --argument "randstr(16)" `
 "description:{2}"

# Read number of modify requests on the LDAPS port:
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=monitor `
 --bindPassword password `
 --baseDN "cn=LDAPS,cn=connection handlers,cn=monitor" `
 "(objectclass=*)" `
 ds-mon-requests-modify
# Run modrate for 10 seconds against the first server:
modrate \
 --maxDuration 10 \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --noRebind \
 --numConnections 4 \
 --numConcurrentRequests 4 \
 --targetDn "uid=user.{1},ou=people,dc=example,dc=com" \
 --argument "rand(0,100000)" \
 --argument "randstr(16)" \
 "description:{2}"

# Read number of modify requests on the LDAPS port:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=monitor \
 --bindPassword password \
 --baseDN "cn=LDAPS,cn=connection handlers,cn=monitor" \
 "(&)" \
 ds-mon-requests-modify

When reading the modrate command output, notice that it shows statistics for throughput (operations/second), response times (milliseconds), and errors/second. If you expect all operations to succeed and yet err/sec is not 0.0, the command options are no doubt incorrectly set. For an explanation of the command output, refer to modrate.

Notice that the monitoring attributes hold similar, alternative statistics.

Measure search rates

Your directory service exists to hold identity data and to make it easy and quick to find. Almost all directory client applications search the directory. Some applications, such as those providing naming services or those in a call path, require very low latency.

To get a sense of the throughput and response times you can expect from your directory even before you tune performance, measure the LDAP search rate with the searchrate command:

  • Bash

  • PowerShell

  • Zsh

# Run searchrate for 10 seconds against the first server:
searchrate \
 --maxDuration 10 \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --noRebind \
 --numConnections 4 \
 --numConcurrentRequests 4 \
 --baseDn "dc=example,dc=com" \
 --argument "rand(0,100000)" \
 "(uid=user.{})"

# Read number of subtree search requests on the LDAPS port:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=monitor \
 --bindPassword password \
 --baseDN "cn=LDAPS,cn=connection handlers,cn=monitor" \
 "(&)" \
 ds-mon-requests-search-sub
# Run searchrate for 10 seconds against the first server:
searchrate.bat `
 --maxDuration 10 `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=bjensen,ou=People,dc=example,dc=com `
 --bindPassword password `
 --noRebind `
 --numConnections 4 `
 --numConcurrentRequests 4 `
 --baseDn "dc=example,dc=com" `
 --argument "rand(0,100000)" `
 "(uid=user.{})"

# Read number of subtree search requests on the LDAPS port:
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=monitor `
 --bindPassword password `
 --baseDN "cn=LDAPS,cn=connection handlers,cn=monitor" `
 "(objectclass=*)" `
 ds-mon-requests-search-sub
# Run searchrate for 10 seconds against the first server:
searchrate \
 --maxDuration 10 \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=bjensen,ou=People,dc=example,dc=com \
 --bindPassword hifalutin \
 --noRebind \
 --numConnections 4 \
 --numConcurrentRequests 4 \
 --baseDn "dc=example,dc=com" \
 --argument "rand(0,100000)" \
 "(uid=user.{})"

# Read number of subtree search requests on the LDAPS port:
ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=monitor \
 --bindPassword password \
 --baseDN "cn=LDAPS,cn=connection handlers,cn=monitor" \
 "(&)" \
 ds-mon-requests-search-sub

Notice that searchrate command output resembles that of the modrate command. The searchrate output also indicates how many entries each search returned. For an explanation of the command output, refer to searchrate.

Check replication

When you measured directory performance, the modrate command made many changes to user’s entries. Replication between your two DS replicas should replay each change so client applications get the same response regardless of which replica they use to read a user’s entry.

Check the data on both replicas is synchronized. The following example uses monitoring metrics to check replication delay is zero on each replica:

  • Bash

  • PowerShell

  • Zsh

$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=monitor \
 --bindPassword password \
 --baseDN cn=monitor \
 "(ds-mon-current-delay=*)" \
 ds-mon-current-delay
Show output
dn: ds-mon-domain-name=dc=example\,dc=com,cn=replicas,cn=replication,cn=monitor
ds-mon-current-delay: 0

dn: ds-mon-server-id=second-ds,cn=remote replicas,ds-mon-domain-name=dc=example\,dc=com,cn=replicas,cn=replication,cn=monitor
ds-mon-current-delay: 0
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=monitor `
 --bindPassword password `
 --baseDN cn=monitor `
 "(ds-mon-current-delay=*)" `
 ds-mon-current-delay
Show output
dn: ds-mon-domain-name=dc=example\,dc=com,cn=replicas,cn=replication,cn=monitor
ds-mon-current-delay: 0

dn: ds-mon-server-id=second-ds,cn=remote replicas,ds-mon-domain-name=dc=example\,dc=com,cn=replicas,cn=replication,cn=monitor
ds-mon-current-delay: 0
$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=monitor \
 --bindPassword password \
 --baseDN cn=monitor \
 "(ds-mon-current-delay=*)" \
 ds-mon-current-delay
Show output
dn: ds-mon-domain-name=dc=example\,dc=com,cn=replicas,cn=replication,cn=monitor
ds-mon-current-delay: 0

dn: ds-mon-server-id=second-ds,cn=remote replicas,ds-mon-domain-name=dc=example\,dc=com,cn=replicas,cn=replication,cn=monitor
ds-mon-current-delay: 0

Learn access control

Until now, you have used the evaluation setup profile, which makes it easy to access Example.com data. It helps you learn and demonstrate directory services without explicitly granting access after server setup.

In a production directory service where security is important, access is under tighter control. In most cases, access is denied by default to prevent accidental information leaks. You must explicitly grant access where required. To grant access, use access control instructions (ACIs).

The sample ACIs described on this page demonstrate some but not all ACI features.

For a deeper dive into the subject, read Access control.

About ACIs

ACIs are implemented as operational LDAP attributes.

Operational attributes are not meant to store application data but to influence server behavior. They hold internal information for the server’s own use, like replication data, timestamps, or ACIs, which the server needs to provide features like replication, account lockout, or access control. Directory servers only return operational attributes in search results when explicitly requested.

Each ACI influences server behavior by indicating:

  • Which directory data it targets

  • Which permissions it allows or denies

  • Which users or groups it applies to

  • Under which conditions (time, network origin, connection security, user properties) it applies

Example ACI with explanation

The following example ACI gives users access to change their own passwords:

aci: (targetattr = "authPassword || userPassword")
 (version 3.0;acl "Allow users to change their own passwords";
 allow (write)(userdn = "ldap:///self");)

Consider the characteristics of this ACI attribute:

Target Entries and Scope

The target entries and scope for this ACI are implicit.

The default target is the entry with this aci attribute.

The default scope includes the target entry and all its subordinates.

In other words, if you set this ACI on ou=People,dc=example,dc=com, it affects all users under that base entry. For example, Babs Jensen, uid=bjensen,ou=People,dc=example,dc=com, can set her own password.

Target Attributes

This ACI affects operations on either of the standard password attributes: (targetattr = "authPassword || userPassword").

The ACI only has an effect when an operation targets either authPassword or userPassword and any subtypes of those attribute types.

Permissions

This ACI affects only operations that change affected attributes: allow (write).

If this is the only ACI that targets password attributes, users have access to change their own passwords, but they do not have access to read passwords.

Subjects

This ACI has an effect when the target entry is the same as the bind DN: (userdn = "ldap:///self").

This means the user must authenticate before changing their password.

Documentation

The wrapper around the permissions and subjects contains human-readable documentation about the ACI: (version 3.0;acl "Allow users to change their own passwords"; …​ ;).

Version 3.0 is the only supported ACI version.

Conditions

This ACI does not define any conditions. It applies all the time, for connections from all networks, and so forth.

Server configuration settings can further constrain how clients connect. Such constraints are not specified by this ACI, however.

Use ACIs

To write ACI attributes:

  • A user must have the modify-acl administrative privilege.

    Privileges are server configuration settings that control access to administrative operations.

  • An ACI must give the user permission to change aci attributes.

By default, only the directory superuser has the right to add, delete, or modify ACI attributes. The directory superuser account has a bypass-acl privilege to perform operations without regard to ACIs.

Any account with permission to change ACIs is dangerous because the power can be misused. The user with permissions to change ACIs can give themselves full access to all directory data in their scope.

Prepare to use the examples:

Stop running servers

Use each server’s stop-ds command to stop any DS servers running on your computer.

This lets the new server use ports another server was already using.

Get sample data
  1. Download the Example.ldif file, shown in the following listing:

    Show listing
    #
    # Copyright © 2020 - 2025 Ping Identity Corporation
    #
    # This code is to be used exclusively in connection with Ping Identity Corporation software or services.
    # Ping Identity Corporation only offers such software or services to legal entities
    # who have entered into a binding license agreement with Ping Identity Corporation.
    #
    dn: dc=example,dc=com
    objectClass: domain
    objectClass: top
    dc: example
    
    dn: ou=Groups,dc=example,dc=com
    objectClass: organizationalUnit
    objectClass: top
    ou: Groups
    
    dn: ou=Self Service,ou=Groups,dc=example,dc=com
    objectClass: organizationalUnit
    objectClass: top
    description: Groups that authenticated users can manage on their own
    ou: Self Service
    
    dn: ou=People,dc=example,dc=com
    objectClass: organizationalUnit
    objectClass: top
    description: Description on ou=People
    ou: People
    
    dn: uid=ACI Admin,ou=People,dc=example,dc=com
    objectClass: person
    objectClass: inetOrgPerson
    objectClass: organizationalPerson
    objectClass: top
    cn: ACI Admin
    givenName: ACI
    mail: aci-admin@example.com
    ou: People
    sn: Admin
    uid: ACI Admin
    userPassword: 5up35tr0ng
    
    dn: uid=bjensen,ou=People,dc=example,dc=com
    objectClass: person
    objectClass: inetOrgPerson
    objectClass: organizationalPerson
    objectClass: top
    cn: Babs Jensen
    givenName: Barbara
    mail: bjensen@example.com
    ou: People
    sn: Jensen
    uid: bjensen
    userPassword: 5up35tr0ng
  2. Save the file to your computer’s temporary directory, such as /tmp or C:\Temp .

Install server with secure settings
  1. Unzip the DS server .zip file into the folder where you want to install the server.

  2. Set up the directory server using the LDIF you downloaded.

    Set up the server without the evaluation setup profile, so the access control settings are secure by default. The default password policies require stronger passwords. The configuration grants very little access to regular users. Only uid=admin has access to the data:

    • Bash

    • PowerShell

    • Zsh

    $ /path/to/opendj/setup \
     --serverId learn-acis \
     --deploymentId $DEPLOYMENT_ID \
     --deploymentIdPassword password \
     --rootUserDn uid=admin \
     --rootUserPassword str0ngAdm1nPa55word \
     --hostname localhost \
     --ldapPort 1389 \
     --ldapsPort 1636 \
     --httpsPort 8443 \
     --adminConnectorPort 4444 \
     --acceptLicense
    $ dsconfig \
     create-backend \
     --backend-name exampleData \
     --type je \
     --set enabled:true \
     --set base-dn:dc=example,dc=com \
     --offline \
     --no-prompt
    $ import-ldif \
     --backendId exampleData \
     --ldifFile /tmp/Example.ldif \
     --offline
    $ start-ds --quiet
    C:\path\to\opendj\setup.bat `
     --serverId learn-acis `
     --deploymentId <deployment-id> `
     --deploymentIdPassword password `
     --rootUserDn uid=admin `
     --rootUserPassword str0ngAdm1nPa55word `
     --hostname localhost `
     --ldapPort 1389 `
     --ldapsPort 1636 `
     --httpsPort 8443 `
     --adminConnectorPort 4444 `
     --acceptLicense
    C:\path\to\opendj\bat\dsconfig.bat `
     create-backend `
     --backend-name exampleData `
     --type je `
     --set enabled:true `
     --set base-dn:dc=example,dc=com `
     --offline `
     --no-prompt
    C:\path\to\opendj\bat\import-ldif.bat `
     --backendId exampleData `
     --ldifFile C:\Temp\Example.ldif `
     --offline
    C:\path\to\opendj\bat\start-ds.bat --quiet
    $ /path/to/opendj/setup \
     --serverId learn-acis \
     --deploymentId $DEPLOYMENT_ID \
     --deploymentIdPassword password \
     --rootUserDn uid=admin \
     --rootUserPassword str0ngAdm1nPa55word \
     --hostname localhost \
     --ldapPort 1389 \
     --ldapsPort 1636 \
     --httpsPort 8443 \
     --adminConnectorPort 4444 \
     --acceptLicense
    $ dsconfig \
     create-backend \
     --backend-name exampleData \
     --type je \
     --set enabled:true \
     --set base-dn:dc=example,dc=com \
     --offline \
     --no-prompt
    $ import-ldif \
     --backendId exampleData \
     --ldifFile /tmp/Example.ldif \
     --offline
    $ start-ds --quiet
Grant ACI admin access

Grant the ACI Admin user access to modify ACIs:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=admin \
 --bindPassword str0ngAdm1nPa55word << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "aci") (version 3.0;acl "ACI Admin can manage ACI attributes";
 allow (write) userdn = "ldap:///uid=ACI Admin,ou=People,dc=example,dc=com";)

dn: uid=ACI Admin,ou=People,dc=example,dc=com
changetype: modify
add: ds-privilege-name
ds-privilege-name: modify-acl
EOF
New-Item -Path . -Name "aci-admin.ldif" -ItemType "file" -Value @"
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "aci") (version 3.0;acl "ACI Admin can manage ACI attributes";
 allow (write) userdn = "ldap:///uid=ACI Admin,ou=People,dc=example,dc=com";)

dn: uid=ACI Admin,ou=People,dc=example,dc=com
changetype: modify
add: ds-privilege-name
ds-privilege-name: modify-acl
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn uid=admin `
 --bindPassword str0ngAdm1nPa55word `
 aci-admin.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn uid=admin \
 --bindPassword str0ngAdm1nPa55word << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "aci") (version 3.0;acl "ACI Admin can manage ACI attributes";
 allow (write) userdn = "ldap:///uid=ACI Admin,ou=People,dc=example,dc=com";)

dn: uid=ACI Admin,ou=People,dc=example,dc=com
changetype: modify
add: ds-privilege-name
ds-privilege-name: modify-acl
EOF
(Optional) Try LDAP examples

Try examples from Learn LDAP.

Babs Jensen does not have the access she had with the evaluation setup profile. For production servers, the best practice is to grant access only when required.

Example ACIs

Prepare to use the examples before trying them. The ACI Admin account must have access to manage ACIs. After you add an example ACI, test users' access. For inspiration, refer to the examples in Learn LDAP.

ACI syntax is powerful and sometimes challenging to get right. For details, refer to Access control.

ACI: access own entry

The following example grants authenticated users access to read their own entry, and modify some attributes:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0;acl "Users can read their entries";
 allow (read, search, compare) (userdn = "ldap:///self");)
-
add: aci
aci: (targetattr = "authPassword || description || displayName || homePhone ||
 jpegPhoto || preferredLanguage || userPassword")
 (version 3.0;acl "Self-service modifications for basic attributes";
 allow (write) (userdn = "ldap:///self");)
EOF
New-Item -Path . -Name "self-access.ldif" -ItemType "file" -Value @"
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0;acl "Users can read their entries";
 allow (read, search, compare) (userdn = "ldap:///self");)
-
add: aci
aci: (targetattr = "authPassword || description || displayName || homePhone ||
 jpegPhoto || preferredLanguage || userPassword")
 (version 3.0;acl "Self-service modifications for basic attributes";
 allow (write) (userdn = "ldap:///self");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 self-access.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "*") (version 3.0;acl "Users can read their entries";
 allow (read, search, compare) (userdn = "ldap:///self");)
-
add: aci
aci: (targetattr = "authPassword || description || displayName || homePhone ||
 jpegPhoto || preferredLanguage || userPassword")
 (version 3.0;acl "Self-service modifications for basic attributes";
 allow (write) (userdn = "ldap:///self");)
EOF

In this example, the list of attributes users can read includes all user attributes. The list users can modify is limited. Other applications may manage other attributes; for example, a user’s manager could require a change through an HR system.

ACI: access subSchemaSubEntry attribute

The subSchemaSubEntry attribute indicates the entry with the LDAP schema definitions for the current entry. Many applications retrieve this attribute and the associated schema to properly display or validate attribute values.

The following example demonstrates how to grant access to read this attribute on directory entries:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "subSchemaSubEntry")
 (version 3.0;acl "Authenticated users can read subSchemaSubEntry";
 allow (read, search, compare) (userdn = "ldap:///all");)
EOF
New-Item -Path . -Name "subSchemaSubentry-access.ldif" -ItemType "file" -Value @"
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "subSchemaSubEntry")
 (version 3.0;acl "Authenticated users can read subSchemaSubEntry";
 allow (read, search, compare) (userdn = "ldap:///all");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 subSchemaSubentry-access.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "subSchemaSubEntry")
 (version 3.0;acl "Authenticated users can read subSchemaSubEntry";
 allow (read, search, compare) (userdn = "ldap:///all");)
EOF

ACI: manage group membership

For some static groups, you might choose to let users manage their own memberships. The following example lets members of self-service groups manage their own membership:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: ou=Self Service,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "member") (version 3.0;acl "Self registration";
 allow (selfwrite) (userdn = "ldap:///uid=*,ou=People,dc=example,dc=com");)
EOF
New-Item -Path . -Name "self-service-groups.ldif" -ItemType "file" -Value @"
dn: ou=Self Service,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "member") (version 3.0;acl "Self registration";
 allow (selfwrite) (userdn = "ldap:///uid=*,ou=People,dc=example,dc=com");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 self-service-groups.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: ou=Self Service,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "member") (version 3.0;acl "Self registration";
 allow (selfwrite) (userdn = "ldap:///uid=*,ou=People,dc=example,dc=com");)
EOF

The selfwrite permission is for adding or deleting one’s own DN from a group.

ACI: manage self-service groups

This example lets users create and delete self-managed groups:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: ou=Self Service,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targattrfilters="add=objectClass:(objectClass=groupOfNames)")
 (version 3.0; acl "Users can create self-service groups";
 allow (add) (userdn = "ldap:///uid=*,ou=People,dc=example,dc=com");)
-
add: aci
aci: (version 3.0; acl "Owner can delete self-service groups";
 allow (delete) (userattr = "owner#USERDN");)
EOF
New-Item -Path . -Name "self-managed-groups.ldif" -ItemType "file" -Value @"
dn: ou=Self Service,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targattrfilters="add=objectClass:(objectClass=groupOfNames)")
 (version 3.0; acl "Users can create self-service groups";
 allow (add) (userdn = "ldap:///uid=*,ou=People,dc=example,dc=com");)
-
add: aci
aci: (version 3.0; acl "Owner can delete self-service groups";
 allow (delete) (userattr = "owner#USERDN");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 self-managed-groups.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: ou=Self Service,ou=Groups,dc=example,dc=com
changetype: modify
add: aci
aci: (targattrfilters="add=objectClass:(objectClass=groupOfNames)")
 (version 3.0; acl "Users can create self-service groups";
 allow (add) (userdn = "ldap:///uid=*,ou=People,dc=example,dc=com");)
-
add: aci
aci: (version 3.0; acl "Owner can delete self-service groups";
 allow (delete) (userattr = "owner#USERDN");)
EOF

ACI: full access

The following ACI grants Babs Jensen permission to perform all LDAP operations, allowing her full administrator access to the directory data under dc=example,dc=com. Babs can read and write directory data, rename and move entries, and use proxied authorization. Some operations also require administrative privileges not shown in this example:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "* || +") (version 3.0;acl "Babs has full access";
 allow (all, export, import, proxy) (userdn = "ldap:///uid=bjensen,ou=People,dc=example,dc=com");)
EOF
New-Item -Path . -Name "full-access.ldif" -ItemType "file" -Value @"
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "* || +") (version 3.0;acl "Babs has full access";
 allow (all, export, import, proxy) (userdn = "ldap:///uid=bjensen,ou=People,dc=example,dc=com");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 full-access.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "* || +") (version 3.0;acl "Babs has full access";
 allow (all, export, import, proxy) (userdn = "ldap:///uid=bjensen,ou=People,dc=example,dc=com");)
EOF

(targetattr = "* || +") permits access to all user attributes and all operational attributes. allow (all, import, export, proxy) permits all user operations, modify DN operations, and proxied authorization. Notice all doesn’t allow modify DN or proxied authorization.

ACI: anonymous reads and searches

In LDAP, an anonymous user is one who does not provide bind credentials. By default, most setup profiles only allow anonymous access to read information about the server’s capabilities or before using the StartTLS operation to get a secure connection before providing credentials.

Unless you set up the server with the evaluation profile, anonymous users cannot read application data by default. You can grant them access, however. First, change the global configuration to allow unauthenticated requests. Second, add an ACI to grant access to the entries.

The following command changes the global configuration property, unauthenticated-requests-policy, to allow unauthenticated requests:

  • Bash

  • PowerShell

  • Zsh

$ dsconfig \
 set-global-configuration-prop \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword str0ngAdm1nPa55word \
 --set unauthenticated-requests-policy:allow \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt
dsconfig.bat `
 set-global-configuration-prop `
 --hostname localhost `
 --port 4444 `
 --bindDN uid=admin `
 --bindPassword str0ngAdm1nPa55word `
 --set unauthenticated-requests-policy:allow `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --no-prompt
$ dsconfig \
 set-global-configuration-prop \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword str0ngAdm1nPa55word \
 --set unauthenticated-requests-policy:allow \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt

This ACI makes all user attributes in dc=example,dc=com data (except passwords) world-readable:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr != "authPassword || userPassword") (version 3.0;acl "Anonymous read-search access";
 allow (read, search, compare) (userdn = "ldap:///anyone");)
EOF
New-Item -Path . -Name "anon-access.ldif" -ItemType "file" -Value @"
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr != "authPassword || userPassword") (version 3.0;acl "Anonymous read-search access";
 allow (read, search, compare) (userdn = "ldap:///anyone");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 anon-access.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr != "authPassword || userPassword") (version 3.0;acl "Anonymous read-search access";
 allow (read, search, compare) (userdn = "ldap:///anyone");)
EOF

Notice ldap:///anyone designates anonymous users and authenticated users. Do not confuse it with ldap:///all, which designates authenticated users only.

ACI: permit insecure access over loopback only

This ACI uses IP address and Security Strength Factor subjects to prevent insecure remote access to dc=example,dc=com data. In most cases, you explicitly grant permission with allow, making it easier to understand and to explain why the server permits a given operation. This demonstrates one use case where it makes sense to deny permission:

  • Bash

  • PowerShell

  • Zsh

$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "* || +") (version 3.0;acl "Restrict insecure LDAP to the loopback address";
 deny (all) (ip != "127.0.0.1" and ssf <= "1");)
EOF
New-Item -Path . -Name "deny-cleartext.ldif" -ItemType "file" -Value @"
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "* || +") (version 3.0;acl "Restrict cleartext LDAP to the loopback address";
 deny (all) (ip != "127.0.0.1" and ssf <= "1");)
"@
ldapmodify.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" `
 --bindPassword 5up35tr0ng `
 deny-cleartext.ldif
$ ldapmodify \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDn "uid=ACI Admin,ou=People,dc=example,dc=com" \
 --bindPassword 5up35tr0ng << EOF
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "* || +") (version 3.0;acl "Restrict insecure LDAP to the loopback address";
 deny (all) (ip != "127.0.0.1" and ssf <= "1");)
EOF
  • ssf = 1 means TLS is configured without a cipher.

    The server verifies integrity using packet checksums, but all content is sent in plain text.

  • ssf = 0 means the content is sent plain text with no connection security.

About directories

A directory resembles a dictionary. If you know a word, you can look up its entry in the dictionary to learn its definition or its pronunciation. If you are bored, curious, or have lots of time, you can also read through the dictionary or the directory.

Where a directory differs from a paper dictionary is in how entries are indexed. Dictionaries typically have only one method of indexation: alphabetical order. In contrast, directories index multiple attributes of their entries. They have indexes names, user identifiers, email addresses, and telephone numbers. You can look up a directory entry by any of these attributes.

PingDS implements the Lightweight Directory Access Protocol (LDAP). Nearly all of what follows is an introduction to LDAP.

PingDS also provides HTTP access to directory data. It helps to understand the underlying LDAP model whether you use HTTP or LDAP.

LDAP history

Phone companies have been managing directories for many decades. The internet itself has relied on distributed directory services like DNS since the mid-1980s.

In the late 1980s, experts from what is now the International Telecommunications Union published the X.500 set of international standards, including the Directory Access Protocol (DAP). The X.500 standards specify Open Systems Interconnect (OSI) protocols and data definitions for general purpose directory services. The X.500 standards were designed to meet the needs of systems built according to the X.400 standards, covering electronic mail services.

LDAP was developed in the early 1990s. LDAP was developed as an alternative for directory access over internet protocols (TCP/IP) rather than OSI protocols. TCP/IP is lightweight enough for desktop implementations. By the mid-1990s, LDAP directory servers were widely used.

LDAP directory servers replicate data. If one server goes down, lookups can continue on other servers. Until the late 1990s, LDAP servers were designed primarily for fast, highly available lookups. If the service needs to support more lookups, you add another replicated directory server.

As organizations rolled out bigger directories for more applications, they also needed fast, highly available updates. Around the year 2000, directories began to support replication with multiple read-write servers. After an update on one server, the service replays it on other peer servers. Adding more servers doesn’t make updates faster because each server must replay each update. The organizations with the very largest directories had trouble replicating all the changes fast enough.

The DS code base began in the mid-2000s when engineers decided the cost of adapting the existing C-based directory technology for high-performance updates would be higher than the cost of building new, high-performance directory using Java technology.

LDAP data

LDAP directory data is organized into entries, similar to the entries for words in the dictionary. LDAP entries usually hold identity data:

dn: uid=bjensen,ou=People,dc=example,dc=com
uid: bjensen
cn: Babs Jensen
cn: Barbara Jensen
facsimileTelephoneNumber: +1 408 555 1992
gidNumber: 1000
givenName: Barbara
homeDirectory: /home/bjensen
l: San Francisco
mail: bjensen@example.com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: posixAccount
objectClass: top
ou: People
ou: Product Development
roomNumber: 0209
sn: Jensen
telephoneNumber: +1 408 555 1862
uidNumber: 1076

Barbara Jensen’s entry has a number of attributes, such as uid: bjensen, telephoneNumber: +1 408 555 1862, and objectClass: posixAccount. (The objectClass attribute type indicates the required and optional attributes for the entry. You can update object classes online and change the definitions of object classes and attributes. Unlike many databases, directories let you extend the schema for the data while they’re running.) When you look up Babs’s entry in the directory, you specify one or more attributes and values to match. The directory server finds matching entries using one more of its indexes, returning them as it finds them.

Attribute values are not necessarily strings. Some attribute values, like certificates and photos, are binary.

Each entry has a unique identifier. The previous example shows the identifier at the top of the entry, dn: uid=bjensen,ou=People,dc=example,dc=com. DN is an acronym for Distinguished Name. No two entries in the directory have the same DN.

You must escape some characters when using them in DNs. The following example shows an entry with escaped characters in the DN:

  • Bash

  • PowerShell

$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=kvaughan,ou=People,dc=example,dc=com \
 --bindPassword bribery \
 --baseDN dc=example,dc=com \
 "(uid=escape)"
Show output
dn: cn=DN Escape Characters \" \# \+ \, \; \< = \> \\,dc=example,dc=com
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: top
givenName: DN Escape Characters
uid: escape
cn: DN Escape Characters " # + , ; < = > \
sn: " # + , ; < = > \
mail: escape@example.com
ldapsearch.bat `
 --hostname localhost `
 --port 1636 `
 --useSsl `
 --usePkcs12TrustStore C:\path\to\opendj\config\keystore `
 --trustStorePassword:file C:\path\to\opendj\config\keystore.pin `
 --bindDN uid=kvaughan,ou=People,dc=example,dc=com `
 --bindPassword bribery `
 --baseDN dc=example,dc=com `
 "(uid=escape)"
Show output
dn: cn=DN Escape Characters \" \# \+ \, \; \< = \> \\,dc=example,dc=com
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: top
givenName: DN Escape Characters
uid: escape
cn: DN Escape Characters " # + , ; < = > \
sn: " # + , ; < = > \
mail: escape@example.com

LDAP entries are arranged hierarchically in the directory. The hierarchical organization resembles a file system on a PC or a web server. Some illustrations picture this as an upside down tree structure or a pyramid. The DN consists of components separated by commas, uid=bjensen,ou=People,dc=example,dc=com. The names are little-endian. The components reflect the hierarchy of directory entries.

Hierarchical directory data view

Barbara Jensen’s entry is located under an entry with DN ou=People,dc=example,dc=com. This is the organizational unit (OU) and parent entry for the people at Example.com. The ou=People entry is under dc=example,dc=com, the base entry for Example.com. DC is an acronym for Domain Component. The directory has other base entries, such as cn=config for the server configuration.

A directory can serve multiple organizations, too. You might find dc=example,dc=com, dc=mycompany,dc=com, and o=myOrganization in the same LDAP directory. When you look up entries, you specify the base DN to look under in the same way you need to know whether to look in an English or a French dictionary for a given word.

The root entry for the directory, technically the entry with DN "" (the empty string), is called the root DSE. It contains information about what the server supports and the base DNs it serves.

A directory server stores two kinds of attributes in a directory entry: user attributes and operational attributes. User attributes hold the information for users of the directory. All attributes shown in the entry above are user attributes. Operational attributes hold information used by the directory itself. Examples of operational attributes include entryUUID, modifyTimestamp, and subschemaSubentry.

When an LDAP search operation finds an entry in the directory, the directory server returns all the visible user attributes unless the search request restricts the list of attributes by specifying those attributes explicitly. The directory server does not return any operational attributes unless the search request specifically asks for them.

Generally speaking, applications should change only user attributes and leave updates of operational attributes to the server. Use the public server interfaces and commands to change server behavior. An exception is access control instruction (aci) attributes. ACIs are operational attributes that control access to directory data.

Communication

Most HTTP applications connect to the server for each request and close the connection after the response comes.

LDAP has a different model. In LDAP, the client application connects to the server and authenticates. The client then requests any number of operations, perhaps processing results in between requests. The client finally disconnects when done, potentially days later.

The standard operations are as follows:

Bind (authenticate)

The first operation in an LDAP session usually involves the client binding to the LDAP server with the server authenticating the client.

Authentication identifies the client’s identity in LDAP terms, the identity the server uses to authorize access to directory data the client wants to read or change.

If the client does not bind explicitly, the server treats the client as an anonymous client. What the server lets anonymous users do depends on access control and configuration settings.

The client can bind again (rebind) on the same connection.

Search (lookup)

After binding, the client can look up entries under a specified base DN matching an LDAP filter. For example, to look up people with the email address bjensen@example.com at Example.com, you specify ou=People,dc=example,dc=com as the base DN and (mail=bjensen@example.com) as the LDAP filter.

Compare

After binding, the client can compare a specified attribute value with the value stored on an entry in the directory.

Modify

After binding, the client can change one or more attribute values on an entry. Administrators usually restrict the attributes client applications can change.

Add

After binding, the client can add one or more new LDAP entries to the server assuming it has access to do so.

Delete

After binding, the client can delete one or more entries assuming it has access to do so. To delete an entry with other entries underneath, first delete the children, then the parent.

Modify DN

After binding, the client can change an entry DN assuming it has access to do so. This renames the entry or moves it to another location.

For example, if Barbara changes her unique identifier from bjensen to something else, her DN would have to change. For another example, if you decide to consolidate ou=Customers and ou=Employees under ou=People instead, all the entries underneath must change distinguished names.

Renaming entire branches of entries can be a major operation for the directory, so avoid moving entire branches if you can.

Unbind

When done making requests, the client can request an unbind operation to end the LDAP session.

Abandon

When a request takes too long to complete or when a search request returns too many entries, the client can send an abandon request to drop the operation in progress.

The server doesn’t have to send a response.

Controls and extensions

LDAP has standardized two mechanisms for extending what directory servers can do beyond the basic operations:

  • LDAP controls

  • LDAP extended operations

LDAP controls are information added to an LDAP message to specify how an LDAP operation should be processed:

  • The server-side sort request control tells the server to return search results in sorted order.

  • The subtree delete request control tells the server to remove child entries with the target entry.

  • The persistent search control lets the client application get change notifications with an ongoing server. Whenever changes happen in the scope of the search, the server sends additional search results. Persistent searches are "permanent" though they can be idle for long periods of time.

  • A directory server can send response controls when the response contains special information. Examples include responses for entry change notification, password policy, and paged results.

For the list of supported LDAP controls, refer to Supported LDAP controls.

LDAP extended operations are additional LDAP operations not included in the original standard list:

  • The cancel extended operation works like an abandon operation with a response from the server.

  • The StartTLS extended operation lets you connect on an unsecure port and start Transport Layer Security (TLS) negotiations to protect communications.

For the list of supported LDAP extended operations, refer to Supported LDAP extended operations.

Indexes

Directories have indexes for multiple attributes. By default, DS does not let normal users perform unindexed searches because servers have to scan an entire directory database when looking for matches.

As directory administrator, you make sure directory data is properly indexed. DS software provides tools for building and managing indexes. For details, refer to Indexes.

Schema

Some databases hold huge amounts of data per application in an application-specific layout. Although such databases can support multiple applications, data organization depends on the applications served.

In contrast, directories are designed for shared, centralized services. The shared, centralized nature of directory services fosters interoperability in practice. It has helped directory services be successful in the long term.

LDAP schemas make the shared model of directory user information possible. LDAP schemas define the data the directory can contain. Directory entries are not arbitrary objects. Their attributes are completely predictable from publicly readable definitions. Many schema definitions are literally standard and defined by RFCs. They are the same not just across a directory service but across different directory services.

Unlike some databases, a directory service can also LDAP schema updates over LDAP while it is running. This gives you great flexibility in adapting the directory to store new data without changing existing data and without stopping the directory service.

For a closer look, refer to LDAP schema.

Access control

Directory services support fine-grained access control.

The directory administrator controls who can access specific data and when, how, where, and under what conditions they can do so with access control instructions (ACIs); for example, ACIs let you:

  • Permit only specific directory operations.

  • Scope controls to apply to the whole directory or to a single entry.

  • Specify network conditions and the encryption strength required for an operation.

ACIs are stored on entries in the directory, so you can update access controls while the service is running. You can delegate control over ACIs to client applications. DS software combines ACIs and separate administrative privileges to secure access to directory data.

For more information, read Access control.

Replication

DS replication consists of copying each update to the directory service to multiple directory servers. This brings both redundancy, in the case of network partitions or of crashes, and scalability for read operations. Most directory deployments involve multiple servers replicating together.

Automated conflict resolution

With writable replicated servers, replication conflicts can arise; for example, two applications write different values to the same attribute on the same entry on the two replicas.

In nearly all cases, DS replication can resolve these situations automatically. This makes the directory service resilient and safe even in the unpredictable real world.

Scaling up

It’s easier to scale a directory service for read operations than for write operations.

To add capacity for read operations, add replicated servers.

Adding servers doesn’t scale up write operations because each write operation must be replayed everywhere. If you have N servers, you have N updates to replay.

Eventual consistency

Replication is eventually consistent.

When directory data changes on one server, it eventually converges to be the same everywhere. The data isn’t necessarily the same everywhere at any particular time, depending on the rate of changes.

Client applications sometimes get this wrong. They write to a pool of load balanced directory servers, immediately read back what they wrote, and can’t tolerate the possible differences. If application users complain about consistency, try mitigating poor application practices with a directory proxy.

HTTP access

DS software maps LDAP data as JSON resources over HTTP for REST clients (HDAP).

LDAP schemas define the HDAP data model:

  • LDAP entries hold sets of attributes, not arbitrarily nested objects.

    Each HDAP resource is an JSON object with fields at the top level:

    {
      "_id" : "dc=com/dc=example/ou=People/uid=bjensen",
      "_rev" : "<revision>",
      "mail" : [ "bjensen@example.com" ],
      "cn" : [ "Barbara Jensen", "Babs Jensen" ],
      "sn" : [ "Jensen" ]
    }
  • JSON has arrays, ordered collections that can contain duplicates.

    LDAP attributes are sets, unordered collections without duplicates.

    HDAP arrays have set semantics in which no duplicates are allowed, and the element order is arbitrary.

If you want a field with nested JSON or an array instead of a set, define a json syntax attribute. For details, refer to Schema and JSON.

You can deploy HDAP as a separate gateway servlet or through an HTTP connection handler on a DS server.

Deployment

When you have understood enough of the concepts to build directory services, prepare the service and test it thoroughly before you roll out shared, centralized services for your organization.

Start with Deployment when beginning your project.

Best practices

Follow these best practices for writing effective, maintainable, high-performance directory client applications.

Authenticate correctly

Unless your application performs only read operations, authenticate to the directory server. Some directory services require authentication to read directory data.

Once you authenticate (bind), directory servers make authorization decisions based on your identity. With servers that support proxied authorization, once authenticated, your application can request an operation on behalf of another identity, such as the identity of the end user.

Your application therefore should have an account, such as cn=My App,ou=Apps,dc=example,dc=com. The directory administrator can authorize appropriate access for your application’s account, and monitor your application’s requests to help you troubleshoot problems if they arise.

Applications can use simple, password-based authentication. When using password-based authentication, use secure connections to protect credentials over the network. For applications, prefer certificate-based authentication if possible.

Reuse connections

LDAP is a stateful protocol. You authenticate (bind), you perform operations, you unbind. The server maintains a context that lets it make authorization decisions concerning your requests. Therefore, reuse connections whenever possible.

Because LDAP supports asynchronous requests, it is normal and expected to make multiple requests over the same connection. Your application can share a pool of connections to avoid the overhead of setting them up and tearing them down.

Check connection health

In a network built for HTTP applications, your long-lived LDAP connections can get cut by network equipment configured to treat idle and old connections as stale resources to reclaim.

When you maintain a particularly long-lived connection, such as a connection for a persistent search, periodically perform a health check to maintain the connection operational.

A health check involves reading or writing an attribute on a well-known entry in your data. It can serve the purposes of maintaining the connection operational, and of verifying access to your data. A success result for a read indicates that the data is available, and the application can read it. A success result for a write indicates that the data is available, and the application can write to it. The exact check to perform depends on how your application uses the directory. Under some circumstances, your data might be temporarily read-only, for example.

When using a connection timeout, take care not to set the timeout so low that long operations, such as unindexed searches, fail to complete before the timeout.

Request exactly what you need all at once

By the time your application makes it to production, you should know what attributes you want. Request them explicitly, and request all the attributes in the same search.

For example, if you require mail and cn, then specify both attributes in your search request.

Use specific LDAP filters

The difference in results between a general filter (mail=*@example.com), and a good, specific filter like (mail=user@example.com) can be huge numbers of entries and enormous amounts of processing time, both for the directory server that has to return search results, and for your application that has to sort through them.

Many use cases can be handled with short, specific filters. As a rule, prefer equality filters over substring filters.

DS servers reject unindexed searches by default, because unindexed searches are resource-intensive. If your application needs to use a filter that results in an unindexed search, work with the directory administrator to find a solution, such as adding the indexes required for your search filters.

Always use & with ! to restrict the potential result set before returning all entries that do not match part of the filter. For example, (&(location=Oslo)(!(mail=birthday.girl@example.com))).

Make modifications specific

Specific modifications help directory servers apply and replicate your changes more effectively.

When you modify attributes with multiple values, such as a static group member attribute, replace or delete specific values individually, rather than replacing the entire list of values.

Trust result codes

Trust the LDAP result code from the directory server. For example, if you request a modification, and you get a success result, consider the operation a success. Do not immediately issue a search to get the modified entry.

LDAP replication model is loosely convergent. In other words, the directory server sends you the success result before replicating the change to every directory server replica across the network. If you issue a read immediately after a write, a load balancer may direct the request to another replica. The result might differ from what you expect.

The loosely convergent model means that the entry could have changed since you read it. If needed, use LDAP assertions to set conditions for your LDAP operations.

Handle input securely

When taking input directly from a user or another program, use appropriate methods to sanitize the data. Failure to sanitize the input data can leave your application vulnerable to injection attacks.

For Java applications, the PingDS format() methods for filters and DNs are similar to the Java String.format() methods. In addition to formatting the output, they escape the input objects. When building a search filter, use one of the methods of the DS APIs to escape input.

Check group membership on the account, not the group

Reading an entire large static group entry to check membership is wasteful.

If you need to determine which groups an account belongs to, request the DS virtual attribute, isMemberOf, when you read the account entry. Other directory servers use other names for this attribute that identifies the groups to an account belongs to.

Check support for features you use

Directory servers expose their capabilities as operational attribute values on the root DSE, which is the entry whose DN is an empty string, "".

This lets your application discover capabilities at run time, rather than storing configuration separately. Putting effort into checking directory capabilities makes your application easier to deploy and to maintain.

For example, rather than hard-coding dc=example,dc=com as a base DN in your configuration, read the root DSE namingContexts attribute.

Directory servers also expose their schema over LDAP. The root DSE attribute subschemaSubentry shows the DN of the entry for LDAP schema definitions.

Store large attribute values by reference

To serve results quickly with high availability, directory servers cache content and replicate it everywhere. If you already store large attribute values elsewhere, such as photos or audio messages, keep only a reference to external content in a user’s account.

Take care with persistent search and server-side sorting

A persistent search lets your application receive updates from the server as they happen by keeping the connection open and forcing the server to check whether to return additional results any time it performs a modification in the scope of your search. Directory administrators therefore might hesitate to grant persistent search access to your application.

DS servers expose a change log to let you discover updates with less overhead. If you do have to use a persistent search instead, try to narrow the scope of your search.

DS servers support a resource-intensive, standard operation called server-side sorting. When your application requests a server-side sort, the directory server retrieves all matching entries, sorts the entries in memory, and returns the results. For result sets of any size, server-side sorting ties up server resources that could be used elsewhere. Alternatives include sorting the results after your application receives them, or working with the directory administrator to enable appropriate browsing (virtual list view) indexes for applications that must regularly page through long lists of search results.

Reuse schemas where possible

DS servers come with schema definitions for a wide range of standard object classes and attribute types. Directories use unique, IANA-registered object identifiers (OIDs) to avoid object class and attribute type name clashes. The overall goal is Internet-wide interoperability.

Therefore, reuse schema definitions that already exist whenever you reasonably can. Reuse them as is. Do not try to redefine existing schema definitions.

If you must add schema definitions for your application, extend existing object classes with AUXILIARY classes. Take care to name your schemas such that they do not clash with other names.

When you have defined schema required for your application, work with the directory administrator to add your definitions to the directory service. DS servers let directory administrators update schema definitions over LDAP. There is no need to interrupt the service to add your application. Directory administrators can, however, have other reasons why they hesitate to add your schema definitions. Coming to the discussion prepared with good schema definitions, explanations of why they should be added, and evident regard for interoperability makes it easier for the directory administrator to grant your request.

Read directory server schemas during initialization

By default, PingDS APIs use a minimal, built-in core schema, rather than reading the schema from the server. Doing so automatically would incur a significant performance cost. Unless schemas change, your application only needs to read them once.

When you start your application, read directory server schemas as a one-off initialization step.

Once you have the directory server schema definitions, use them to validate entries.

Handle referrals

When a directory server returns a search result, the result is not necessarily an entry. If the result is a referral, then your application should follow up with an additional search based on the URIs provided in the result.

Troubleshooting: check result codes

LDAP result codes are standard, and listed in LDAP result codes.

When your application receives a result, it must rely on the result code value to determine what action to take. When the result is not what you expect, read or at least log the additional message information.

Troubleshooting: check server logs

If you can read the directory server access log, then check what the server did with your application’s request. The following excerpt shows a successful search by cn=My App,ou=Apps,dc=example,dc=com:

Show excerpt
{"eventName":"DJ-LDAP","client":{"ip":"<clientIp>","port":12345},"server":{"ip":"<serverIp>","port":1636},"request":{"protocol":"LDAPS","operation":"CONNECT","connId":4},"transactionId":"0","response":{"status":"SUCCESSFUL","statusCode":"0","elapsedTime":0,"elapsedTimeUnits":"MILLISECONDS"},"timestamp":"<timestamp>","_id":"<uuid>"}
{"eventName":"DJ-LDAP","client":{"ip":"<clientIp>","port":12345},"server":{"ip":"<serverIp>","port":1636},"request":{"protocol":"LDAPS","operation":"TLS","connId":4},"transactionId":"0","response":{"status":"SUCCESSFUL","statusCode":"0","elapsedTime":0,"elapsedTimeUnits":"MILLISECONDS"},"security":{"protocol":"TLSv1.3","cipher":"TLS_AES_128_GCM_SHA256","ssf":128},"timestamp":"<timestamp>","_id":"<uuid>"}
{"eventName":"DJ-LDAP","client":{"ip":"<clientIp>","port":12345},"server":{"ip":"<serverIp>","port":1636},"request":{"protocol":"LDAPS","operation":"BIND","connId":4,"msgId":1,"version":"3","dn":"cn=My App,ou=Apps,dc=example,dc=com","authType":"SIMPLE"},"transactionId":"<uuid>","response":{"status":"SUCCESSFUL","statusCode":"0","elapsedTime":1,"elapsedQueueingTime":0,"elapsedProcessingTime":1,"elapsedTimeUnits":"MILLISECONDS","additionalItems":{"ssf":128}},"userId":"cn=My App,ou=Apps,dc=example,dc=com","timestamp":"<timestamp>","_id":"<uuid>"}
{"eventName":"DJ-LDAP","client":{"ip":"<clientIp>","port":12345},"server":{"ip":"<serverIp>","port":1636},"request":{"protocol":"LDAPS","operation":"SEARCH","connId":4,"msgId":2,"dn":"dc=example,dc=com","scope":"sub","filter":"(uid=kvaughan)","attrs":["isMemberOf"]},"transactionId":"<uuid>","response":{"status":"SUCCESSFUL","statusCode":"0","elapsedTime":3,"elapsedQueueingTime":0,"elapsedProcessingTime":3,"elapsedTimeUnits":"MILLISECONDS","nentries":1,"entrySize":430},"userId":"cn=My App,ou=Apps,dc=example,dc=com","timestamp":"<timestamp>","_id":"<uuid>"}
{"eventName":"DJ-LDAP","client":{"ip":"<clientIp>","port":12345},"server":{"ip":"<serverIp>","port":1636},"request":{"protocol":"LDAPS","operation":"UNBIND","connId":4,"msgId":3},"transactionId":"<uuid>","timestamp":"<timestamp>","_id":"<uuid>"}
{"eventName":"DJ-LDAP","client":{"ip":"<clientIp>","port":12345},"server":{"ip":"<serverIp>","port":1636},"request":{"protocol":"LDAPS","operation":"DISCONNECT","connId":4},"transactionId":"0","response":{"status":"SUCCESSFUL","statusCode":"0","elapsedTime":0,"elapsedTimeUnits":"MILLISECONDS","reason":"Client Unbind"},"timestamp":"<timestamp>","_id":"<uuid>"}

Notice these features of the messages:

  • The request operation types appear in upper case.

  • The messages track the client information and identify the specific sequence of operations with connection ID (connId) and message ID (msgID) numbers.

  • The elapsedTime for the response indicates the total time to complete the request. The elapsedQueueingTime is the time the request waited in the queue. The elapsedProcessingTime is the time actively processing the request.

  • A status code 0 corresponds to a successful result, as described in RFC 4511.

For details about the message format, refer to Access log format.

Troubleshooting: inspect network traffic

If result codes and server logs are not enough, many network tools can interpret HTTP and LDAP packets. Install the necessary keys to decrypt encrypted packet content.

Next steps

After you work through the examples in these pages, try the following suggestions:

Learn about replication

Data replication is sometimes called the "killer feature" of LDAP directories. Its strengths are in enabling very high availability for directory services even during network outages, and automatically resolving conflicts that can occur when the network is down, for example. LDAP directories have been improving and hardening replication features for decades.

Its weaknesses are that replication protocols have not been standardized for interoperability, and that unwary developers can misunderstand its property of eventual consistency if they are too used to the strong, immediate consistency of monolithic, transactional databases.

Replication necessarily involves multiple servers and additional configuration. You can learn more about it by reading Replication and the related pages.

Browse DS documentation

Category Topics Covered

DS features, fixes, and known issues

Implementing common use cases for directory services

Deploying PingDS in on-premises and cloud environments

Installing DS software

Upgrading DS software

Configuring DS servers after installation

Ensuring a PingDS deployment is secure

Day-to-day operations for maintaining DS servers

Configuring DS server logs

What to monitor when running DS servers, and where to look for metrics and other information

How to use LDAP features and command-line tools

How to configure and use DS REST APIs for HTTP access (HDAP)

The dsconfig subcommands and server configuration properties

Evolving LDAP SDK and server APIs, including common APIs

LDAP-specific features of DS software

All default LDAP schema, including monitoring attributes and object classes

DS server error log messages by category and ID

Tools bundled with DS software

Try third-party tools

LDAP is a standard protocol, and so you can use LDAP-compliant third-party tools to manage directory data:

Many software solutions include support for LDAP authentication and LDAP-based address books.

Ping Identity does not endorse or support third-party tools.

Use DS with AM

Use DS with IDM

Remove DS software

For details, refer to Uninstallation.

Glossary

Abandon operation

LDAP operation to stop processing of a request in progress, after which the server drops the connection without a reply to the client application.

Access control

Control to grant or to deny access to a resource.

Access control instruction (ACI)

Instruction added as a directory entry attribute for fine-grained control over what a given user or group member is authorized to do in terms of LDAP operations and access to user data.

ACIs are implemented independently from privileges, which apply to administrative operations.

Related: Privilege

Access control list (ACL)

An access control list connects a user or group of users to one or more security entitlements. For example, users in group sales are granted the entitlement read-only to some financial data.

Access log

Server log tracing the operations the server processes including timestamps, connection information, and information about the operation itself.

Account lockout

The act of making an account temporarily or permanently inactive after successive authentication failures.

Active user

A user that has the ability to authenticate and use the services, having valid credentials.

Add operation

LDAP operation to add a new entry or entries to the directory.

Anonymous

A user that does not need to authenticate, and is unknown to the system.

Anonymous bind

A bind operation using simple authentication with an empty DN and an empty password, allowing anonymous access such as reading public information.

Approximate index

Index is used to match values that "sound like" those provided in the filter.

Attribute

Properties of a directory entry, stored as one or more key-value pairs. Typical examples include the common name (cn) to store the user’s full name and variations of the name, user ID (uid) to store a unique identifier for the entry, and mail to store email addresses.

Attribute value assertion (AVA)

An attribute description and a matching rule assertion value for the attribute.

DS software uses AVAs in RDNs, and to determine whether an entry matches an assertion. For example, a search filter specifying the AVA uid=bjensen asserts that matching entries have a uid attribute value equal to bjensen.

Audit log

Type of access log that dumps changes in LDIF.

Authentication

The process of verifying who is requesting access to a resource; the act of confirming the identity of a principal.

Authorization

The process of determining whether access should be granted to an individual based on information about that individual; the act of determining whether to grant or to deny a principal access to a resource.

Backend

Repository that stores directory data. Different implementations with different capabilities exist.

Binary copy

Backup files from one replica are restored on another replica.

Bind operation

LDAP authentication operation to determine the client’s identity in LDAP terms, the identity which is later used by the server to authorize (or not) access to directory data that the client wants to lookup or change.

Branch

The distinguished name (DN) of a non-leaf entry in the Directory Information Tree (DIT), and that entry and all its subordinates taken together.

Some administrative operations allow you to include or exclude branches by specifying the DN of the branch.

Related: Suffix

Collective attribute

A standard mechanism for defining attributes that appear on all the entries in a particular subtree.

Compare operation

LDAP operation to compare a specified attribute value with the value stored on an entry in the directory.

Control

Information added to an LDAP message to further specify how an LDAP operation should be processed. DS supports many LDAP controls.

Change sequence number (CSN)

An opaque string uniquely identifying a single change to directory data. A CSN indicates exactly when a change occurred on which replica. An example CSN is 010f016df804edca0000008fevaluation-only.

DS replication uses CSNs to replay replicated operations consistently on all replicas. DS replicas record CSNs in historical data values for ds-sync-state and ds-sync-hist attributes.

When troubleshooting replication data consistency, it can be useful to interpret CSNs. Contact support for help.

Database cache

Memory space set aside to hold database content.

Delete operation

LDAP operation to remove an existing entry or entries from the directory.

Directory

A directory is a network service which lists participants in the network such as users, computers, printers, and groups. The directory provides a convenient, centralized, and robust mechanism for publishing and consuming information about network participants.

Directory hierarchy

A directory can be organized into a hierarchy in order to make it easier to browse or manage. Directory hierarchies normally represent something in the physical world, such as organizational hierarchies or physical locations.

For example, the top level of a directory may represent a company, the next level down divisions, the next level down departments, and down the hierarchy. Alternately, the top level may represent the world, the next level down countries, next states or provinces, and next cities.

Directory Information Tree (DIT)

A set of directory entries organized hierarchically in a tree structure, where the vertices are the entries, and the arcs between vertices define relationships between entries.

Directory object

A directory object is an item in a directory. Example objects include users, user groups, computers, and more. Objects may be organized into a hierarchy and contain identifying attributes.

Related: Entry

Directory proxy server

Server that forwards LDAP requests to remote directory servers. A standalone directory proxy server does not store user data.

Directory server

Server application for centralizing information about network participants. A highly available directory service consists of multiple directory servers configured to replicate directory data.

Related: Replication

Directory superuser

Directory account with privileges to do full administration of the DS server, including bypassing access control evaluation, changing access controls, and changing administrative privileges.

Related: Superuser

Distinguished name (DN)

Fully qualified name for a directory entry, such as uid=bjensen,ou=People,dc=example,dc=com, built by concatenating the entry RDN (uid=bjensen) with the DN of the parent entry (ou=People,dc=example,dc=com).

Domain

A replication domain consists of several directory servers sharing the same synchronized set of data.

The base DN of a replication domain specifies the base DN of the replicated data.

Dynamic group

Group that specifies members using LDAP URLs.

Entry

An entry is an object in the directory, defined by one of more object classes, and their related attributes.

Entry cache

Memory space set aside to hold frequently accessed, large entries, such as static groups.

Equality index

Index used to match values that correspond exactly (though generally without case sensitivity) to the value provided in the search filter.

Errors log

Server log tracing server events, error conditions, and warnings, categorized and identified by severity.

Etime

Elapsed time within the server to process a request, starting from the moment the decoded operation is available to be processed by a worker thread.

Export

Save directory data in an LDIF file.

Extended operation

Additional LDAP operation not included in the original standards. DS servers support several standard LDAP extended operations.

Extensible match index

Index for a matching rule other than approximate, equality, ordering, presence, substring or VLV, such as an index for generalized time.

External user

An individual that accesses company resources or services but is not working for the company. Typically, a customer or partner.

Filter

An LDAP search filter is an expression that the server uses to find entries that match a search request, such as (mail=*@example.com) to match all entries having an email address in the example.com domain.

Group

Entry identifying a set of members whose entries are also in the directory.

Generation ID

The initial state identifier for a replicated directory server base DN. It is a hash of the first 1000 entries of the base DN, computed when creating the backend, importing data from LDIF, or initializing replication.

Replication can only proceed between base DNs that have the same generation ID.

HDAP

Short for HTTP Directory Access Protocol.

HDAP is not a standard. HDAP is the name of the feature providing REST APIs and HTTP access to directory data. HDAP translates HTTP requests to LDAP requests and LDAP responses to HTTP responses.

HDAP gateway

Standalone HDAP web application.

Idle time limit

Defines how long DS allows idle connections to remain open.

Import

Read in and index directory data from an LDIF file.

Inactive user

An entry in the directory that once represented a user but which is now no longer able to be authenticated.

Index

Directory server backend feature to allow quick lookup of entries based on their attribute values.

Index entry limit

When the number of entries that an index key points to exceeds the index entry limit, DS stops maintaining the list of entries for that index key.

Internal user

An individual who works within the company either as an employee or as a contractor.

LDAP Data Interchange Format (LDIF)

Standard, portable, text-based representation of directory content.

Refer to RFC 2849.

LDAP URL

LDAP Uniform Resource Locator, such as ldaps://ds.example.com:636/dc=example,dc=com??sub?(uid=bjensen).

Refer to RFC 2255.

LDAPS

LDAP over SSL.

Lightweight Directory Access Protocol (LDAP)

A simple and standardized network protocol used by applications to connect to a directory, search for objects and add, edit or remove objects.

Refer to RFC 4510.

Matching rule

Defines rules for performing matching operations against assertion values. Matching rules are frequently associated with an attribute syntax, and are used to compare values according to that syntax.

For example, the distinguishedNameEqualityMatch matching rule can be used to determine whether two DNs are equal and can ignore unnecessary spaces around commas and equal signs, differences in capitalization in attribute names, and other discrepancies.

Modify DN operation

LDAP modification operation to request that the server change the distinguished name of an entry.

Modify operation

LDAP modification operation to request that the server change one or more attributes of an entry.

Naming context

Base DN under which client applications can look for user data.

Object class

Identifies entries that share certain characteristics. Most commonly, an entry’s object classes define the attributes that must and may be present on the entry.

Object classes are stored on entries as values of the objectClass attribute. Object classes are defined in the directory schema, and can be abstract (defining characteristics for other object classes to inherit), structural (defining the basic structure of an entry, one structural inheritance per entry), or auxiliary (for decorating entries already having a structural object class with other required and optional attributes).

Object identifier (OID)

String that uniquely identifies an object, such as 0.9.2342.19200300.100.1.1 for the user ID attribute or 1.3.6.1.4.1.1466.115.121.1.15 for DirectoryString syntax.

Operational attribute

An attribute that has a special (operational) meaning for the server, such as pwdPolicySubentry or modifyTimestamp.

Ordering index

Index used to match values for a filter that specifies a range.

Password policy

A set of rules regarding what sequence of characters constitutes an acceptable password. Acceptable passwords are generally those that would be too difficult for another user, or an automated program to guess and thereby defeat the password mechanism.

Password policies may require a minimum length, a mixture of different types of characters (lowercase, uppercase, digits, punctuation marks, and other characters), avoiding dictionary words or passwords based on the user’s name, and other attributes.

Password policies may also require that users not reuse old passwords and that users change their passwords regularly.

Password reset

Password change performed by a user other than the user who owns the entry.

Password storage scheme

Mechanism for encoding user passwords stored on directory entries. DS implements a number of password storage schemes.

Password validator

Mechanism for determining whether a proposed password is acceptable for use. DS implements a number of password validators.

Plugin

Java library with accompanying configuration that implements a feature through processing that is not essential to the core operation of DS servers.

As the name indicates, plugins can be plugged in to an installed server for immediate configuration and use without recompiling the server.

DS servers invoke plugins at specific points in the lifecycle of a client request. The DS configuration framework lets directory administrators manage plugins with the same tools used to manage the server.

Presence index

Index used to match the fact that an attribute is present on the entry, regardless of the value.

Principal

Entity that can be authenticated, such as a user, a device, or an application.

Privilege

Server configuration settings controlling access to administrative operations such as exporting and importing data, restarting the server, performing password reset, and changing the server configuration.

Privileges are implemented independently from access control instructions (ACI), which apply to LDAP operations and user data.

Referential integrity

Ensuring that group membership remains consistent following changes to member entries.

Referint log

Server log tracing referential integrity events, with entries similar to the errors log.

Referral

Reference to another directory location, which can be another directory server running elsewhere or another container on the same server, where the current operation can be processed.

Relative distinguished name (RDN)

Initial portion of a DN that distinguishes the entry from all other entries at the same level, such as uid=bjensen in uid=bjensen,ou=People,dc=example,dc=com.

Replica

Directory server this is configured to use replication.

Replication

Data synchronization that ensures all directory servers participating eventually share a consistent set of directory data.

Replication server

Server dedicated to transmitting replication messages. A standalone replication server does not store user data.

Root DSE

The directory entry with distinguished name "" (empty string), where DSE is an acronym for DSA-Specific Entry. DSA is an acronym for Directory Server Agent, a single directory server.

The root DSE serves to expose information over LDAP about what the directory server supports in terms of LDAP controls, auth password schemes, SASL mechanisms, LDAP protocol versions, naming contexts, features, LDAP extended operations, and other information.

Schema

LDAP schema defines the object classes, attributes types, attribute value syntaxes, matching rules and other constrains on entries held by the directory server.

Search filter

Refer to: Filter

Search operation

LDAP lookup operation where a client requests that the server return entries based on an LDAP filter, and a base DN under which to search.

Simple authentication

Bind operation performed with a user’s entry DN and user’s password.

Use simple authentication only if the network connection is secure.

Size limit

Sets the maximum number of entries returned for a search.

Static group

Group that enumerates member entries.

Subentry

An entry, such as a password policy entry, that resides with the user data but holds operational data, and is not visible in search results unless explicitly requested.

Substring index

Index used to match values specified with wildcards in the filter.

Suffix

The distinguished name (DN) of a root entry in the Directory Information Tree (DIT), and that entry and all its subordinates taken together as a single object of administrative tasks such as export, import, indexing, and replication.

Superuser

User with privileges to perform unconstrained administrative actions on DS server. This account is analogous to the Linux root and Windows Administrator accounts.

The conventional default superuser DN is uid=admin. You can create additional superuser accounts, each with different administrative privileges.

Superuser privileges include the following:

  • bypass-acl: The holder is not subject to access control.

  • privilege-change: The holder can edit administrative privileges.

  • proxied-auth: The holder can make requests on behalf of another user, including directory superusers.

Task

Mechanism to provide remote access to server administrative functions.

DS software supports tasks to back up and restore backends, to import and export LDIF files, and to stop and restart the server.

Time limit

Defines the maximum processing time DS devotes to a search operation.

Unbind operation

LDAP operation to release resources at the end of a session.

Unindexed search

Search operation for which no matching index is available.

If no indexes are applicable, then the directory server potentially has to go through all entries to look for candidate matches. For this reason, the unindexed-search privilege, which allows users to request searches for which no applicable index exists, is reserved for the directory manager by default.

User

An entry that represents an individual that can be authenticated through credentials contained or referenced by its attributes. A user may represent an internal user or an external user, and may be an active user or an inactive user.

User attribute

An attribute for storing user data on a directory entry such as mail or givenname.

Virtual attribute

An attribute with dynamically generated values that appear in entries but are not persistently stored in the backend.

Virtual directory

An application that exposes a consolidated view of multiple physical directories over an LDAP interface. Consumers of the directory information connect to the virtual directory’s LDAP service.

Behind the scenes, requests for information and updates to the directory are sent to one or more physical directories where the actual information resides. Virtual directories enable organizations to create a consolidated view of information that for legal or technical reasons cannot be consolidated into a single physical copy.

Virtual list view (VLV) index

Browsing index designed to help the directory server respond to client applications that need, for example, to browse through a long list of results a page at a time in a GUI.

Virtual static group

DS group that lets applications get dynamic groups represented as static groups.

X.500

A family of standardized protocols for accessing, browsing and maintaining a directory. X.500 is functionally similar to LDAP, but is generally considered to be more complex, and has consequently not been widely adopted.