Customizing users and groups

November 14, 2023
Users and groups are nodes like others on a Jahia platform, they follow definitions and can be manipulated through administration interfaces and programmatically, using APIs

Users batch creation

Jahia allows administrators to import users in batch to create local accounts (meaning you don't use an external repository of users like Active Directory or LDAP).

Prepare a CSV file

Your CSV file must be structured as follow: The file must include a header line with at minimum the columns j:nodename (username) and j:password.

You can include any other supported user properties as values, in the desired order. Then, each user to be created must be declared on a different line, with associated values in the correct order.

Example:

j:nodename,j:password,j:firstName,j:lastName
steven,Sseagal666,Steven,Seagal
chucky,TexasRanger,Chuck,Norris

Import users into Jahia

To import users, log yourself into the Jahia Administration Interface (default: http://localhost:8080/administration/) Click on 'Manage users' Click on the button "Batch create users" In the form newly displayed: Type the separator used in your CSV file (default is ',') Select the CSV file by clicking on 'browse' Click on the OK button at the bottom of the screen

DEFAULT PROPERTIES AVAILABLE ON USERS NODES

  • j:nodename (string) 
  • j:accountLocked (boolean)=false autocreated 
  • j:password (string) indexed=no 
  • j:external (boolean)=false autocreated 
  • j:externalSource (string) nofulltext 
  • j:firstName (string) 
  • j:lastName (string) 
  • j:gender (string,choicelist[resourceBundle]) < male,female,other 
  • j:title (string,choicelist[resourceBundle]) < mister,master,professor,doctor,miss,madam 
  • j:birthDate (date,datepicker) 
  • j:organization (string) 
  • j:function (string) 
  • j:about (string,richtext) 
  • j:email (string) < '^$|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}' 
  • j:skypeID (string) 
  • j:twitterID (string) 
  • j:facebookID (string) 
  • j:linkedinID (string) 
  • j:picture (weakreference,picker[type='file']) < jnt:file 
  • j:publicProperties (string) multiple 
  • * (string)

User profile

When the user access to his own profile, in live mode he can edit all his information directly on the page without going to edit mode engines or content manager.

This module (located in modules/default/) mainly use jQuery and Jeditable.

This is a good entry point to have example of how to use in-line edition for editing small properties in live mode.

In this documentation we will show how to make edition of text, richtext, date, file, dropdown properties.

For each of those type of data we use the same principle: a JavaScript function applied to a class that allow the edition of the content when the user clicks on it.

Simple Text

Here the JavaScript example of how to edit a simple text :

<script type="text/javascript">
$(document).ready(function() {
        $(".edit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            data[submitId] = value;
            data['jcrMethodToCall'] = 'put';
            $.post("${url.base}${currentNode.path}", data, function(result) {
                $("#personDisplay").html(result.j_title + " " + result.j_firstName + " " + result.j_lastName);
            }, "json");
            return(value);
        }, {
            type    : 'text',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit'
        });
    });
</script>

In this JavaScript function, we create a data map for handling our form data, we pass a 'jcrMethodToCall' parameter to Jahia to use a put method instead of a post. For the name of the properties to update we get it form the id of the HTML object associated with this edition. When the post is submitted we get the updated node as json data and we update some specific content on the page.

Now the HTML code associated :

<span class="label"><fmt:message key="jnt_user.j_email"/> : </span>
<span id="j_email" class="edit">${fields['j:email']}</span>

Rich Text

For rich text it works the same way, only the type of the editable will change :

<script type="text/javascript">
$(document).ready(function() {
     $(".ckeditorEdit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            data[submitId] = value;
            data['jcrMethodToCall'] = 'put';
            $.post("${url.base}${currentNode.path}", data, null, "json");
            return(value);
        }, {
            type : 'ckeditor',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit'
        });
    });
</script>

Now the HTML for using this CKEditor inline edition :

<div class="ckeditorEdit" id="j_about">${fields['j:about']}</div>

Date Picker

For date, we will use a date picker plugin for jQuery. Here how we use it for Jahia.

<jcr:nodeProperty node="${currentNode}" name="j:birthDate" var="birthDate"/>
<c:if test="${not empty birthDate}">
    <fmt:formatDate value="${birthDate.date.time}" pattern="dd/MM/yyyy" var="editBirthDate"/>
</c:if>
<fmt:formatDate value="${now}" pattern="dd/MM/yyyy" var="editNowDate"/>
<script type="text/javascript">
$(document).ready(function() {
     $(".dateEdit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            if (value.match("[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]")) {
                var split = value.split("/");
                var birth = new Date();
                birth.setFullYear(split[2], split[1], split[0]);
                var month = "";
                if (birth.getMonth() < 10) {
                    month = "0" + birth.getMonth();
                } else month = birth.getMonth();
                data[submitId] = birth.getFullYear() + '-' + month + '-' + birth.getDate() + 'T00:00:00';
                data['jcrMethodToCall'] = 'put';
                $.post("${url.base}${currentNode.path}", data, null, "json");
            }
            return(value);
        }, {
            type : 'datepicker',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit',
            datepicker : {
                flat: true,
                date: '${not empty editBirthDate ? editBirthDate : editNowDate}',
                format: 'd/m/Y',
                view: 'years',
                current: '${not empty editBirthDate ? editBirthDate : editNowDate}',
                calendars: 1,
                starts: 1     }
        });
     });
