Authentication, Users & Groups Management

Table of Contents

13.1. Introduction
13.2. Users and Groups configuration
13.2.1. Schemas
13.2.2. Directories
13.2.3. UserManager
13.2.4. User Management Interface
13.3. Authentication
13.3.1. Authentication Framework Overview
13.3.2. Pluggable JAAS Login Module
13.3.3. Pluggable Web Authentication Filter

13.1. Introduction

In Nuxeo EP, the concept of a user is needed for two main reasons:

  • Users are needed for authentication and authorization to work,

  • Users have associated information that can be displayed, for instance to display someone's full name or email address.

An abstraction, the UserManager, centralizes the way a Nuxeo EP application deals with users (and groups of users). The UserManager is queried by the platform's LoginModule when someone attemps to authenticate against the framework. It is also queried whenever someone wants the last name or email of a user for instance, or to get all users having "Bob" as their first name.

13.2. Users and Groups configuration

The data about users (login, password, name, personal information, etc.) and the groups they belong to (simple members, or any application-related group) are managed through the Directory abstraction. This means that users can be stored in LDAP or in SQL, and groups can be part of the LDAP tree or stored in a SQL table, but the application doesn't see the difference as long as the connectors are configured properly.

13.2.1. Schemas

To define a new source of users, you simply define a new directory (or redefine the default one) to use different connection and schema information. We'll use an example where the pet-name field is added to the user's schema.

Let's start by defining our new schema in a .xsd file, myuser.xsd:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:nxs="http://www.nuxeo.org/ecm/schemas/user"
    targetNamespace="http://www.nuxeo.org/ecm/schemas/user">

  <xs:include schemaLocation="base.xsd" />

  <xs:element name="username" type="xs:string" />
  <xs:element name="password" type="xs:string" />
  <xs:element name="email" type="xs:string" />
  <xs:element name="firstName" type="xs:string" />
  <xs:element name="lastName" type="xs:string" />
  <xs:element name="company" type="xs:string" />

  <xs:element name="petName" type="xs:string" />

  <!-- inverse reference -->
  <xs:element name="groups" type="nxs:stringList" />

</xs:schema>

This schema must be registered in an extension point:

<extension target="org.nuxeo.ecm.core.schema.TypeService" point="schema">
  <schema name="myuser" src="myuser.xsd" />
</extension>

TODO: FG groups

13.2.2. Directories

The user schema can now be used when we define a new directory, MyUserDirectory. A SQL directory is defined like this:

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

    <schema>myuser</schema>
    <idField>username</idField>
    <passwordField>password</passwordField>

    <dataSource>java:/nxsqldirectory</dataSource>
    <table>myusers</table>
    <dataFile>myusers.csv</dataFile>
    <createTablePolicy>on_missing_columns</createTablePolicy>

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

  </directory>
</extension>

And we can provide a file, myusers.csv, which will be used to populate the table if it is missing:

username, password, firstName, lastName, company, email, petName
bob,bobSecret,Bob,Doe,ACME,[email protected],Lassie

If instead we had used an LDAP directory, the configuration would look like:

<extension target="org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory" point="servers">
  <server name="default">
    <ldapUrl>ldap://localhost:389</ldapUrl>
    <bindDn>cn=manager,dc=example,dc=com</bindDn>
    <bindPassword>secret</bindPassword>
  </server>
</extension>

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

    <schema>myuser</schema>
    <idField>username</idField>
    <passwordField>password</passwordField>

    <server>default</server>
    <searchBaseDn>ou=people,dc=example,dc=com</searchBaseDn>
    <searchClass>inetOrgPerson</searchClass>
    <searchScope>subtree</searchScope>

    <fieldMapping name="username">uid</fieldMapping>
    <fieldMapping name="password">userPassword</fieldMapping>
    <fieldMapping name="email">mail</fieldMapping>
    <fieldMapping name="firstName">givenName</fieldMapping>
    <fieldMapping name="lastName">sn</fieldMapping>
    <fieldMapping name="company">o</fieldMapping>

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

  </directory>
</extension> 

Detailed configuration on SQL Directories and LDAP Directories can be found in Chapter 19, Directories and Vocabularies.

13.2.3. UserManager

