Directory Services 7.3.5

Virtual attributes

Virtual attributes augment directory entries with attribute values that the DS server computes or obtains dynamically. Virtual attribute values do not exist in persistent storage. They help to limit the amount of data that needs to be stored. They fit well in some use cases, such as determining the groups a users belongs to, or adding an ETag to an entry.

Important considerations

  • Do not index virtual attributes.

    Virtual attribute values are generated by the server when they are read. They are not designed to be stored in a persistent index. Since you do not index virtual attributes, searching on a virtual attribute can result in an unindexed search. Unindexed searches are resource-intensive for large directories. By default, a directory server lets only the directory superuser perform unindexed searches.

  • Avoid searches that use a simple filter with a virtual attribute.

    If you must use a virtual attribute in a search filter, use it in a complex search filter that first narrows the search by filtering on an indexed attribute. For example, the following filter first narrows the search based on the user’s ID before checking group membership. Make sure that the user performing the search has access to read isMemberOf in the results:

    (&(uid=user-id)(isMemberOf=group-dn))

If you must use the entryDN and isMemberOf virtual attributes in a filter, use a simple equality filter. The following example shows how to add access to read isMemberOf, and then run a search that returns the common names for members of a group:

$ 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: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="isMemberOf")(version 3.0; acl "See isMemberOf"; allow (read,search,compare)
  groupdn= "ldap:///cn=Directory Administrators,ou=Groups,dc=example,dc=com";)
EOF

$ 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 \
 "(isMemberOf=cn=Directory Administrators,ou=Groups,dc=example,dc=com)" \
 cn

dn: uid=hmiller,ou=People,dc=example,dc=com
cn: Harry Miller

dn: uid=kvaughan,ou=People,dc=example,dc=com
cn: Kirsten Vaughan

dn: uid=rdaugherty,ou=People,dc=example,dc=com
cn: Robert Daugherty

Virtual attributes are generally operational, and so, are returned only when explicitly requested. The searches in these examples are unindexed, are therefore performed with directory administrator credentials:

$ 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 dc=example,dc=com \
 "(dc=example)"

dn: dc=example,dc=com
dc: example
objectClass: domain
objectClass: top

$ 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 dc=example,dc=com \
 "(dc=example)" \
 numSubordinates

dn: dc=example,dc=com
numSubordinates: 12

Default virtual attributes

DS servers define the following virtual attributes by default:

entryDN

The DN of the entry.

entryUUID

String. Provides a universally unique identifier for the entry.

etag

String entity tag. Defined in RFC 2616. Useful when checking whether an entry has changed since it was retrieved.

hasSubordinates

Boolean. Indicates whether the entry has children.

numSubordinates

The number of child entries directly beneath the entry.

isMemberOf

DN. Groups the entry belongs to.

By default, the server generates isMemberOf on entries having one of the following object classes:

  • groupOfEntries

  • groupOfNames

  • groupOfUniqueNames

  • person

To generate isMemberOf on entries with other object classes, edit the filter property of the isMemberOf virtual attribute configuration.

member

DN. Generated for virtual static groups.

uniqueMember

DN. Generated for virtual static groups.

pwdPolicySubentry

DN of the password policy that applies to the entry.

Default global access control settings prevent normal users from accessing this operational attribute.

subschemaSubentry

DN. References the schema definitions.

collectiveAttributeSubentries

DNs of applicable collective attribute definitions.

governingStructureRule

DN. References the rule specifying the type of subordinates the entry can have.

structuralObjectClass

DN. References the structural object class for the entry.

Static virtual attributes

You can use the existing virtual attribute types to create your own virtual attributes. You can also use the user-defined type to create your own virtual attribute types. The virtual attribute is defined by the server configuration, which is not replicated:

$ dsconfig \
 create-virtual-attribute \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --name "Served By Description" \
 --type user-defined \
 --set enabled:true \
 --set attribute-type:description \
 --set base-dn:dc=example,dc=com \
 --set value:"Served by my favorite directory server" \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt

$ 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=wlutz)" \
 description

dn: uid=wlutz,ou=People,dc=example,dc=com
description: Description on ou=People
description: Served by my favorite directory server

Template-based virtual attributes

DS lets you create virtual attributes based on the values of non-virtual attributes. The following example overrides the mail attribute on user entries with a user-template virtual attribute:

$ dsconfig \
 create-virtual-attribute \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --name "Virtual email address" \
 --type user-template \
 --set enabled:true \
 --set attribute-type:mail \
 --set template:"{givenName}.{sn}@{domain|virtual.example.com}" \
 --set conflict-behavior:virtual-overrides-real \
 --set filter:"(objectClass=inetOrgPerson)" \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt

$ 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=bjensen)" \
 mail

dn: uid=bjensen,ou=People,dc=example,dc=com
mail: Barbara.Jensen@virtual.example.com

A template string, such as {givenName}.{sn}@{domain|virtual.example.com}, follows these rules:

  • It has zero or more {placeholder} values, where placeholder is an attribute name.

    DS replaces the {placeholder} with the value of the attribute.

    When a {placeholder} references a multi-valued attribute, DS generates one virtual attribute value for each of the real values.

    When a {placeholder} references an attribute that does not exist on the entry, DS replaces the {placeholder} with an empty string.

  • Template placeholders can have default values that DS uses when the attribute is missing.

    In this example, {domain|virtual.example.com} defaults to virtual.example.com.

  • To escape placeholder values, use a backslash: \{placeholder}.

    When adding a sequence of backslashes to a template string with the dsconfig command, use single quotes to prevent your shell from interpreting backslashes as escape characters. For example, to set the template to \\{placeholder}, use --set template:'\\{placeholder}'.

You can read a virtual LDAP attribute over REST as you would any other attribute:

$ curl \
--user bjensen:hifalutin \
--cacert ca-cert.pem \
--silent \
--get \
--data-urlencode "_fields=contactInformation/emailAddress" \
--data-urlencode "_prettyPrint=true" \
"https://localhost:8443/api/users/bjensen"

{
  "_id" : "bjensen",
  "_rev" : "<revision>",
  "contactInformation" : {
    "emailAddress" : "Barbara.Jensen@virtual.example.com"
  }
}