</script>

In this case the internal format for date is day/month/year, when submitting we convert that to an ISO8601 for updating date in Jahia (This is the internal format of the JCR).

HTML usage example :

<jcr:nodeProperty node="${currentNode}" name="j:birthDate" var="birthDate"/>
<c:if test="${not empty birthDate}">
    <fmt:formatDate value="${birthDate.date.time}" pattern="dd, MMMM yyyy" var="displayBirthDate"/>
</c:if>
<c:if test="${empty birthDate}">
   <jsp:useBean id="now" class="java.util.Date"/>
   <fmt:formatDate value="${now}" pattern="dd, MMMM yyyy" var="displayBirthDate"/>
</c:if>
<div class="dateEdit" id="j_birthDate">${displayBirthDate}</div>

DropDown List

We will now see how to use the choicelist initializer mechanism to allow creation of custom static drop down list (as opposed to dynamic one that will load data from the server on demand)

<jcr:propertyInitializers node="${currentNode}" name="j:title" var="titleInit"/>
<script type="text/javascript">
    var titleMap = "{<c:forEach items="${titleInit}" varStatus="status" var="title"><c:if test="${status.index > 0}">,</c:if>'${title.value.string}':'${title.displayName}'</c:forEach>}";
$(document).ready(function() {
    $(".titleEdit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            data[submitId] = value;
            data['jcrMethodToCall'] = 'put';
            $.post("${url.base}${currentNode.path}", data, null, "json");
            return eval("values="+titleMap)[value];
        }, {
            type    : 'select',
            data   : titleMap,
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit'
        });
    });
</script>

The tag <jcr:propertyInitializers/> store the list of ChoiceListValue object associated with the property to the specified var. We iterate over the list to create a JavaScript map that will be used for initializing our data for the dropdown list.

HTML usage example :

<div class="titleEdit" id="j_title">
    <jcr:nodePropertyRenderer node="${currentNode}" name="j:title" renderer="resourceBundle"/>
</div>

Ajax Upload

Now we will see how to upload a file in live mode using ajax requests.

This is a two-phase submit, first we upload the selected file to the user files folder, then we get back the id of the file and we associate it to the property we want. Here the code doing that :

<script type="text/javascript">
$(document).ready(function() {
        $(".imageEdit").editable('${url.base}${currentNode.path}', {
            type : 'ajaxupload',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit',
            callback : function (data, status) {
                uploadedImageCallback(data, status);
            }
        });

        function uploadedImageCallback(data, status) {
            var datas = {};
            datas['j:picture'] = data.uuids[0];
            datas['jcrMethodToCall'] = 'put';
            $.post('${url.base}${currentNode.path}', datas, function(result) {
                var input = $('<div class="itemImage itemImageRight"><img src="' + result.j_picture + '" width="60" height="60"/></div>');
                $("#portrait").html(input);
            }, "json");
        }
    });
</script>

We see that the ajaxupload editable allow to define a callback with the data from the result of the post. We use those datas (list of uuids) to associate the newly created file with our profile j:picture property. Once associated in the json for the j:picture property we have the url associated instead of the uuid of the reference.

HTML usage example :

<div class="image imageEdit" id="portrait">
    <div class="itemImage itemImageRight"><jcr:nodeProperty var="picture" node="${currentNode}" name="j:picture"/>
        <c:if test="${not empty picture}">
            <img src="${picture.node.url}" alt="${person}" width="60" height="60"/>
        </c:if>
    </div>
</div>

Access User Properties From My Templates

In your templates, the user as a JahiaUser object is available directly under the renderContext object.

// Display username
${renderContext.user.username}
// Display user firstname
${renderContext.user.properties['j:firstName']}

Users extension

General info about users and users data

All users nodes are found under the /users folder, they are split following some customizable rules.

By default we split them into 3 subfolders, to ensure scalability of the platform.

Under a user node, you can store anything you want (any properties, any sub-nodes).

All properties you will add to users are not displayed in their profiles. If you want the users to manage those properties through their profiles pages you, you will have to customize the profiles templates (through the studio/jsp files).

The user nodes are not for storing some navigational history or heavy data sets requiring a huge continuous write activity, they must be reserved to store contents/preferences/properties related to a user.

To store some behavioral data about users you will have to implement some Apache Camel log listeners like the Page Hit module (available on the Jahia Forge as a code sample) to store data in an asynchronous way in a DB/Hadoop (FS or DB)/other type of storage. See the Jahia/Apache Camel documentation.

If you have a limited number of data (less than 1000 nodes) per user you want to use for personalization then you can store them under subnodes of the user.

Differences between Jahia Users / LDAP Users

From Jahia side there are not many differences between LDAP users and Jahia users, you can store your custom properties/nodes in both of them. In fact all users of the system will have it own sub node, that it came from Jahia or from an external system like LDAP.

The only difference is that in the case of the LDAP users, all properties are not coming from Jahia nor from LDAP but are a merge of those two sources.

LDAP Connector

For Jahia 8 and Jahia 7.3, please refer to the Connecting to a LDAP server documentation

Versions 7.1 and 7.2

Overview

This module provides LDAP provider implementations for the user and user groups. Jahia supports multiple LDAP providers to be used simultaneously to achieve better flexibility and data aggregation.