We can now tell the UserManager that this directory should be the one to use when dealing with users:

<extension target="org.nuxeo.ecm.platform.usermanager.UserService" point="userManager">
  <userManager>

    <users>
      <directory>MyUserDirectory</directory>
      <emailField>email</emailField>
      <searchFields append="true">
        <searchField>username</searchField>
        <searchField>firstName</searchField>
        <searchField>lastName</searchField>
        <searchField>myfield</searchField>
      </searchFields>
    </users>

  </userManager>
</extension>

This configuration also sets the email field, and search fields that have to be queried when searching for users. It can be completed to set the anonmymous user, add virtual users, or set the group directory properties.

<extension target="org.nuxeo.ecm.platform.usermanager.UserService" point="userManager">
  <userManager>

    <users>

      <directory>MyUserDirectory</directory>
      <emailField>email</emailField>
      <searchFields append="true">
        <searchField>username</searchField>
        <searchField>firstName</searchField>
        <searchField>lastName</searchField>
        <searchField>myfield</searchField>
      </searchFields>
      <listingMode>tabbed</listingMode>

      <anonymousUser id="Anonymous">
        <property name="firstName">Anonymous</property>
        <property name="lastName">User</property>
      </anonymousUser>
      <virtualUser id="MyCustomAdministrator" searchable="false">
        <password>secret</password>
        <property name="firstName">My Custom</property>
        <property name="lastName">Administrator</property>
        <group>administrators</group>
      </virtualUser>
      <virtualUser id="MyCustomMember" searchable="false">
        <password>secret</password>
        <property name="firstName">My Custom</property>
        <property name="lastName">Member</property>
        <group>members</group>
        <group>othergroup</group>
        <propertyList name="listprop">
          <value>item1</value>
          <value>item2</value>
        </propertyList>
      </virtualUser>
      <virtualUser id="ExistingVirtualUser" remove="true" />

    </users>

    <defaultAdministratorId>Administrator</defaultAdministratorId>
    <userSortField>lastName</userSortField>
    <userPasswordPattern>^[a-zA-Z0-9]{5,}$</userPasswordPattern>

    <groups>
      <directory>somegroupdir</directory>
      <membersField>members</membersField>
      <subGroupsField>subgroups</subGroupsField>
      <parentGroupsField>parentgroup</parentGroupsField>
      <listingMode>search_only</listingMode>
    </groups>
    <defaultGroup>members</defaultGroup>
    <groupSortField>groupname</groupSortField>

  </userManager>
</extension>

The anonymous user represents a special kind of virtual user, used to reprensent users that do not need to log in the application. This feature is used in conjunction with the anonymous plugin (see next chapter).

Virtual users can be added for authentication. Properties are used to create the appropriate model as if user was retrieved from the user directory. This is a convenient way to add custom users to the application when the user directory (using LDAP for instance) cannot be modified. Virtual users with the "administrators" group will have the same rights than the default administrator.

The default administrator id can be set either to an existing or virtual user.

The group directory can also be configured to define the groups hierarchy and the contained users. This configuration has to match the user directory inverse references.

Every authenticated user will be placed in the configured default group. This group does not need to exist in the backing group directory, nor does any other group listed in virtual users configuration.

13.2.4. User Management Interface

Since 5.2.GA, The default users and groups management pages use some layouts for display. If you're using custom schemas and would like to display your new fields, or would like to change the default display, you can redefine layouts names "user" and "group" by contributing new layouts with this name. See chapter Chapter 8, Layouts for more information about layouts configuration.

Do not forget to put "<require>org.nuxeo.ecm.platform.forms.layouts.webapp</require>" on your layout contribution to ensure default layouts are overriden.

<?xml version="1.0"?>

