Directories and Vocabularies

Table of Contents

19.1. Introduction
19.2. Directory with a Relational Database Management System (SQL) server as backend
19.3. Directory with an LDAP server as backend
19.3.1. Server definition
19.3.2. Directory declaration
19.4. Handling references between directory entries
19.4.1. References defined by a many-to-many SQL table
19.4.2. Static reference as a dn-valued LDAP attribute
19.4.3. Dynamic reference as a ldapUrl-valued LDAP attribute
19.4.4. LDAP tree reference
19.4.5. Defining inverse references
19.5. Combining multiple directories into a single virtual directory
19.5.1. Multi-directory sources
19.5.2. Sub-directories
19.6. The Directory API
19.7.
19.7.1. Building custom option lists in forms with directories
19.7.2. Vocabularies/Directories management

19.1. Introduction

TODO OG - General overview of the directory concept and goals

19.2. Directory with a Relational Database Management System (SQL) server as backend

The SQL server as storage backend is provided by org.nuxeo.ecm.directory.sql.* component.

<component name="org.nuxeo.ecm.directory.sql.storage">
  <implementation class="org.nuxeo.ecm.directory.sql.SQLDirectoryDescriptor" />

  <require>org.nuxeo.ecm.directory.sql.SQLDirectoryFactory</require>

  <extension target="org.nuxeo.ecm.directory.sql.SQLDirectoryFactory"
    point="directories">
    <directory name="userDirectory">
      <schema>user</schema>
      <dataSource>java:/nxsqldirectory</dataSource>
      <table>users</table>
      <idField>username</idField>
      <passwordField>password</passwordField>
      <autoincrementIdField>false</autoincrementIdField>
      <dataFile>users.csv</dataFile>
      <createTablePolicy>on_missing_columns</createTablePolicy>
      <querySizeLimit>15</querySizeLimit>
    </directory>
  </extension>
</component>

This code declares a directories node which defines a directory of users or of groups. The following information are given to describe the directory:

  • name: name of the server which will be used in the declaration of the directories

  • schema: namee of the schema describing the user attributes in the directory

  • dataSource: type of storage for the directory. In this example, the HSQLDB is used. Other RDBMS like PostgreSQL can be used to store the datas by changing the local datasource.

  • table: name of the table in which the data will be stored

  • idField: the id field designs the primary key in the table, used for retrieving entries by id

  • password: field from the table which contain the passwords, relative to the identfiant

  • autoincrementIdField: boolean value which tells if the idField is automatically incremented - this value is most of the time at false, because the identifiant is a string.

  • dataFile: file from which data are getting to populate the table. Be careful to follow the structure of the schema given above.

  • createTablePolicy: indicates how the dataFile will be used to populate the table. Three values are allowed:

    • never: the dataFile is never used

    • on_missing_columns: the dataFile is used to create missing columns, it means at creation of the table or each time a new column is added, to follow the schema for example. Colums cannot be deleted.

    • always: the dataFile is used to create the table as each restart of the application server

  • querySizeLimit: the maximum number of results that the queries on this directory should return; if there are more results than this, an exception will be raised

19.3. Directory with an LDAP server as backend

The LDAP server as storage backend is provided by the org.nuxeo.ecm.directory.ldap.* component

19.3.1. Server definition

First of all, LDAP servers have to be defined by adding a contribution to the servers extension point. The syntax is:

  <extension target="org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory"
    point="servers">

    <server name="default">

      <ldapUrl>ldap://localhost:389</ldapUrl>
      <bindDn>cn=nuxeo5,ou=applications,dc=example,dc=com</bindDn>
      <bindPassword>changeme</bindPassword>
    </server>

  </extension>

These information need to be provided to use an LDAP connection:

  • name:name of the server which will be used in the declaration of the directories

  • ldapUrl:address of the LDAP server. A single server declaration can point to a cluster of replicated servers. To leverage such a cluster and improve availibility, please provide one <ldapUrl/> tag for each replica of the cluster. ldaps is the convention to use TLS/SSL connection.

  • bindDn: the Distinguished Name used to bind to the LDAP server

  • bindPassword: corresponding password relative to the Distinguished Name for binding

    These credentials are used by Nuxeo5 to browse directory and create/modify entries.

19.3.2. Directory declaration