Registering LDAP providers

Since version 7.1, LDAP user and group providers can be configured using the administration UI under Server settings / Users and Roles / User and group providers. The UI provides a convenient way to add, remove, start or suspend providers and also adjust their configuration.

ldap-provider-properties.png

Various configuration properties can be provided/adjusted directly in the provider edit screen:

ldap-providers.png

Internally the OSGi ConfigurationAdmin service is used to store the user and group LDAP provider configuration. Thanks to this, hot deploy and editing of your configuration can be done.

Please, refere to the property reference for more details.

User provider properties reference

The following LDAP properties can be specified to customize the user provider configuration:

  1. Connection and authentication parameters
    Key Default value Description
    url <none> The LDAP connection URL, e.g. ldap://127.0.0.1:389/
    public.bind.dn <none> This is the user on the LDAP server permitted to search the LDAP directory within the defined search base.
    public.bind.password <none> The password used to authenticate searches in LDAP directory
    target.site <none> [Optional] Set a site key to mount the provider on a site
    authentification.mode simple LDAP directory authentication type
    context.factory com.sun.jndi.ldap.LdapCtxFactory The implementation class for context factory to use
    ldap.connect.pool apache-commons Type of pool to use: ldap, apache-commons, or none. See next sections for pool type-specific parameters.
  2. Connection parameters for native Java ldap pool
    Key Default value Description
    ldap.connect.pool.debug <none> A string that indicates the level of debug output to produce. Valid values are "fine" (trace connection creation and removal) and "all" (all debugging information).
    ldap.connect.pool.initSize 1 The string representation of an integer that represents the number of connections per connection identity to create when initially creating a connection for the identity.
    ldap.connect.pool.maxSize <none> The string representation of an integer that represents the maximum number of connections per connection identity that can be maintained concurrently.
    ldap.connect.pool.prefSize <none> The string representation of an integer that represents the preferred number of connections per connection identity that should be maintained concurrently.
    ldap.connect.pool.timeout <none> The string representation of an integer that represents the number of milliseconds that an idle connection may remain in the pool without being closed and removed from the pool.
  3. Connection parameters for apache-commons pool
    Key Default value Description
    ldap.connect.pool.maxActive 8 The maximum number of active connections of each type (read-only|read-write) that can be allocated from this pool at the same time, or non-positive for no limit.
    ldap.connect.pool.maxTotal -1 The overall maximum number of active connections (for all types) that can be allocated from this pool at the same time, or non-positive for no limit.
    ldap.connect.pool.maxIdle 8 The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool, without extra ones being released, or non-positive for no limit.
    ldap.connect.pool.minIdle 0 The minimum number of active connections of each type (read-only|read-write) that can remain idle in the pool, without extra ones being created, or zero to create none.
    ldap.connect.pool.maxWait -1 The maximum number of milliseconds that the pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
    ldap.connect.pool.whenExhaustedAction BLOCK

    Specifies the behavior when the pool is exhausted.

     - The FAIL option will throw aNoSuchElementException when the pool is exhausted.

     - The BLOCK option will wait until a new object is available. If max-wait is positive a NoSuchElementException is thrown if no new object is available after the max-wait time expires.

     - The GROW option will create and return a new object (essentially makingmax-active meaningless).

    ldap.connect.pool.testOnBorrow false The indication of whether objects will be validated before being borrowed from the pool. If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
    ldap.connect.pool.testOnReturn false The indication of whether objects will be validated before being returned to the pool.
    ldap.connect.pool.testWhileIdle false The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to validate, it will be dropped from the pool.
    ldap.connect.pool.timeBetweenEvictionRunsMillis -1 The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle object evictor thread will be run.
    ldap.connect.pool.numTestsPerEvictionRun 3 The number of objects to examine during each run of the idle object evictor thread (if any).
    ldap.connect.pool.minEvictableIdleTimeMillis 1000 * 60 * 30 The minimum amount of time an object may sit idle in the pool before it is eligible for eviction by the idle object evictor (if any).
  4. Search parameters
    Key Default value Description
    uid.search.name <none> The search base that defines which part of the LDAP directory tree to search, e.g. dc=jahia,dc=com
    To improve performance when retrieving the members of groups it is advisable to have a different base between user and group search.
    uid.search.attribute cn The name of the attribute that will be used as a user key.
    search.objectclass person The objectClass value for a user object
    search.wildcards.attributes ou, cn, o, c, mail, uid, uniqueIdentifier, givenName, sn, dn A list of attributes to use for wildcard searches such as *=*test*
    search.countlimit 100 The number of entries to limit search results too. If the LDAP user search returns more matching entries than specified with this parameter, the first search.countlimit will be returned only.
    search.attribute.inDn false If set to true, signal that the users and groups ids are contained in the dn. Useful to improve the performance when lookup the members.
    user.search.filter <none> A pre-defined filter which will be applied additionally to all user searches and lookups, effectively filtering non-matching users out. This option is available since LDAP module version 3.0.6 (DX 7.1.2.3) and 3.1.1 (DX 7.2.0.2).
  5. Attribute mapping parameters - defines a mapping between Jahia's jnt:user node properties and the corresponding LDAP object attributes.
    Key Default value Description
    user.j:firstName.attribute.map givenName First name
    user.j:lastName.attribute.map sn Second name
    user.j:email.attribute.map mail User's e-mail address
    user.j:organization.attribute.map ou The name of the organization

    Additional attribute mappings can be specified, using the following pattern for the entry key: user.<jahia-user-property-name>.attribute.map and the name of the corresponding LDAP object attribute as the value.