<component name="org.nuxeo.ecm.platform.forms.layouts.usersAndGroups">

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

    <layout name="user">
      <templates>
        <template mode="any">/layouts/layout_default_template.xhtml</template>
      </templates>
      <rows>
        <row>
          <widget>username</widget>
        </row>
        <row>
          <widget>firstname</widget>
        </row>
        <row>
          <widget>lastname</widget>
        </row>
        <row>
          <widget>company</widget>
        </row>
        <row>
          <widget>email</widget>
        </row>
        <row>
          <widget>firstPassword</widget>
        </row>
        <row>
          <widget>secondPassword</widget>
        </row>
        <row>
          <widget>passwordMatcher</widget>
        </row>
        <row>
          <widget>groups</widget>
        </row>
      </rows>
      <widget name="username" type="text">
        <labels>
          <label mode="any">username</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">username</field>
        </fields>
        <widgetModes>
          <mode value="create">edit</mode>
          <mode value="editPassword">hidden</mode>
          <mode value="any">view</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="required">true</property>
          <property name="styleClass">dataInputText</property>
          <property name="validator">
            #{userManagerActions.validateUserName}
          </property>
        </properties>
      </widget>
      <widget name="firstname" type="text">
        <labels>
          <label mode="any">firstName</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">firstName</field>
        </fields>
        <widgetModes>
          <mode value="editPassword">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="lastname" type="text">
        <labels>
          <label mode="any">lastName</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">lastName</field>
        </fields>
        <widgetModes>
          <mode value="editPassword">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="company" type="text">
        <labels>
          <label mode="any">company</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">company</field>
        </fields>
        <widgetModes>
          <mode value="editPassword">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="email" type="text">
        <labels>
          <label mode="any">email</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">email</field>
        </fields>
        <widgetModes>
          <mode value="editPassword">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="required">true</property>
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="firstPassword" type="secret">
        <labels>
          <label mode="any">password</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">password</field>
        </fields>
        <widgetModes>
          <mode value="create">edit</mode>
          <mode value="editPassword">edit</mode>
          <mode value="any">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="required">true</property>
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="secondPassword" type="secret">
        <labels>
          <label mode="any">password.verify</label>
        </labels>
        <translated>true</translated>
        <widgetModes>
          <mode value="create">edit</mode>
          <mode value="editPassword">edit</mode>
          <mode value="any">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <property name="required">true</property>
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="passwordMatcher" type="template">
        <labels>
          <label mode="any"></label>
        </labels>
        <translated>true</translated>
        <widgetModes>
          <mode value="create">edit</mode>
          <mode value="editPassword">edit</mode>
          <mode value="any">hidden</mode>
        </widgetModes>
        <properties widgetMode="edit">
          <!-- XXX: depends on firstPassword and secondPassword widget names -->
          <property name="template">
            /widgets/user_password_validation_widget_template.xhtml
          </property>
        </properties>
      </widget>
      <widget name="groups" type="template">
        <labels>
          <label mode="any">label.userManager.userGroups</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="user">groups</field>
        </fields>
        <widgetModes>
          <mode value="edit">
            #{nxu:test(currentUser.administrator, 'edit', 'view')}
          </mode>
          <mode value="editPassword">hidden</mode>
        </widgetModes>
        <properties widgetMode="any">
          <property name="template">
            /widgets/user_suggestion_widget_template.xhtml
          </property>
          <property name="userSuggestionSearchType">GROUP_TYPE</property>
        </properties>
      </widget>
    </layout>

    <layout name="group">
      <templates>
        <template mode="any">/layouts/layout_default_template.xhtml</template>
      </templates>
      <rows>
        <row>
          <widget>groupname</widget>
        </row>
        <row>
          <widget>members</widget>
        </row>
        <row>
          <widget>subgroups</widget>
        </row>
      </rows>
      <widget name="groupname" type="text">
        <labels>
          <label mode="any">label.groupManager.groupName</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="group">groupname</field>
        </fields>
        <widgetModes>
          <mode value="create">edit</mode>
          <mode value="any">hidden</mode>
        </widgetModes>
        <properties widgetMode="any">
          <property name="required">true</property>
          <property name="styleClass">dataInputText</property>
        </properties>
      </widget>
      <widget name="members" type="template">
        <labels>
          <label mode="any">label.groupManager.userMembers</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="group">members</field>
        </fields>
        <properties widgetMode="any">
          <property name="template">
            /widgets/user_suggestion_widget_template.xhtml
          </property>
          <property name="userSuggestionSearchType">USER_TYPE</property>
        </properties>
      </widget>
      <widget name="subgroups" type="template">
        <labels>
          <label mode="any">label.groupManager.groupMembers</label>
        </labels>
        <translated>true</translated>
        <fields>
          <field schema="group">subGroups</field>
        </fields>
        <properties widgetMode="any">
          <property name="template">
            /widgets/user_suggestion_widget_template.xhtml
          </property>
          <property name="userSuggestionSearchType">GROUP_TYPE</property>
        </properties>
      </widget>
    </layout>

  </extension>

