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 theisMemberOf
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 tovirtual.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"
}
}