User group provider properties reference

The following LDAP properties can be specified to customize the group provider configuration:

  1. Connection and authentication parameters
    Key Default value Description
    url <none> The LDAP connection URL, e.g. ldap://127.0.0.1:389/
    public.bind.dn <none> This is the user on the LDAP server permitted to search the LDAP directory within the defined search base.
    public.bind.password <none> The password used to authenticate searches in LDAP directory
    target.site <none> [Optional] Set a site key to mount the provider on a site
    authentification.mode simple LDAP directory authentication type
    context.factory com.sun.jndi.ldap.LdapCtxFactory The implementation class for context factory to use
    ldap.connect.pool apache-commons Type of pool to use: LDAP, apache-commons, or none. See above sections for pool type-specific parameters.
  2. Search and membership parameters
    Key Default value Description
    preload false If set to true forces the reading of all group members when a group is retrieved. Otherwise, group members will be read when the first request for user membership will be made
    search.countlimit 100 The number of entries to limit search results too. If the LDAP user search returns more matching entries than specified with this parameter, the first search.countlimit will be returned only.
    refferal ignore Specifies how referrals encountered by the service provider are to be processed. Possible values are follow - follow referrals automatically, ignore - ignore referrals, throw - throw ReferralException when a referral is encountered
    ad.range.step 0 Handle Active Directory range searches when retrieving group members. If set to 0 all members are retrieved with a single search. If set e.g. to 100, searches like range=0-100, range=101-200, range=201-300 etc. are used to retrieve all members iteratively.
    search.name <none> The search base that defines which part of the LDAP directory tree to search, e.g. dc=jahia,dc=com
    To improve performance when retrieving the members of groups it is advisable to have a different base between user and group search.
    search.attribute cn The name of the attribute that will be used as a user key.
    search.objectclass groupOfUniqueNames The objectClass value for a group object
    search.wildcards.attributes cn,description,uniqueMember A list of attributes to use for wildcard searches such as *=*test*
    members.attribute uniqueMember The name of the LDAP group object attribute to retrieve membership from
    dynamic.enabled false Set to true if you want to use the dynamic groups
    dynamic.search.objectclass groupOfURLs The name of the LDAP group object attribute to retrieve dynamic membership from
    dynamic.members.attribute memberurl The name of the LDAP group object attribute to retrieve dynamic membership from
    canGroupContainSubGroups false Set to true to enable support for nested (hierarchical) groups, i.e. where a group could have other groups as members.
    group.search.filter <none> A pre-defined filter which will be applied additionally to all group searches and lookups, effectively filtering non-matching groups out. This option is available since LDAP module version 3.0.6 (DX 7.1.2.3) and 3.1.1 (DX 7.2.0.2).
  3. Attribute mapping parameters - defines a mapping between Jahia's jnt:group node properties and the corresponding LDAP object attributes.
    Key Default value Description
    group.groupname.attribute.map cn Group name
    group.description.attribute.map description Description

    Additional attribute mappings can be specified, using the following pattern for the entry key: "group.<jahia-group-property-name>.attribute.map" and the name of the corresponding LDAP object attribute as the value.

 

Legacy versions 6.5/6.6/7.0

Overview

This module provides LDAP provider implementations for the user and user groups. From version 6.5 up Jahia supports multiple LDAP providers to be used simultaneously to achieve better flexibility and data aggregation.

Registering LDAP providers

6.5/6.6

