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.
Class of service
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 \
--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)" \
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 \
--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=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 \
--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=scarter)" \
classOfService mailQuota diskQuota
dn: uid=scarter,ou=People,dc=example,dc=com
classOfService: gold
mailQuota: 10 GB
diskQuota: 100 GB
Inherit from the manager
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 \
--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)" \
departmentNumber
dn: uid=bjensen,ou=People,dc=example,dc=com
departmentNumber: 3001
Inherit from the locality
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 \
--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=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 \
--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)" \
preferredLanguage street
dn: uid=bjensen,ou=People,dc=example,dc=com
preferredLanguage: en, ko;q=0.8
street: 201 Mission Street Suite 2900
Inherit from a parent entry
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 \
--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=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 \
--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)" \
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 the server replicates them.
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.