</component>

      

Example 13.1. Sample layout contribution for users and groups management.


Before 5.2.GA, you need to redefine the deprecated layout configuration of two standard document types, User and UserCreate (which are used in the default user management screens and backing beans) to add our new field:

<extension target="org.nuxeo.ecm.platform.types.TypeService" point="types">

  <type id="User" coretype="User">
    <label>User</label>
    <icon>/icons/user.gif</icon>
    <default-view>view_user</default-view>
    <layout>
      <widget schemaname="myuser" fieldname="username"
        jsfcomponent="h:inputTextReadOnly" />
      <widget schemaname="myuser" fieldname="firstName"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="lastName"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="email"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="company"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="petName"
        jsfcomponent="h:inputText" />
    </layout>
  </type>

  <type id="UserCreate" coretype="UserCreate">
    <label>UserCreate</label>
    <icon>/icons/user.gif</icon>
    <default-view>create_user</default-view>
    <layout>
      <widget schemaname="myuser" fieldname="username"
        jsfcomponent="h:inputText" required="true" />
      <widget schemaname="myuser" fieldname="password"
        jsfcomponent="h:inputSecret" required="true" />
      <widget schemaname="myuser" fieldname="firstName"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="lastName"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="email"
        jsfcomponent="h:inputText" required="true" />
      <widget schemaname="myuser" fieldname="company"
        jsfcomponent="h:inputText" />
      <widget schemaname="myuser" fieldname="petName"
        jsfcomponent="h:inputText" />
    </layout>
  </type>

</extension>

13.3. Authentication

13.3.1. Authentication Framework Overview

Nuxeo Authentication is based on the JAAS standard.

Authentication infrastructure is based on 2 main components :

  • a JAAS Login Module: NuxeoLoginModule

  • a Web Filter: NuxeoAuthenticationFilter

Users and groups are managed via the UserManagerService that handles the indirection to users and groups directories (SQL or LDAP).

Nuxeo authentication framework is pluggable so that you can contribute new plugin and don't have to rewrite and reconfigure a complete JAAS infrastructure.

13.3.2. Pluggable JAAS Login Module

NuxeoLoginModule is a JAAS LoginModule. It is responsible for handling all login call within Nuxeo's security domains:

  • nuxeo-ecm: for the service stack and the core

  • nuxeo-ecm-web: for the web application on the top of the service stack

On JBoss application server, the JBoss Client Login module is used to propagate security between the web part and the service stack.

Here is the default JBoss security configuration:

 <domain name="nuxeo-ecm-web">
      <login-module code = "org.nuxeo.ecm.platform.login.NuxeoLoginModule"
             flag = "required">
              <option name="principalClassName">org.nuxeo.ecm.platform.login.NuxeoPrincipal</option>
              <option name="useUserIdentificationInfoCB">true</option>
          </login-module>
       <login-module code="org.jboss.security.ClientLoginModule" flag="required">
            <option name="password-stacking">true</option>
            <option name="restore-login-identity">true</option>
            <option name="multi-threaded">true</option>
       </login-module>
 </domain>
 <domain name="nuxeo-ecm">
        <login-module code = "org.nuxeo.ecm.platform.login.NuxeoLoginModule" flag = "required">
              <option name="principalClassName">org.nuxeo.ecm.platform.login.NuxeoPrincipal</option>
              <option name="useUserIdentificationInfoCB">true</option>
         </login-module>
 </domain>

As shown by this configuration, the principals returned by NuxeoLoginModule is org.nuxeo.ecm.platform.login.NuxeoPrincipal.

Each protected service declares the nuxeo-ecm security domain

<?xml version="1.0" encoding="UTF-8"?>
<jboss>
  <enterprise-beans>
    <session>
        <ejb-name>DocumentManagerBean</ejb-name>
        <security-domain>nuxeo-ecm</security-domain>
    </session>
  </enterprise-beans>