The following steps show how to enable and configure in version 6.5 or 6.6 the user and group LDAP providers, by declaring them in a custom Jahia module.

  1. Create the following folder structure for your module:
      META-INF
         | + spring
         |     | - users-ldap.xml
         |     | - groups-ldap.xml
         | - MANIFEST.MF
  2. The content of the MANIFEST.MF file should include three mandatory attributes:
      ...
      depends: Jahia LDAP connector
      package-name: My Custom LDAP Provider
      root-folder: my-custom-ldap-provider
      ...

    depends attribute defines a dependency to the Jahia LDAP connector module (this module).

    package-name specifies your module ID

    root-folder contains the name of the folder, the content of the module will be extracted into (a sub-folder of the /modules folder).

    All other attributes in MANIFEST.MF file are optional.

  3. LDAP user provide configuration in users-ldap.xml

    The file contains a Spring bean definition for the user provider as follows:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
        <bean parent="JahiaUserManagerLDAPProvider">
            <property name="ldapProperties">
                <map>
                    <entry key="url" value="ldap://127.0.0.1:389/"/>
                    <entry key="public.bind.dn" value=""/>
                    <entry key="public.bind.password" value=""/>
                    <entry key="uid.search.name" value="dc=jahia"/>
                </map>
            </property>
        </bean>
    </beans>

    The bean has asJahiaUserManagerLDAPProvider its parent and should override at least the propertyldapProperties to configure connector and LDAP attribute mapping. Default LDAP parameter values are defined in parent and can be overridden in the mapldapProperties. See property reference for more details.

    In case multiple LDAP user providers are used, additionally two properties need to be provided to specify a unique provider key and a priority (lookup sequence):

    ...
        <bean parent="JahiaUserManagerLDAPProvider">
            <property name="key" value="myldap"/>
            <property name="priority" value="2"/>
        ...
        </bean>
    ...

    The fileusers-ldap.xml name is arbitrary and the file itself can be omitted if no LDAP user provider is needed.

  4. LDAP user group provide a configuration in groups-ldap.xml

    The file contains a Spring bean definition for the user group provider as follows:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
        <bean parent="JahiaGroupManagerLDAPProvider">
            <property name="ldapProperties">
                <map>
                    <entry key="url" value="ldap://127.0.0.1:389/"/>
                    <entry key="public.bind.dn" value=""/>
                    <entry key="public.bind.password" value=""/>
                    <entry key="search.name" value="dc=jahia"/>
                </map>
            </property>
        </bean>
    
    </beans>

    The bean has JahiaGroupManagerLDAPProvider as its parent and should override at least the ldapProperties property to properly configure connector and LDAP attribute mapping. Default LDAP parameter values are defined in parent and can be overridden in the ldapProperties map. See property reference for more details.

    In case multiple LDAP user group providers are used, additionally two properties need to be provided to specify a unique provider key and a priority (lookup sequence):

    ...
        <bean parent="JahiaGroupManagerLDAPProvider">
            <property name="key" value="myldap"/>
            <property name="priority" value="2"/>
        ...
        </bean>
    ...

    The groups-ldap.xml file name is arbitrary and the file itself can be omitted, if no LDAP user provider is needed.

  5. Zip the content of your module into a file with .war, e.g. my-custom-ldap-provider.war
  6. Copy the module WAR file into the WEB-INF/var/shared_module folder of your server (all cluster nodes)
  7. If the module hot deployment is enabled the providers will be registered automatically. Otherwise a server restart is required for a module to be deployed and providers to be registered.
7.0+

Since version 7, OSGi ConfigurationAdmin service is used to declare user and group LDAP providers. Thanks to this, hot deploy and editing of your configuration can be done.

You just need to create a configuration file in the digital-factory-data/karaf/etc/ directory (for 7.0.0.0/7.0.0.1 the /WEB-INF/var/modules/ directory of your Jahia webapp).
This file should respect the following name format: org.jahia.services.usermanager.ldap-config.cfg
The config part can be replaced by another configuration name, if you need to create several LDAP configurations. Starting from DX 7.2.0.0, configuration files should not be placed in /modules anymore, and rather moved in the karaf/etc folder.

This file is a regular properties file. It contains properties for one user and/or one group LDAP provider(s).
User-specific and group-specific properties keys respectively need to be prefixed by user. and group..
To avoid repeating shared properties like connection and authentication parameters, you can keep only one without prefix.
Notice that if you want to declare only a user or only a group provider, all the properties keys in the configuration file should start with the corresponding prefix.

See property reference for more details.

Here is an example of a LDAP configuration file content:

user.uid.search.name:dc=jahia,dc=com
group.search.name:dc=jahia,dc=com
public.bind.password:ldapadmin
public.bind.dn:cn=admin,dc=jahia,dc=com

url=ldap://myldapserver:389/

A LDAP configuration file can be created or edited without restarting your application server.
Existing configurations can also be edited from the Jahia Felix Web Console Configuration accessible in the tools at the following URL:
<your_server>/tools/osgi/console/configMgr


User provider properties reference

The following LDAP properties can be specified in the ldapProperties map to customize the user provider configuration:

  1. Connection and authentication parameters
    Key Default value Description
    url <none> The LDAP connection URL, e.g. ldap://127.0.0.1:389/
    public.bind.dn <none> This is the user on the LDAP server permitted to search the LDAP directory within the defined search base.
    public.bind.password <none> The password, used to authenticate searches in LDAP directory
    target.site <none> [Optional] Set a site key to mount the provider on a site
    authentification.mode simple LDAP directory authentication type
    context.factory com.sun.jndi.ldap.LdapCtxFactory The implementation class for context factory to use
    ldap.connect.pool true Enables/disables connection pooling
    ldap.connect.timeout 5000 When connection pooling has been enabled and no pooled connection is available, the client application will block, waiting for an available connection. Setting this timeout value will specify how long to wait for a pooled connection (in milliseconds). If you omit this property, the application will wait indefinitely. See connection pooling for details.
    search.countlimit 100 The number of entries to limit search results to. If the LDAP user search returns more matching entries than specified with this parameter, the first search.countlimit will be returned only.
  2. Search parameters
    Key Default value Description
    uid.search.name <none> The search base that defines which part of the LDAP directory tree to search, e.g. dc=jahia,dc=com
    uid.search.attribute cn The name of the attribute that will be used as a user key.
    search.objectclass person The objectClass value for a user object
    search.wildcards.attributes ou, cn, o, c, mail, uid, uniqueIdentifier, givenName, sn, dn A list of attributes to use for wildcard searches such as *=*test*
  3. Attribute mapping parameters - defines a mapping between Jahia's jnt:user node properties and the corresponding LDAP object attributes.
    Key Default value Description
    j:firstName.attribute.map givenName First name
    j:lastName.attribute.map sn Second name
    j:email.attribute.map mail User's e-mail address
    j:organization.attribute.map ou The name of the organization

    Additional attribute mappings can be specified, using the following pattern for the entry key: "<jahia-user-property-name>.attribute.map" and the name of the corresponding LDAP object attribute as the value.