Once you have declared the server, you can define new LDAP directories, using the following syntax for its declaration:

  <extension target="org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory"
    point="directories">

    <directory name="userDirectory">
      <server>default</server>
      <schema>user</schema>
      <idField>username</idField>
      <passwordField>password</passwordField>

      <searchBaseDn>ou=people,dc=example,dc=com</searchBaseDn>
      <searchClass>person</searchClass>
      <searchFilter>(&amp;(sn=toto*)(myCustomAttribute=somevalue))</searchFilter>
      <searchScope>onelevel</searchScope>
      <substringMatchType>subany</substringMatchType>

      <readOnly>false</readOnly>

      <cacheTimeout>3600</cacheTimeout>
      <cacheMaxSize>1000</cacheMaxSize>

      <creationBaseDn>ou=people,dc=example,dc=com</creationBaseDn>
      <creationClass>top</creationClass>
      <creationClass>person</creationClass>
      <creationClass>organizationalPerson</creationClass>
      <creationClass>inetOrgPerson</creationClass>

    </directory>

  </extension>

The attributes are:

  • name, schema, idField and passwordField are the same as for SQL directories

  • searchBaseDn: entry point into the server's LDAP tree structure. Searches are only made below this root node

  • searchClass: restricts the type of entries to return as result

  • searchFilter :additional filter to restrict the search results

  • searchScope: the scope of the search. It can take two values:

    • onelevel:search only under the current node.

    • subtree: search in the whole subtree. Use this parameter when the [people] branch is nested.

  • substringMatchType: defines who the query is built using wildcard characters. Three different values can be provided:

    • subany: wildcards are added around the string to match (as *foo*)

    • subinitial: wildcard is added before the string (*bar)

    • subfinal: wildcard is added after the string (baz*). This is the default behaviour.

  • readOnly: boolean value. This parameter allows to create new entries or modify existing ones in the LDAP server

  • cacheTimeout: cache timeout in seconds

  • cacheMaxSize: maximum number of cached entries before global invalidation

    To disable the cache, comment <cache* /> tags

  • creationBaseDn: entry point in the server's LDAP tree structure where new entries will be created. This is useless to provided if readOnly attribute is set to false.

  • creationClass: use as many tag as needed to specify which class are used to defined new people entries in LDAP server.

19.4. Handling references between directory entries

Directory references leverage two common ways of string relationship in LDAP directories

19.4.1. References defined by a many-to-many SQL table

TODO OG

19.4.2. Static reference as a dn-valued LDAP attribute

The static reference strategy is to apply when a multi-valued attribute stores the exhaustive list of distinguished names of reference entries, for example the uniqueMember of the groupOfUniqueNames object.

<ldapReference field="members" directory="userDirectory"
  staticAttributeId="uniqueMember" />

The staticAttributeId attribute contains directly the value which can be read and manipulated.

19.4.3. Dynamic reference as a ldapUrl-valued LDAP attribute

The dynamic attribute strategy is used when a potentially multi-value attribute stores a LDAP URL intensively, for example the memberURL's attribute of the groupOfURL object class.

<ldapReference field="members" directory="userDirectory"
  forceDnConsistencyCheck="false"
  dynamicAttributeId="memberURL" />

The value contained in dynamicAttributeId looks like ldap:///ou=groups,dc=example,dc=com??subtree?(cn=sub*) and will be resolved by dynamical queries to get all values. The forceDnConsistencyCheck attribute will check that the value got through the dynamic resolution correspond to the attended format. otherwise, the value will be ignored. Use this check when you are not sure of the validity of the distinguished name

19.4.4. LDAP tree reference

The LDAP tree reference can be used to resolve children in the LDAP tree hierarchy.

<ldapTreeReference field="children" directory="groupDirectory"
  scope="subtree" />

The field has to be a list of strings. It will resolve children of entries in the current directory, and look them up in the directory specified in the reference.The scope attribute. Available scopes are "onelevel" (default), "subtree". Children with same id than parent will be filtered. An inverse reference can be used to retrieve the parent form the children entries. It will be stored in a list, even if there can be only 0 or 1 parent.

WARNING: Edit is NOT IMPLEMENTED: modifications to this field will be ignored when saving the entry.

19.4.5. Defining inverse references

Inverse references are defined with the following declarations:

<references>
  <inverseReference field="groups" directory="groupDirectory"
    dualReferenceField="members" />
</references>