</jboss>

13.3.2.1. NuxeoLoginModule

NuxeoLoginModule mainly handles 2 tasks:

  • login user

    This means extract information from the CallBack stack and validate identity.

    NuxeoLoginModule supports several types of CallBacks (including Nuxeo specific CallBack) and uses a plugin system to be able to validate user identity in a pluggable way.

  • Principal creation

    For that NuxeoLoginModule uses Nuxeo UserManager service that does the indirection to the users/groups directories.

When used in conjonction with UserIdentificationInfoCallback (Nuxeo custom CallBack system), the LoginModule will choose the right LoginPlugin according to the CallBack information.

13.3.2.2. NuxeoLoginModule Plugins

Because validating User identity can be more complex that just checking login/password, NuxeoLoginModule exposes an extension point to contribute new LoginPlugins

Each LoginPlugin has to implement the org.nuxeo.ecm.platform.login.LoginPlugin interface.

Main method is:

 String validatedUserIdentity(UserIdentificationInfo userIdent)

that is used to validate the UserIdentificationInfo.

Typically, default implementation will extract Login/Password from UserIdentificationInfo and call the checkUsernamePassword against the UserManager that will validate this information against the users directory.

Other plugins can use other informations carried by UserIdentificationInfo (token, ticket ...) to validate the identity against an external SSO system. The UserIdentificationInfo also carries the LoginModule plugin name that must be used to validate identity. Even if technically, a lot of SSO system could be implemented using this plugin system, most SSO implementations have be moved to the Authentication Plugin at the Web Filter level, because they need a http dialog.

For now, the NuxeoLoginModule has only two way to handle validateUserIdentity:

  • default

    Uses UserManager

  • Trusted_LM

    This plugin assumes the user identity has already been validated by the authentication filter, so the validatedUserIdentity will always return true. Using this LoginModule plugin, a user will be logged if the user exists in the UserManager. This plugin is used for most SSO system in conjonction with a Authentication plugin that will actually do the work of validating password or token.

13.3.2.3. Remote Login to the EJB layer

The Login system can be used via Remote EJB calls.

You can login as System user:

LoginContext lc = Framework.login();
// do some service calls
lc.logout();      

You can login using user / password:

LoginContext lc = Framework.login(userName, password);
// do some service calls
lc.logout();      

You can also call the login method and pass it directly a CallBackHandler. This can be used in conjonction with org.nuxeo.ecm.platform.api.login.UserIdentificationInfoCallbackHandler.

13.3.3. Pluggable Web Authentication Filter

The Web Authentication filter is responsible for:

  • guarding access to web resources

    The filter can be parametrized to guard urls with a given pattern

  • finding the right plugin to get user identification information

    This can be getting a userName/Password, getting a token in a cookie or a header, redirecting user to another authentication server.

  • create the LoginContext

    This means creating the needed callBacks and call the JAAS Login

  • store and reestablish login context

    In order to avoid recreating a login context for each request, the LoginContext is cached.

13.3.3.1. NuxeoAuthenticationFilter

The NuxeoAuthenticationFilter is one of the top level filter in Nuxeo Web Filters stack.

For each request it will try to find a existing LoginContext and create a RequestWrapper that will carry the NuxeoPrincipal.

If no existing LoginContext is found it will try to prompt the client for authentication information and will establish the login context.

If order to execute the task of prompting the client and retrieving UserIndetificationInfo, the filter will rely on a set of configured plugins.

Each plugin must:

  • Implement org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin

    The two main methods are:

    Boolean handleLoginPrompt(HttpServletRequest httpRequest,HttpServletResponse httpResponse, String baseURL);
    UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest, HttpServletResponse httpResponse);

  • Define the LoginModule plugin to use if needed

    Typically, SSO AuthenticationPlugin will do all the work and will use the Trusted_LM LoginModule Plugin.

  • Define if stating URL must be saved

    AuthenticationPlugins that uses HTTP redirect in order to do the login prompt will let the Filter store the first accessed URL in order to cleanly redirect the user to the page he asked after the authentication is successful.

Additionnaly, AuthenticationPlugin can also implement the org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPluginLogoutExtension interface if a specific processing must be done when logging out.