User group provider properties reference

The following LDAP properties can be specified in the ldapProperties map to customize the group provider configuration:

  1. Connection and authentication parameters
    Key Default value Description
    url <none> The LDAP connection URL, e.g. ldap://127.0.0.1:389/
    public.bind.dn <none> This is the user on the LDAP server permitted to search the LDAP directory within the defined search base.
    public.bind.password <none> The password used to authenticate searches in LDAP directory
    target.site <none> [Optional] Set a site key to mount the provider on a site
    authentification.mode simple LDAP directory authentication type
    context.factory com.sun.jndi.ldap.LdapCtxFactory The implementation class for context factory to use
    ldap.connect.pool true Enables/disables connection pooling
    ldap.connect.timeout 5000 When connection pooling has been enabled and no pooled connection is available, the client application will block, waiting for an available connection. Setting this timeout value will specify how long to wait for a pooled connection (in milliseconds). If you omit this property, the application will wait indefinitely. See connection pooling for details.
  2. Search and membership parameters
    Key Default value Description
    preload false If set to true forces the reading of all group members when a group is retrieved. Otherwise, group members will be read when the first request for user membership will be made
    search.countlimit 100 The number of entries to limit search results too. If the LDAP user search returns more matching entries than specified with this parameter, the first search.countlimit will be returned only.
    refferal ignore Specifies how referrals encountered by the service provider are to be processed. Possible values are follow - follow referrals automatically, ignore - ignore referrals, throw - throw ReferralException when a referral is encountered
    ad.range.step 0 Handle Active Directory range searches when retrieving group members. If set to 0 all members are retrieved with a single search. If set e.g. to 100, searches like range=0-100, range=101-200, range=201-300 etc. are used to retrieve all members iteratively.
    search.name <none> The search base that defines which part of the LDAP directory tree to search, e.g. dc=jahia,dc=com
    search.attribute cn The name of the attribute that will be used as a user key.
    search.objectclass groupOfUniqueNames The objectClass value for a group object
    search.wildcards.attributes cn,description,uniqueMember A list of attributes to use for wildcard searches such as *=*test*
    members.attribute uniqueMember The name of the LDAP group object attribute to retrieve membership from
    dynamic.search.objectclass groupOfURLs The name of the LDAP group object attribute to retrieve dynamic membership from
    dynamic.members.attribute memberurl The name of the LDAP group object attribute to retrieve dynamic membership from
  3. Attribute mapping parameters - defines a mapping between Jahia's jnt:user node properties and the corresponding LDAP object attributes.
    Key Default value Description
    groupname.attribute.map cn Group name
    description.attribute.map description Description

    Additional attribute mappings can be specified, using the following pattern for the entry key: "<jahia-group-property-name>.attribute.map" and the name of the corresponding LDAP object attribute as the value.

Customizing user dashboard

The user dashboard is defined as a wrapper for the user object jnt:user. This wrapper is editable in the studio.

By default, we use a tabular list where each tab is, in fact, displaying a specific view of the jnt:user object.

You can define a new view for the type jnt:user in your module and then add a new tab in the wrapper to render it.

As an example, we will show you how the module docspace define a view for the user listing all the docspaces he can access, the path of the template is (docspace/jnt_user/html/user.mydocspaces.jsp).

<template:addResources type="css" resources="docspace.css"/>
<template:addResources type="javascript" resources="jquery.min.js"/>
<div id="${currentNode.identifier}">
    <jcr:sql var="result"
             sql="select * from [jnt:folder] as file order by file.[jcr:lastModified] desc"/>
    <c:set var="currentList" value="${result.nodes}" scope="request"/>
    <c:set var="listTotalSize" value="${functions:length(result.nodes)}" scope="request"/>
    <c:choose>
        <c:when test="${empty param.pagesize}">
            <c:set var="pageSize" value="40"/>
        </c:when>
        <c:otherwise>
            <c:set var="pageSize" value="${param.pagesize}"/>
        </c:otherwise>
    </c:choose>
    <template:initPager totalSize="${listTotalSize}" pageSize="${pageSize}" id="${currentNode.identifier}"/>
    <ul class="docspacelist">
        <c:forEach items="${currentList}" var="subchild" varStatus="status" begin="${begin}" end="${end}">
            <c:if test="${jcr:hasPermission(subchild, 'jcr:write') and (not empty jcr:getParentOfType(subchild, 'jnt:page'))}">
                <li>
                    <a class="adocspace" href="${url.basePreview}${subchild.path}.html"
                       title="${subchild.name}">${functions:abbreviate(subchild.name,20,30,'...')}</a>

                    &nbsp;<span><fmt:message key="label.lastModif"/>:&nbsp;<fmt:formatDate
                        value="${subchild.properties['jcr:lastModified'].date.time}" dateStyle="short"
                        type="both"/></span>
                </li>
            </c:if>
        </c:forEach>
    </ul>
    <template:displayPagination nbItemsList="5,10,20,40,60,80,100,200"/>
    <template:removePager id="${currentNode.identifier}"/>
</div>

Users registration

Optional Module: user_registration

This module declares a component which, once inserted in a page, present a form to users to allow self-registration into the system;