This syntax should be understood as "the member groups value is an inverse reference on groupDirectory directory using members reference". It is the group directory that stores all members for a given group. So the groups of a member are retrieved by querying in which groups a member belongs to.

19.5. Combining multiple directories into a single virtual directory

Multi directories are used to combine values coming from different directories.

For example, it is useful to combine entries from LDAP directory with a standard directory provided by Nuxeo5.

19.5.1. Multi-directory sources

<component name="org.nuxeo.ecm.directory.multi.config">

  <extension
    target="org.nuxeo.ecm.directory.multi.MultiDirectoryFactory"
    point="directories">

    <directory name="multi">
      <schema>schema</schema>
      <idField>uid</idField>
      <passwordField>password</passwordField>

      <source name="sourceA" creation="true">
        ...
      </source>

      <source name="sourceB">
        ....
      </source>
    </directory>

  </extension>

</component>

19.5.2. Sub-directories

      <source name="source1" creation="true">
        <subDirectory name="dir1">
          <field for="thefoo">foo</field>
        </subDirectory>
        <subDirectory name="dir2">
          <field for="uid">id</field>
          <field for="thebar">bar</field>
        </subDirectory>
      </source>

19.6. The Directory API

This component provides tools to dialog with directories, make queries and get informations. Three classes are a

19.7.1. Building custom option lists in forms with directories

Directories are also useful to build and store values that will be used in option lists. We usually call "vocabulary" that kind of directories as they follow a simple schema.

<component name="org.nuxeo.ecm.directories">

  <extension target="org.nuxeo.ecm.directory.sql.SQLDirectoryFactory"
    point="directories">

    <directory name="country">
      <schema>xvocabulary</schema>
      <parentDirectory>continent</parentDirectory>
      <dataSource>java:/nxsqldirectory</dataSource>
      <cacheTimeout>3600</cacheTimeout>
      <cacheMaxSize>1000</cacheMaxSize>
      <table>country</table>
      <idField>id</idField>
      <autoincrementIdField>false</autoincrementIdField>
      <dataFile>directories/country.csv</dataFile>
      <createTablePolicy>on_missing_columns</createTablePolicy>
    </directory>

    <directory name="continent">
      <schema>vocabulary</schema>
      <dataSource>java:/nxsqldirectory</dataSource>
      <cacheTimeout>3600</cacheTimeout>
      <cacheMaxSize>1000</cacheMaxSize>
      <table>continent</table>
      <idField>id</idField>
      <autoincrementIdField>false</autoincrementIdField>
      <dataFile>directories/continent.csv</dataFile>
      <createTablePolicy>on_missing_columns</createTablePolicy>
    </directory>

  </extension>

</component>
 

Example 19.1. Sample declaration of vocabularies.


The different attributes have the same behaviour as other directories.

Let's have a look at the schema attribute which can take two different values:

  • vocabulary: this schema is provided to make default vocabulary. It defines the following fields: id, label, order and obsolete.

  • xvocabulary: this schema is used to define linked vocabularies. It defines the following fields: id, label, order, obsolete and parent. When using xvocabulary schema, an other attribute should be defined: parentDirectory points the parent directory name to which the current one is relative.

When these vocabularies are set up, the following JSF methods can be used to render them in forms:

<div xmlns:h="http://java.sun.com/jsf/html"
  xmlns:nxdir="http://nuxeo.org/nxdirectory">

  <nxdir:selectOneListbox
    value="#{mydoc.myschema.myfield}"
    directoryName="continent"
    id="continentSelect"
    localize="true" />
  <h:message for="continentSelect" class="errorMessage" />

</div>
 

Example 19.2. Sample declaration of vocabularies.


In this example, a simple vocabulary selection list is displayed. The equivalent tag for multi selection, nxdir:selectManyListbox is also available.

<div xmlns:h="http://java.sun.com/jsf/html"
  xmlns:nxdir="http://nuxeo.org/nxdirectory">

  <h:selectOneListbox value="#{mydoc.myschema.myfield}"
    id="continentSelect">
    <nxdir:selectItems
      directoryName="continent"
      var="item"
      itemValue="#{item.vocabulary.id}"
      itemLabel="#{item.vocabulary.label}"
      displayAll="true" />
  </h:selectOneListbox>
  <h:message for="continentSelect" class="errorMessage" />

</div>
 