Here is a sample XML descriptor for registering an AuthenticationPlugin:

<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.ui.web.auth.defaultConfig">
  <extension
    target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
    point="authenticators">

    <authenticationPlugin name="FORM_AUTH" enabled="true"
      class="org.nuxeo.ecm.platform.ui.web.auth.plugins.FormAuthenticator">
      <needStartingURLSaving>true</needStartingURLSaving>
      <parameters>
        <parameter name="LoginPage">login.jsp</parameter>
        <parameter name="UsernameKey">user_name</parameter>
        <parameter name="PasswordKey">user_password</parameter>
      </parameters>
    </authenticationPlugin>
   </extension>
</component>

As you can see in the above example, the descriptor contains the parameters tag that can be used to embed arbitrary additional configuration that will be specific to a given AuthenticationPlugin. In the above example, it is used to define the field names and the JSP file used for form based authentication.

NuxeoAuthenticationFilter supports several authentication system. This is, for example, useful for having users using Form based authentication and having RSS clients using Basic Authentication. Because of that AuthenticationPlugin must be ordered. For that purpose, NuxeoAuthenticationFilter uses a dedicated extension point that let you define the AuthenticationChain.

<component name="Anonymous.auth.activation">
   <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
    <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="chain">

    <authenticationChain>
      <plugins>
        <plugin>BASIC_AUTH</plugin>
        <plugin>ANONYMOUS_AUTH</plugin>
        <plugin>FORM_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
</component>

The NuxeoAuthenticationFilter will use this chain to trigger the login prompt. When authentication is needed, the Filter will call, in a first round, the handleRetrieveIdentity method on all the plugins in the order of the authentication chain and then, in a second round, the handleLoginPrompt method in the same order on all the plugins if the authentication could not be achieved in the first round. The aim is to have as much automatic authentications as possible, that's why all the manual authentications (those which need a prompt) are done in a second round.

Some authentication plugins may choose to trigger or not the LoginPrompt depending on the situation. For example: the BasicAuthentication plugin generates the login prompt (in the case of the BasicAuthentication plugin the login prompt is an HTTP basic authentication which takes the form of a popup) only for specific URLs used for RSS feeds or restlet calls. This allows the platform to be easily called by Restlets and RSS clients without bothering browser clients who are presented with web forms to authenticate.

13.3.3.2. Built-in Authentication Plugins

NuxeoAuthenticationFilter comes with two built-in authentication plugins:

  • FORM_AUTH: Form based Authentication

    This is a standard form based authentication. Current implementation let you configure the name of the Login and Password fields, and the name of the page used to display the login page

  • BASIC_AUTH: Basic HTTP Authentication

    This plugin supports standard HTTP Basic Authentication. By default, this plugin only generates the authentication prompt on configured URLs.

There are also additional components that provides other Authentication plugins (see below).

13.3.3.3. Additional Authentication Plugins

Nuxeo provides a set of other authentication plugins that are not installed by default with the standard Nuxeo EP setup. These plugins can be downloaded and installed separately.

CAS2 Authentication

This plugin implements a client for CAS SSO system (Central Authentication System). It can be configured to use a CAS proxy. It has been tested and reported to work with CAS V2.

It's easy to test this plugin by installing the JA-SIG Central Authentication Service Open Source CAS server.

To install this authentication plugin, you need to:

  • be sure that there is a CAS server already setup and running

  • download the nuxeo-platform-login-cas2 plugin

  • put it in $JBOSS_HOME/server/default/deploy/nuxeo.ear/plugins and restart the server

  • configure the CAS2 descriptor

  • put CAS2 plugin into the authentication chain

In order to configure CAS2 Auth, you need to create an XML configuration file into $JBOSS_HOME/server/default/deploy/nuxeo.ear/config

Here is a sample file named CAS2-config.xml.