It is possible to notify the relevant administrator(s) that a new user has registered (by email) * The landing page (where the user is redirected after the form is submitted) need to be set.
By default, the following fields are displayed and marked as "mandatory" (this, of course, can be customized into the definition)

  • First Name
  • Last Name
  • Username
  • Password
  • Email

The form can be extended at will, with any additional user property.

<input type="text" name="property_name" id="property_name" value="" tabindex="xx"/>

Registering a user triggers an automatic email to the administrator of the platform, and a welcome email to the newly registered user.

On the server, the Jahia folder User registration folder / mail / templates / contains the template used for these automatic notifications: this template accepts HTML and can be customized easily.

Users and groups service modifications 7.1

Main changes

org.jahia.services.usermanager.JahiaUser and org.jahia.services.usermanager.JahiaGroup most usages have been replaced by JCR node decorators, respectively org.jahia.services.content.decorator.JCRUserNode and org.jahia.services.content.decorator.JCRGroupNode.

 

The session current user and workflow participants are still JahiaUser.

JahiaUser and JahiaGroup are used as well in the new User and Group Providers.

User and group keys are now the node path instead of “{provider}name”.

Something to take care of in the JSPs is that now we manipulate JCR nodes, user properties are JCR properties instead of just String values.

 

Details

Services

  • In the org.jahia.services.usermanager.JahiaUserManagerService class, all methods that previously returned or took in argument a JahiaUser (lookup, user creation/edition, etc) now manipulate JCRUserNode objects.
  • In the org.jahia.services.usermanager.JahiaGroupManagerService class, all methods that previously returned or took in argument a JahiaGroup (lookup, group creation/edition, etc) now manipulate JCRGroupNode objects.

TLDs

  • uiComponents:userList and uiComponents:groupList tags have been removed (they were deprecated)
  • functions:memberOf and functions:notMemberOf need the RenderContext as an additional argument
  • functions:lookupUser (deprecated) / user:lookupUser returns JCRUserNode instead of JahiaUser
  • functions:displayName (deprecated) / user:displayName takes a JCRNodeWrapper instead of a Principal
  • functions:fullName (deprecated) / user:fullName takes a JCRNodeWrapper instead of a Principal
  • user:formatUserValueOption takes a JCRNodeWrapper instead of a Principal
  • user:formatUserTextOption takes a JCRNodeWrapper instead of a Principal
  • user:isReadOnlyProvider takes a JCRNodeWrapper instead of a Principal

Mail templates

  • user and currentUser variables are now JCRUserNode instead of JahiaUser

Removed and deprecated classes

The following classes have been removed:

org.jahia.services.usermanager.jcr.JCRPrincipal
org.jahia.services.usermanager.jcr.JCRUser
org.jahia.services.usermanager.jcr.JCRGroup


org.jahia.services.usermanager.jcr.JCRUserManagerProvider 
org.jahia.services.usermanager.jcr.JCRGroupManagerProvider 

are now deprecated. They’ve been kept to register legacy providers using the User and Group Bridge Provider.

New LDAP provider

The new ldap provider, support all the previous parameters available from the conf property file.

Here is the documentation for the old parameters:

http://www.jahia.com/fr/home/resources/developers-techwiki/users-and-groups/ldap-connector.html

 

In addition, some new parameters are available, to better handle performance/cache/… and other configuration setters.

Here is the list of all the new parameters:

connection pool configuration:

  • Since we used spring ldap it’s possible to use different pool system
  • The choice is between “ldap”, “apache-commons”, “none”
  • ldap: is the default ldap connection pool
  • apache-commons: is the more configurable pool, and it's brought by spring ldap
  • none: to specify that we don’t want a pooled connection

here is the configurations related to this pools:

 

key default desc
ldap.connect.pool apache-commons Type of pool to use : ldap, apache-commons, or none

 Connection parameters for native ldap pool :

key default desc
ldap.connect.pool.debug   A string that indicates the level of debug output to produce. Valid values are "fine" (trace connection creation and removal) and "all" (all debugging information).
ldap.connect.pool.initSize 1 The string representation of an integer that represents the number of connections per connection identity to create when initially creating a connection for the identity.
ldap.connect.pool.maxSize   The string representation of an integer that represents the maximum number of connections per connection identity that can be maintained concurrently
ldap.connect.pool.prefSize   The string representation of an integer that represents the preferred number of connections per connection identity that should be maintained concurrently.
ldap.connect.pool.timeout   The string representation of an integer that represents the number of milliseconds that an idle connection may remain in the pool without being closed and removed from the pool.

 Connection parameters for apache-commons pool :

