Collective Attributes
Collective attributes provide a standard mechanism for inheriting attribute values. DS servers support standard collective attributes, described in RFC 3671. The inherited values appear on all the entries in a subtree, optionally filtered by object class. Standard collective attribute type names have the prefix c-
.
DS servers extend collective attributes to make them easier to use. You can define any DS attribute as collective with the ;collective
attribute option. Use LDAP filters in the subtree specification for fine-grained control over which entries inherit the values.
You establish an inheritance relationship between entries by specifying one of the following:
Which attribute holds the DN of the entry to inherit attributes from.
How to construct the RDN of the entry to inherit attributes from.
This example defines attributes that depend on the user's service level.
This example shows collective attributes that depend on a classOfService
attribute value:
For entries with
classOfService: bronze
,mailQuota
is 1 GB, anddiskQuota
is 10 GB.For entries with
classOfService: silver
,mailQuota
is 5 GB, anddiskQuota
is 50 GB.For entries with
classOfService: gold
,mailQuota
is 10 GB, anddiskQuota
is 100 GB.
You define collective attributes in the user data using an LDAP subentry. As a result, collective attribute settings are replicated. Collective attributes use attributes defined in the directory schema. The following LDIF shows the schema definitions:
dn: cn=schema changetype: modify add: attributeTypes attributeTypes: ( example-class-of-service-attribute-type NAME 'classOfService' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE USAGE userApplications X-ORIGIN 'DS Documentation Examples' ) - add: attributeTypes attributeTypes: ( example-class-of-service-disk-quota NAME 'diskQuota' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications X-ORIGIN 'DS Documentation Examples' ) - add: attributeTypes attributeTypes: ( example-class-of-service-mail-quota NAME 'mailQuota' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications X-ORIGIN 'DS Documentation Examples' ) - add: objectClasses objectClasses: ( example-class-of-service-object-class NAME 'cos' SUP top AUXILIARY MAY ( classOfService $ diskQuota $ mailQuota ) X-ORIGIN 'DS Documentation Examples' )
The collective attribute definitions set the quotas depending on class of service:
dn: cn=Bronze Class of Service,dc=example,dc=com objectClass: collectiveAttributeSubentry objectClass: extensibleObject objectClass: subentry objectClass: top cn: Bronze Class of Service diskQuota;collective: 10 GB mailQuota;collective: 1 GB subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=bronze)" } dn: cn=Silver Class of Service,dc=example,dc=com objectClass: collectiveAttributeSubentry objectClass: extensibleObject objectClass: subentry objectClass: top cn: Silver Class of Service diskQuota;collective: 50 GB mailQuota;collective: 5 GB subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=silver)" } dn: cn=Gold Class of Service,dc=example,dc=com objectClass: collectiveAttributeSubentry objectClass: extensibleObject objectClass: subentry objectClass: top cn: Gold Class of Service diskQuota;collective: 100 GB mailQuota;collective: 10 GB subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=gold)" }
When the collective attributes are defined, you see the results on user entries. Before trying this example, set up a directory server with the ds-evaluation
profile:
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=bjensen)" \ classOfService mailQuota diskQuota
dn: uid=bjensen,ou=People,dc=example,dc=com classOfService: bronze mailQuota: 1 GB diskQuota: 10 GB
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=kvaughan)" \ classOfService mailQuota diskQuota
dn: uid=kvaughan,ou=People,dc=example,dc=com classOfService: silver mailQuota: 5 GB diskQuota: 50 GB
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=scarter)" \ classOfService mailQuota diskQuota
dn: uid=scarter,ou=People,dc=example,dc=com classOfService: gold mailQuota: 10 GB diskQuota: 100 GB
This example demonstrates how to set an employee's department number using the manager's department number.
The employee-manager relationship is defined by the employee's manager
attribute. Each manager
attribute specifies the DN of a manager's entry. The server looks up the manager's department number to set the attribute on the employee's entry.
The collective attribute subentry specifies that users inherit department number from their manager:
dn: cn=Inherit Department Number From Manager,dc=example,dc=com objectClass: top objectClass: subentry objectClass: inheritedCollectiveAttributeSubentry objectClass: inheritedFromDNCollectiveAttributeSubentry cn: Inherit Department Number From Manager subtreeSpecification: { base "ou=People", specificationFilter "(objectClass=posixAccount)" } inheritFromDNAttribute: manager inheritAttribute: departmentNumber
Babs Jensen's manager is Torrey Rigden:
dn: uid=bjensen,ou=People,dc=example,dc=com manager: uid=trigden, ou=People, dc=example,dc=com
Torrey's department number is 3001:
dn: uid=trigden,ou=People,dc=example,dc=com departmentNumber: 3001
Babs inherits her department number from Torrey. Before trying this example, set up a directory server with the ds-evaluation
profile:
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=bjensen)" \ departmentNumber
dn: uid=bjensen,ou=People,dc=example,dc=com departmentNumber: 3001
This example demonstrates how to set a user's default language preferences and street address based on locality.
For this example, the relationship between entries is based on locality. The collective attribute subentry specifies how to construct the RDN of the entry with the values to inherit:
dn: cn=Inherit From Locality,dc=example,dc=com objectClass: top objectClass: subentry objectClass: inheritedCollectiveAttributeSubentry objectClass: inheritedFromRDNCollectiveAttributeSubentry cn: Inherit From Locality subtreeSpecification: { base "ou=People", specificationFilter "(objectClass=posixAccount)" } inheritFromBaseRDN: ou=Locations inheritFromRDNAttribute: l inheritFromRDNType: l inheritAttribute: preferredLanguage inheritAttribute: street collectiveConflictBehavior: real-overrides-virtual
The RDN of the entry from which to inherit attributes is l=localityName,ou=Locations
, where localityName is the value of the l
(localityName
) attribute on the user's entry.
In other words, if the user's entry has l: Bristol
, then the RDN of the entry from which to inherit attributes starts with l=Bristol,ou=Locations
. The actual entry looks like this:
dn: l=Bristol,ou=Locations,dc=example,dc=com objectClass: top objectClass: locality objectClass: extensibleObject l: Bristol street: Broad Quay House, Prince Street preferredLanguage: en-gb
The subentry specifies that the inherited attributes are preferred language and street address.
The object class extensibleObject
allows the entry to take a preferred language. The object class extensibleObject
means, "Let me add whatever attributes I want." The best practice is to add a custom auxiliary object class instead. The example uses shortcut extensibleObject
for simplicity.
Notice the last line of the collective attribute subentry:
collectiveConflictBehavior: real-overrides-virtual
This line indicates that when a collective attribute clashes with a real attribute, the real value takes precedence over the virtual, collective value. You can set collectiveConflictBehavior
to virtual-overrides-real
for the opposite precedence, or to merge-real-and-virtual
to keep both sets of values.
In this example, users can set their own language preferences. When users set language preferences manually, the user's settings override the locality-based setting.
Sam Carter is located in Bristol. Sam has specified no preferred languages:
dn: uid=scarter,ou=People,dc=example,dc=com l: Bristol
Sam inherits the street address and preferred language from the Bristol locality. Before trying this example, set up a directory server with the ds-evaluation
profile:
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=scarter)" \ preferredLanguage street
dn: uid=scarter,ou=People,dc=example,dc=com preferredLanguage: en-gb street: Broad Quay House, Prince Street
Babs's locality is San Francisco. Babs prefers English, but also knows Korean:
dn: uid=bjensen,ou=People,dc=example,dc=com preferredLanguage: en, ko;q=0.8 l: San Francisco
Babs inherits the street address from the San Francisco locality, but keeps her language preferences:
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=bjensen)" \ preferredLanguage street
dn: uid=bjensen,ou=People,dc=example,dc=com preferredLanguage: en, ko;q=0.8 street: 201 Mission Street Suite 2900
This example demonstrates how to inherit a description from the parent entry.
The following collective attribute subentry specifies that entries inherit a description from their parent unless they already have a description:
dn: cn=Inherit Description From Parent,dc=example,dc=com objectClass: top objectClass: subentry objectClass: inheritedCollectiveAttributeSubentry objectClass: inheritedFromDNCollectiveAttributeSubentry cn: Inherit Description From Parent subtreeSpecification: { base "", minimum 2, specificationFilter "(objectClass=posixAccount)" } inheritFromDNAttribute: entryDN inheritFromDNParent: 1 inheritAttribute: description collectiveConflictBehavior: real-overrides-virtual
In this example, inheritFromDNAttribute
uses the virtual attribute entryDN
. The setting inheritFromDNParent: 1
indicates that the server should locate the parent entry by removing one leading RDN from the entryDN
. (To inherit from the grandparent entry, you would specify inheritFromDNParent: 2
, for example.)
Sam Carter's entry has no description attribute, and so inherits the parent's description:
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=scarter)" \ description
dn: uid=scarter,ou=People,dc=example,dc=com description: Description on ou=People description: Served by my favorite directory server
Babs Jensen's entry already has a description attribute, and so does not inherit from the parent:
$ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePasswordFile /path/to/opendj/config/keystore.pin \ --baseDN dc=example,dc=com \ "(uid=bjensen)" \ description
dn: uid=bjensen,ou=People,dc=example,dc=com description: Original description
About Subentry Scope
LDAP subentries reside with the user data and so are replicated. Subentries hold operational data. They are not visible in search results unless explicitly requested. This section describes how a subentry's subtreeSpecification
attribute defines the scope of the subtree that the subentry applies to.
An LDAP subentry's subtree specification identifies a subset of entries in a branch of the DIT. The subentry scope is these entries. In other words, these are the entries that the subentry affects.
The attribute value for a subtreeSpecification
optionally includes the following parameters:
base
Indicates the entry, relative to the subentry's parent, at the base of the subtree.
By default, the base is the subentry's parent.
specificationFilter
Indicates an LDAP filter. Entries matching the filter are in scope.
DS servers extend the standard implementation to allow any search filter, not just an assertion about the
objectClass
attribute.By default, all entries under the base entry are in scope.
The following illustration shows this for an example collective attribute subentry:
Notice that the base of ou=People
on the subentry cn=Silver Class of Service,dc=example,dc=com
indicates that the base entry is ou=People,dc=example,dc=com
.
The filter "(classOfService=silver)"
means that Kirsten Vaughan and Sam Carter's entries are in scope. Babs Jensen's entry, with classOfService: bronze
does not match and is therefore not in scope. The ou=People
organizational unit entry does not have a classOfService
attribute, and so is not in scope, either.