<component name="MyAPP.Cas2SSO">
   <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
   <require>org.nuxeo.ecm.platform.login.Cas2SSO</require>

   <!--  configure you CAS server parameters -->
   <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="authenticators">
      <authenticationPlugin
              name="CAS2_AUTH">
       <loginModulePlugin>Trusting_LM</loginModulePlugin>
       <parameters>
         <parameter name="ticketKey">ticket</parameter>
         <parameter name="appURL">http://127.0.0.1:8080/nuxeo/nxstartup.faces</parameter>
         <parameter name="serviceLoginURL">http://127.0.0.1:8080/cas/login</parameter>
         <parameter name="serviceValidateURL">http://127.0.0.1:8080/cas/serviceValidate</parameter>
         <parameter name="serviceKey">service</parameter>
         <parameter name="logoutURL">http://127.0.0.1:8080/cas/logout</parameter>
       </parameters>
      </authenticationPlugin>
  </extension>

   <!-- Include CAS2 into authentication chain -->
    <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="chain">
    <authenticationChain>
      <plugins>
        <plugin>BASIC_AUTH</plugin>
        <plugin>CAS2_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
</component>

Tip: If while authenticating on the CAS server you get in the logs the following exception, it simply means that the user JOEUSER does not exist in the Nuxeo directory and does not mean that the CAS process is not working.

ERROR [org.nuxeo.ecm.platform.login.NuxeoLoginModule] createIdentity failed
javax.security.auth.login.LoginException: principal JOEUSER does not exist
        at org.nuxeo.ecm.platform.login.NuxeoLoginModule.createIdentity(NuxeoLoginModule.java:304)
        at org.nuxeo.ecm.platform.login.NuxeoLoginModule.validateUserIdentity(NuxeoLoginModule.java:362)
        at org.nuxeo.ecm.platform.login.NuxeoLoginModule.getPrincipal(NuxeoLoginModule.java:216)
        at org.nuxeo.ecm.platform.login.NuxeoLoginModule.login(NuxeoLoginModule.java:271)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:769)
        at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186)
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
        at javax.security.auth.login.LoginContext.login(LoginContext.java:579)
        at org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.doAuthenticate(NuxeoAuthenticationFilter.java:205)
        
PROXY_AUTH: Proxy based Authentication

This plugin assumes Nuxeo is behind a authenticating reverse proxy that transmit user identity using HTTP headers. This modules has be used on projects that uses a apache reverse proxy using client certificates to authenticate. SSO system (Central Authentication System V2):

To install this authentication plugin, you need to:

  • download the nuxeo-platform-login-mod_sso plugin

  • put it in $JBOSS_HOME/server/default/deploy/nuxeo.ear/plugins and restart the server

  • configure the plugin via an XML descriptor

  • put the plugin into the authentication chain

In order to configure this plugin, you need to create an XML configuration file into $JBOSS_HOME/server/default/deploy/nuxeo.ear/config

Here is a sample file named proxy-auth-config.xml.

<component name="MyAPP.Mod_sso">
   <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
   <require>org.nuxeo.ecm.platform.login.Proxy</require>

   <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="authenticators">
      <authenticationPlugin
              name="PROXY_AUTH">
       <loginModulePlugin>Trusting_LM</loginModulePlugin>
       <parameters>
         <!-- configure here the name of the http header that is used to retrieve user identity -->
         <parameter name="ssoHeaderName">remote_user</parameter>
       </parameters>
      </authenticationPlugin>
  </extension>

   <!-- Include Proxy Auth into authentication chain -->
    <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="chain">
    <authenticationChain>
      <plugins>
        <!--  Keep basic Auth at top of Auth chain to support RSS access via BasicAuth -->
        <plugin>BASIC_AUTH</plugin>
        <plugin>PROXY_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
</component>
NTLM_AUTH: NTLM and IE challenge/response authentication

This plugin uses JCIFS to handle NTLM authentication.

This plugging was partially contributed by Nuxeo EP users and has been reported to work by several users.