key default desc
ldap.connect.pool.maxActive 8 The maximum number of active connections of each type (read-only|read-write) that can be allocated from this pool at the same time, or non-positive for no limit.
ldap.connect.pool.maxTotal -1 The overall maximum number of active connections (for all types) that can be allocated from this pool at the same time, or non-positive for no limit.
ldap.connect.pool.maxIdle 8 The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool, without extra ones being released, or non-positive for no limit.
ldap.connect.pool.minIdle 0 The minimum number of active connections of each type (read-only|read-write) that can remain idle in the pool, without extra ones being created, or zero to create none.
ldap.connect.pool.maxWait -1 The maximum number of milliseconds that the pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
ldap.connect.pool.whenExhaustedAction block Specifies the behaviour when the pool is exhausted. The FAIL option will throw a NoSuchElementException when the pool is exhausted. The BLOCK option will wait until a new object is available. If maxWait is positive a NoSuchElementException is thrown if no new object is available after the maxWait time expires. The GROW option will create and return a new object (essentially making maxActive meaningless)
ldap.connect.pool.testOnBorrow false The indication of whether objects will be validated before being borrowed from the pool. If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
ldap.connect.pool.testOnReturn false The indication of whether objects will be validated before being returned to the pool.
ldap.connect.pool.testWhileIdle false The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to validate, it will be dropped from the pool.
ldap.connect.pool.timeBetweenEvictionRunsMillis -1 The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle object evictor thread will be run.
ldap.connect.pool.numTestsPerEvictionRun 3 The number of objects to examine during each run of the idle object evictor thread (if any).
ldap.connect.pool.minEvictableIdleTimeMillis 1000 * 60 * 30 The minimum amount of time an object may sit idle in the pool before it is eligible for eviction by the idle object evictor (if any).


 

Other new props:

 

key default desc
search.attribute.inDn false If set to true, signal that the users and groups ids are contains in the dn. Useful to improve the performance when lookup the members
dynamic.enabled false Set to true if you want to use the dynamic groups

 

Supporting both DX 7.0 and 7.1+

If you are building a module that should work for both DX 7.0 and 7.1+ you could use Reflection API calls to perform method invocations such as these :

protected JCRUserNode getUserNode(JCRSessionWrapper jcrSessionWrapper, String userName) {
  // because of an API change between DF 7.0 and 7.1 we use reflection API to make sure we call the proper
  // method.
  try {
      Method lookupUserMethod = jahiaUserManagerService.getClass().getMethod("lookupUser", String.class);
      if (lookupUserMethod.getReturnType().equals(JahiaUser.class)) {
          // DF 7.0 case
          JahiaUser jahiaUser = (JahiaUser) lookupUserMethod.invoke(jahiaUserManagerService, userName);
          if (jahiaUser != null && jahiaUser instanceof JCRUser) {
              return (JCRUserNode) ((JCRUser) jahiaUser).getNode(jcrSessionWrapper);
          }
      } if (lookupUserMethod.getReturnType().equals(JCRUserNode.class)) {
          JCRUserNode jcrUserNode = (JCRUserNode) lookupUserMethod.invoke(jahiaUserManagerService, userName);
          return jcrUserNode;
      } else {
          logger.error("Unrecognized lookup user method " + lookupUserMethod.toString() + "!");
      }
  } catch (NoSuchMethodException e) {
      logger.error("Error finding lookupUser method !", e);
  } catch (InvocationTargetException e) {
      logger.error("Error invoking lookupUser method !", e);
  } catch (IllegalAccessException e) {
      logger.error("Error invoking lookupUser method !", e);
  } catch (RepositoryException e) {
      logger.error("Error retrieving user JCR node !", e);
  }
  return null;
}

 

Be careful though because the Reflection API has a performance costs so if you intend to make a lot of these types of calls you should probably improve the above code by removing dynamic lookups as much as possible (try using static variables to store the method lookups)

 

As the above method returns a JCRUserNode decorator, some methods are not directly available in DX 7.0 so you might need to put them in your own code, such as the isAccountLocked method :

 

public boolean isAccountLocked(JCRUserNode jcrUserNode) {
  try {
      return !jcrUserNode.isRoot() && jcrUserNode.hasProperty("j:accountLocked") && jcrUserNode.getProperty("j:accountLocked").getBoolean();
  } catch (RepositoryException e) {
      logger.error(e.getMessage(), e);
      return false;
  }
}

 

Here is another example for accessing the getUserMembership() method that has also changed from DX 7.0 to 7.1 :

 

protected List<String> getUserMembership(JahiaUser jahiaUser) {
  // because of an API change between DF 7.0 and 7.1 we use reflection API to make sure we call the proper
  // method.
  try {
      Method getUserMembershipMethod = jahiaGroupManagerService.getClass().getMethod("getUserMembership", JahiaUser.class);
      // we are in the DF 7.0 case
      return (List<String>) getUserMembershipMethod.invoke(jahiaGroupManagerService, jahiaUser);
  } catch (NoSuchMethodException e) {
      // we are in the DF 7.1 case
      try {
          Method getUserMembershipMethod = jahiaGroupManagerService.getClass().getMethod("getUserMembership", String.class, String.class);
          Method jahiaPrincipalMethod = jahiaUser.getClass().getMethod("getRealm");
          String realm = (String) jahiaPrincipalMethod.invoke(jahiaUser);
          return (List<String>) getUserMembershipMethod.invoke(jahiaGroupManagerService, jahiaUser.getUsername(), realm);
      } catch (NoSuchMethodException e1) {
          logger.error("Coudln't find any recognized getUserMembership method !", e1);
      } catch (InvocationTargetException e1) {
          logger.error("Error invoking getUserMembership method !", e1);
      } catch (IllegalAccessException e1) {
          logger.error("Error invoking getUserMembership method !", e1);
      }
  } catch (InvocationTargetException e) {
      logger.error("Error invoking getUserMembership method !", e);
  } catch (IllegalAccessException e) {
      logger.error("Error invoking getUserMembership method !", e);
  }
  return null;
}