Example 19.3. Sample declaration of vocabularies.


This is the same example, but using standard JSF selection components, and another JSF method to display select items. This is more configurable, and can be helpful when using another schema than the default "vocabulary" and "xvocabulary" ones.

<div xmlns:h="http://java.sun.com/jsf/html"
  xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
  xmlns:nxdir="http://nuxeo.org/nxdirectory">

  <a4j:region id="countrySelectRegion">

    <nxdir:chainSelect size="2" value="#{mydoc.myschema.myfield}"
      id="#{continentCountryChainSelect}">

      <nxdir:chainSelectListbox index="0" size="0" directoryName="continent"
        localize="true" id="selectContinent">
        <a4j:support event="onchange" reRender="selectCountry" />
      </nxdir:chainSelectListbox>
  
      <nxdir:chainSelectListbox size="0" index="1" directoryName="country"
        localize="true" id="selectCountry" />

    </nxdir:chainSelect>

  </a4j:region>

  <h:message styleClass="errorMessage" for="continentCountryChainSelect" />
  
</div>
 

Example 19.4. Sample declaration of vocabularies.


This is an example of a chained list box.

For more information about available tags, please check the documentation at http://doc.nuxeo.org/current/tlddoc/. Note that this kind of rendering can be achieved using layout configuration too.

19.7.2. Vocabularies/Directories management

Some pages are available to be able to delete/add/edit entries in these directories. In the default application, it is presented clicking on the "Manage vocabularies" link that is displayed on top of every page if logged in as an administrator.

The list of directories that can be managed will be displayed. Since Nuxeo 5.2.1, this is managed through another extension point, dedicated to the User Interface part, and uses layouts configuration.

<component name="org.nuxeo.ecm.webapp.directory.directoryUI">

  <extension target="org.nuxeo.ecm.directory.ui.DirectoryUIManager"
    point="directories">

    <directory name="continent" layout="vocabulary" sortField="label">
      <deleteConstraint
        class="org.nuxeo.ecm.directory.api.ui.HierarchicalDirectoryUIDeleteConstraint">
        <property name="targetDirectory">country</property>
        <property name="targetDirectoryField">parent</property>
      </deleteConstraint>
    </directory>
    <directory name="country" layout="country_vocabulary" sortField="parent" />

  </extension>

  <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
    point="layouts">

    <layout name="vocabulary">
      <templates>
        <template mode="any">
          /directory/directory_layout_template.xhtml
        </template>
      </templates>
      <rows>
        <row>
          <widget>vocabulary_id</widget>
        </row>
        <row>
          <widget>vocabulary_label</widget>
        </row>
        <row>
          <widget>vocabulary_obsolete</widget>
        </row>
        <row>
          <widget>vocabulary_order</widget>
        </row>
      </rows>
    </layout>

    <layout name="country_vocabulary">
      <templates>
        <template mode="any">
          /directory/directory_layout_template.xhtml
        </template>
      </templates>
      <rows>
        <row>
          <widget>parent</widget>
        </row>
        <row>
          <widget>xvocabulary_id</widget>
        </row>
        <row>
          <widget>xvocabulary_label</widget>
        </row>
        <row>
          <widget>xvocabulary_obsolete</widget>
        </row>
        <row>
          <widget>xvocabulary_order</widget>
        </row>
      </rows>
      <widget name="parent" type="selectOneDirectory">
        <labels>
          <label mode="any">label.vocabulary.entry.parent</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field>xvocabulary:parent</field>
        </fields>
        <properties mode="any">
          <property name="directoryName">continent</property>
          <property name="localize">true</property>
        </properties>
        <properties widgetMode="edit">
          <property name="required">true</property>
        </properties>
      </widget>
    </layout>
  </extension>

</component>

 

Example 19.5. Sample declaration of directory to display.


This files is declaring the directories to display, and the layouts to use when displaying them. The layouts configuration is standard, please refer to the chapter Chapter 8, Layouts for more information.

Note that the Directory UI declaration can state a delete constraint to be used when trying to delete an item. The class checking the deletion can be contributed and has to follow the org.nuxeo.ecm.directory.api.ui.DirectoryUIDeleteConstraint interface. The class in the example takes as parameters some information about the directory where to check constraints on. It is designed to refuse deletion of a parent vocabulary item, if there is still a reference to it in the child vocabulary.