If you have troubles with latest version of IE on POST requests, please see JCIFS instructions on that (http://jcifs.samba.org/src/docs/ntlmhttpauth.html#post).

To install this authentication plugin, you need to :

  • download the nuxeo-platform-login-ntlm plugin

  • put it in $JBOSS_HOME/server/default/deploy/nuxeo.ear/plugins and restart the server

  • configure the plugin via an XML descriptor

  • put the plugin into the authentication chain

In order to configure this plugin, you need to create an XML configuration file into $JBOSS_HOME/server/default/deploy/nuxeo.ear/config

Here is a sample file named ntlm-auth-config.xml.

<component name="MyAPP.NTLM">
   <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
   <require>org.nuxeo.ecm.platform.login.NTLM</require>

   <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="authenticators">
      <authenticationPlugin
              name="NTLM_AUTH">
       <loginModulePlugin>Trusting_LM</loginModulePlugin>
       <parameters>
          <!-- Add here parameters for you domain, please ee http://jcifs.samba.org/src/docs/ntlmhttpauth.html 
         <parameter name="jcifs.http.domainController">MyControler</parameter>
          -->
       </parameters>

      </authenticationPlugin>
  </extension>

   <!-- Include NTLM Auth into authentication chain -->
    <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="chain">
    <authenticationChain>
      <plugins>
        <plugin>BASIC_AUTH</plugin>
        <plugin>NTLM_AUTH</plugin>
        <plugin>FORM_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
</component>
PORTAL_AUTH: SSO implementation for portal clients

This plugin provides a way to handle identity propagation between an external application and Nuxeo. It was coded in order to propagate user identify between a JSR168 portal and a Nuxeo server. See the Nuxeo-Http-client-library for more information.

To install this authentication plugin, you need to :

  • download the nuxeo-platform-login-portal-sso plugin

  • put it in $JBOSS_HOME/server/default/deploy/nuxeo.ear/plugins and restart the server

  • configure the plugin via an XML descriptor

  • put the plugin into the authentication chain

In order to configure this plugin, you need to create an XML configuration file into $JBOSS_HOME/server/default/deploy/nuxeo.ear/config

Here is a sample file named portal-auth-config.xml.

<component name="MyAPP.postal_sso">
   <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
   <require>org.nuxeo.ecm.platform.login.Portal</require>

   <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="authenticators">
      <authenticationPlugin
              name="PORTAL_AUTH">
       <loginModulePlugin>Trusting_LM</loginModulePlugin>
       <parameters>
         <!-- define here shared secret between the portal and Nuxeo server -->
         <parameter name="secret">nuxeo5secretkey</parameter>
         <parameter name="maxAge">3600</parameter>
       </parameters>
      </authenticationPlugin>
  </extension>

   <!-- Include Portal Auth into authentication chain -->
    <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="chain">
    <authenticationChain>
      <plugins>
        <!--  Keep basic Auth at top of Auth chain to support RSS access via BasicAuth -->
        <plugin>BASIC_AUTH</plugin>
        <plugin>PORTAL_AUTH</plugin>
        <plugin>FORM_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
</component>
ANONYMOUS_AUTH: Anonymous authentication plugin

This plugin provides anonymous authentication. Users are automatically logged as a configurable Anonymous user. This modules also includes additional actions (to be able to login when already logged as Anonymous) and a dedicated Exception handling (to automatically redirect Anonymous users to login screen after a security error).

To install this authentication plugin, you need to :

  • download the nuxeo-platform-login-anonymous plugin

  • put it in $JBOSS_HOME/server/default/deploy/nuxeo.ear/plugins and restart the server

  • configure the plugin via an XML descriptor (define who the anonymous user will be)

  • put the plugin into the authentication chain

In order to configure this plugin, you need to create an XML configuration file into $JBOSS_HOME/server/default/deploy/nuxeo.ear/config

Here is a sample file named anonymous-auth-config.xml.

<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.login.anonymous.config">

  <!-- Make sure these components are read first -->
  <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
  <require>org.nuxeo.ecm.platform.login.anonymous</require>
 
  <!-- Add an Anonymous user -->
  <extension target="org.nuxeo.ecm.platform.usermanager.UserService"
    point="userManager">
    <userManager>
      <users>
        <anonymousUser id="Guest">
          <property name="firstName">Guest</property>
          <property name="lastName">User</property>
        </anonymousUser>
      </users>
    </userManager>
  </extension>
  
  <!-- Override the default authentication chain present in
    nuxeo-platform-ui-web to add ANONYMOUS_AUTH. -->
  <extension
    target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
    point="chain">
    <authenticationChain>
      <plugins>
        <plugin>BASIC_AUTH</plugin>
        <plugin>ANONYMOUS_AUTH</plugin>
        <plugin>FORM_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
</component>