JBoss.orgCommunity Documentation
The eXo Core is a set of common services, such as Authentication and Security, Organization, Database, Logging, JNDI, LDAP, Document reader, and other services, that are used by eXo products and modules. It also can be used in the business logic.
Database creator DBCreator
is responsible for execution
DDL script in runtime. A DDL script may contain templates for database name,
user name and password which will be replaced by real values at execution
time.
Three templates supported:
${database}
for database name;
${username}
for user name;
${password}
for user's password;
Service provide method for execute script for new database creation.
Database name which are passed as parameter will be substituted in DDL
script instead of ${database}
template. Returns
DBConnectionInfo
object (with all neccesary information of
new database's connection) or throws DBCreatorException
exception if any errors occurs in other case.
public DBConnectionInfo createDatabase(String dbName) throws DBCreatorException;
For MSSQL and Sybase servers, use autocommit mode to set true for connection. It's due to after execution "create database" command newly created database not available for "use" command and therefore you can't create new user inside database per one script.
public DBConnectionInfo getDBConnectionInfo(String dbName) throws DBCreatorException;
Return database connection information without database creation.
Service's configuration.
<component> <key>org.exoplatform.services.database.creator.DBCreator</key> <type>org.exoplatform.services.database.creator.DBCreator</type> <init-params> <properties-param> <name>db-connection</name> <description>database connection properties</description> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/" /> <property name="username" value="root" /> <property name="password" value="admin" /> <property name="additional_property" value="value"> ... <property name="additional_property_n" value="value"> </properties-param> <properties-param> <name>db-creation</name>. <description>database creation properties</description>. <property name="scriptPath" value="script.sql" /> <property name="username" value="testuser" /> <property name="password" value="testpwd" /> </properties-param> </init-params> </component>
db-connection
properties section contains parameters
needed for connection to database server
There is four reserved and mandatory properties driverClassName, url, username and password. But db-connection may contain additonal properties.
For example, next additional proprites allows reconnect to MySQL database when connection was refused:
<properties-param> <name>db-connection</name> ... <property name="validationQuery" value="select 1"/> <property name="testOnReturn" value="true"/> ... </properties-param>
db-creation
properties section contains paramaters
for database creation using DDL script:
scriptPath
: absolute path to DDL script
file;
username:
user name for substitution
${username}
template in DDL script;
password
: user's password for substitution
${password}
template in DDL script;
Specific db-connection
properties section for
different databases.
MySQL:
<property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/" /> <property name="username" value="root" /> <property name="password" value="admin" />
PostgreSQL:
<property name="driverClassName" value="org.postgresql.Driver" /> <property name="url" value="jdbc:postgresql://localhost/" /> <property name="username" value="root" /> <property name="password" value="admin" />
PostgrePlus:
<property name="driverClassName" value="com.edb.Driver" /> <property name="url" value="jdbc:edb://localhost/" /> <property name="username" value="root" /> <property name="password" value="admin" />
MSSQL:
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/> <property name="url" value="jdbc:sqlserver://localhost:1433;"/> <property name="username" value="root"/> <property name="password" value="admin"/>
Sybase:
<property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDriver" /> <property name="url" value="jdbc:sybase:Tds:localhost:5000/"/> <property name="username" value="root"/> <property name="password" value="admin"/>
Oracle:
<property name="driverClassName" value="oracle.jdbc.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@db2.exoua-int:1521:orclvm" /> <property name="username" value="root" /> <property name="password" value="admin" />
MySQL:
CREATE DATABASE ${database}; USE ${database}; CREATE USER '${username}' IDENTIFIED BY '${password}'; GRANT SELECT,INSERT,UPDATE,DELETE ON ${database}.* TO '${username}';
PostgreSQL:
CREATE USER ${username} WITH PASSWORD '${password}'; CREATE DATABASE ${database} WITH OWNER ${username};
MSSQL:
USE MASTER; CREATE DATABASE ${database}; USE ${database}; CREATE LOGIN ${username} WITH PASSWORD = '${password}'; CREATE USER ${username} FOR LOGIN ${username};
Sybase:
sp_addlogin ${username}, ${password}; CREATE DATABASE ${database}; USE ${database}; sp_adduser ${username};
Oracle:
CREATE TABLESPACE "${database}" DATAFILE '/var/oracle_db/orclvm/${database}' SIZE 10M AUTOEXTEND ON NEXT 6M MAXSIZE UNLIMITED LOGGING EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO; CREATE TEMPORARY TABLESPACE "${database}.TEMP" TEMPFILE '/var/oracle_db/orclvm/${database}.temp' SIZE 5M AUTOEXTEND ON NEXT 5M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M; CREATE USER "${username}" PROFILE "DEFAULT" IDENTIFIED BY "${password}" DEFAULT TABLESPACE "${database}" TEMPORARY TABLESPACE "${database}.TEMP" ACCOUNT UNLOCK; GRANT CREATE SEQUENCE TO "${username}"; GRANT CREATE TABLE TO "${username}"; GRANT CREATE TRIGGER TO "${username}"; GRANT UNLIMITED TABLESPACE TO "${username}"; GRANT "CONNECT" TO "${username}"; GRANT "RESOURCE" TO "${username}";
The purpose is to make a simple, unified way for the authentication and the storing/propagation of user sessions through all the eXo components and J2EE containers. JAAS is supposed to be the primary login mechanism but the Security Service framework should not prevent other (custom or standard) mechanisms from being used. You can learn more about JAAS in the Java Tutorial
The central point of this framework is the ConversationState object which stores all information about the state of the current user (very similar to the Session concept). The same ConversationState also stores acquired attributes of an Identity which is a set of principals to identify a user.
The ConversationState has definite lifetime. This object should be created when the user's identity becomes known by eXo (login procedure) and destroyed when the user leaves an eXo based application (logout procedure). Using JAAS it should happen in LoginModule's login() and logout() methods respectively.
The ConversationState can be stored
In a static local thread variable, or
As a key-value pair in the ConversationRegistry component.
One or the other, or both methods can be used to set/retrieve the state at runtime. The most important thing is that they should be complementary, i.e. make sure that the conversation state is set before you try to use it.
Local Thread Variable: Storing the ConversationState in a static local thread variable makes it possible to represent it as a context (current user's state).
ConversationState.setCurrent(conversationState); .... ConversationState.getCurrent();
Key-Value way
If you store the ConversationState inside the ConversationRegistry component as a set of key-value pairs, the session key is an arbitrary String (user name, ticket id, httpSessionId etc).
conversationRegistry.register("key", conversationState); ... conversationRegistry.getState("key");
ConversationRegistry The ConversationRegistry is a mandatory component deployed into eXo Container as following:
<component> <type>org.exoplatform.services.security.ConversationRegistry</type> </component>
An Authenticator is responsible for Identity creation, it consists of two methods:
validateUser() accepts an array of credentials and returns the userId (which can be something different from the username).
createIdentity() accepts the userId and returns a newly created Identity object.
public interface Authenticator { /** * Authenticate user and return userId which can be different to username. * @param credentials - list of users credentials (such as name/password, X509 certificate etc) * @return userId * @throws LoginException * @throws Exception */ String validateUser(Credential[] credentials) throws LoginException, Exception; /** * @param credentials - userId. * @return Identity * @throws Exception */ Identity createIdentity(String userId) throws Exception; }
It is up to the application developer (and deployer) whether to use the Authenticator component(s) and how many implementations of this components should be deployed in eXo container. The developer is free to create an Identity object using a different way, but the Authenticator component is the highly recommended way from architectural considerations.
Typical functionality of the validateUser(Credential\[] credentials) method is the comparison of incoming credentials (username/password, digest etc) with those credentials that are stored in an implementation specific database. Then, validateUser(Credential\[] credentials) returns back the userId or throws a LoginException in a case of wrong credentials.
Default Authenticator implementation is org.exoplatform.services.organization.auth.OrganizationAuthenticatorImpl which compares incoming username/password credentials with the ones stored in OrganizationService. Configuration example:
<component> <key>org.exoplatform.services.security.Authenticator</key> <type>org.exoplatform.services.organization.auth.OrganizationAuthenticatorImpl</type> </component>
The framework described is not coupled with any authentication mechanism but the most logical and implemented by default is the JAAS Login module. The typical sequence looks as follows (see org.exoplatform.services.security.jaas.DefaultLoginModule):
LoginModule.login() creates a list of credentials using standard JAAS Callbacks features, obtains an Authenticator instance, and creates an Identity object calling Authenticator.authenticate(..) method
Authenticator authenticator = (Authenticator) container() .getComponentInstanceOfType(Authenticator.class); // RolesExtractor can be null RolesExtractor rolesExtractor = (RolesExtractor) container(). getComponentInstanceOfType(RolesExtractor.class); Credential[] credentials = new Credential[] {new UsernameCredential(username), new PasswordCredential(password) }; String userId = authenticator.validateUser(credentials); identity = authenticator.createIdentity(userId);
LoginModule.commit() obtains the IdentityRegistry object, and register the identity using userId as a key.
When initializing the login module, you can set the option parameter "singleLogin". With this option you can disallow the same Identity to login for a second time.
By default singleLogin is disabled, so the same identity can be registered more than one time. Parameter can be passed in this form singleLogin=yes or singleLogin=true.
IdentityRegistry identityRegistry = (IdentityRegistry) getContainer().getComponentInstanceOfType(IdentityRegistry.class); if (singleLogin && identityRegistry.getIdentity(identity.getUserId()) != null) throw new LoginException("User " + identity.getUserId() + " already logined."); identity.setSubject(subject); identityRegistry.register(identity);
In the case of using several LoginModules, JAAS allows to place the login() and commit() methods in different REQUIRED modules.
After that, the web application must use SetCurrentIdentityFilter. This filter obtains the ConversationRegistry object and tries to get the ConversationState by sessionId (HttpSession). If there is no ConversationState, then SetCurrentIdentityFilter will create a new one, register it and set it as current one using ConversationState.setCurrent(state).
LoginModule.logout() can be called by JAASConversationStateListener, it extends ConversationStateListener.
This listener must be configured in web.xml. The method sessionDestroyed(HttpSessionEvent) is called by the ServletContainer. This method removes the ConversationState from the ConversationRegistry ConversationRegistry.unregister(sesionId) and calls the method LoginModule.logout().
ConversationRegistry conversationRegistry = (ConversationRegistry) getContainer().getComponentInstanceOfType(ConversationRegistry.class); ConversationState conversationState = conversationRegistry.unregister(sesionId); if (conversationState != null) { log.info("Remove conversation state " + sesionId); if (conversationState.getAttribute(ConversationState.SUBJECT) != null) { Subject subject = (Subject) conversationState.getAttribute(ConversationState.SUBJECT); LoginContext ctx = new LoginContext("exo-domain", subject); ctx.logout(); } else { log.warn("Subject was not found in ConversationState attributes."); }
There are several JAAS Login modules included in eXo Platform sources:
org.exoplatform.services.security.jaas.DefaultLoginModule which provides both authentication (using eXo Authenticator based mechanism) and authorization, filling Conversation Registry as it described in previous section. There are also several per-Application Server extensions of this login module which can be found in org.exoplatform.services.security.jaas package, which can be used in appropriate AS. In particular, we have dedicated Login modules for Tomcat, JBoss, Jonas and WebSphere.
Besides that, for the case when third party authentication mechanism required, we have org.exoplatform.services.security.jaas.IdentitySetLoginModule, which catches a login identity from third party "authenticating" login module and preforms eXo specific authorization job. In this case third party login module has to put login (user) name to the shared state map under "javax.security.auth.login.name" key and third party LM has to be configured before IdentitySetLoginModule like:
exo { com.third.party.LoginModuleImpl required; org.exoplatform.services.security.jaas.IdentitySetLoginModule required; };
As you know, when a user in JAAS is authenticated, a Subject is created as a result. This Subject represents the authenticated user. It is important to know and follow the rules regarding Subject filling which are specific for each J2EE server, where eXo Platform is deployed.
To make it workable for the particular J2EE server, it is necessary to add specific Principals/Credentials to the Subject to be propagated into the specific J2EE container implementation. We extended the DefaultLoginModule by overloading its commit() method with a dedicated logic, presently available for Tomcat, JBOSS and JONAS application servers.
Furthermore, you can use the optional RolesExtractor which is responsible for mapping primary Subject's principals (userId and a set of groups) to J2EE Roles:
public interface RolesExtractor { Set <String> extractRoles(String userId, Set<MembershipEntry> memberships); }
This component may be used by Authenticator to create the Identity with a particular set of Roles.
OrganizationService is the service that allows to access the Organization model. This model is composed of:
Users
Groups
Memberships
It is the basis of eXo personalization and authorizations in eXo and is used to all over the platform. The model is abstract and does not rely on any specific storage. Multiple implementations exist in eXo:
Hibernate: for storage into a RDBMS
Jndi: for storage into a directory such as an LDAP or MS Active Directory
Jcr: for storage inside a Java Content Repository
Gather a set of users
Applicative or business
Tree structure
No inheritance
Expressed as /group/subgroup/subsubgroup
To create a custom organization service you need to implement a several interfaces and extend some classes which will be listed below.
First of all you need to create classes implementing the following interfaces (each of which represent a basic unit of organization service):
org.exoplatform.services.organization.User
This is the interface for a User data model. The OrganizationService implementor can use the different strategy to implement this class, he can use the native field for each get method or use a Map to hold the user data.
org.exoplatform.services.organization.UserProfile
This is the interface for a UserProfile data model. The implementor should have an user map info in the implementation. The map should only accept the java.lang.String for the key and the value.
org.exoplatform.services.organization.Group
This is the interface for the group data model.
org.exoplatform.services.organization.Membership
This is the interface for the membership data model.
org.exoplatform.services.organization.MembershipType
This is the interface for the membership type data model.
After each set method is called the developer must call UserHandler.saveUser (GroupHandler.saveGroup, MembershipHandler.saveMembership etc.) method to persist the changes.
You can find examples of the mentioned above implementations at github server:
After you created basic organization service unit instances you need to create classess to handle them e.g. to persist changes, to add listener etc. For that purpose you need to implement a several interfaces correspondingly:
User handler
org.exoplatform.services.organization.UserHandler
This class is acted as a sub component of the organization service. It is used to manage the user account and broadcast the user event to all the registered listener in the organization service. The user event can be: new user event, update user event and delete user event. Each event should have 2 phases: pre event and post event. The method createUser , saveUser and removeUser broadcast the event at each phase so the listeners can handle the event properly.
org.exoplatform.services.organization.ExtendedUserHandler
Optional. Implement it if you want to be able to use Digest access authentication i.e. you need a one way password encyption for authentication.
org.exoplatform.services.organization.UserEventListenerHandler
Optional. Provides the ability to get the list of org.exoplatform.services.organization.UserEventListener. List should be unmodifiable to prevent modification outside of org.exoplatform.services.organization.UserHandler.
User profile handler
org.exoplatform.services.organization.UserProfileHandler
This interface is acted as a sub interface of the organization service. It is used to manage the the UserProfile record, the extra information of an user such address, phone... The interface should allow the developer create, delete and update a UserProfile. and broadcast the event to the user profile event listeners.
org.exoplatform.services.organization.UserProfileEventListenerHandler
Optional. Provides the ability to get the list of org.exoplatform.services.organization.UserProfileEventListener. List should be unmodifiable to prevent modification outside of org.exoplatform.services.organization.UserProfileHandler.
Group handler
org.exoplatform.services.organization.GroupHandler
This class is acted as a sub component of the organization service. It is used to manage the group and broadcast the group event to all the registered listener in the organization service. The group event can be: new group event, update group event and delete group event. Each event should have 2 phases: pre event and post event. The methods createGroup, saveGroup and removeGroup broadcast the event at each phase so the listeners can handle the event properly.
org.exoplatform.services.organization.GroupEventListenerHandler
Optional. Provides the ability to get the of org.exoplatform.services.organization.GroupEventListener. List should be unmodifiable to prevent modification outside of org.exoplatform.services.organization.GroupHandler.
Membership handler
org.exoplatform.services.organization.MembershipHandler
This class is acted as a sub component of the organization service. It is used to manage the membership - the relation of user , group, and membership type - and broadcast the membership event to all the registered listener in the organization service. The membership event can be: new linked membership and delete the membership type event. Each event should have 2 phases: pre event and post event. The method linkMembership and removeMembership broadcast the event at each phase so the listeners can handle the event properly.
org.exoplatform.services.organization.MembershipEventListenerHandler
Optional. Provides the ability to get the of org.exoplatform.services.organization.MembershipEventListener. List should be unmodifiable to prevent modification outside of org.exoplatform.services.organization.MembershipHandler.
Membership type handler
org.exoplatform.services.organization.MembershipTypeHandler
This class is acted as a sub component of the organization service. It is used to manage the membership type record, the information about user role in group... The interface should allow the developer create, delete and update a membership type and broadcast the event to the umembership type event listeners.
org.exoplatform.services.organization.MembershipTypeEventListenerHandler
Optional. Provides the ability to get the list of org.exoplatform.services.organization.MembershipTypeEventListener. List should be unmodifiable to prevent modification outside of org.exoplatform.services.organization.MembershipTypeHandler.
You can find examples of the mentioned above implementations at github server:
Finally you need to create your main custom organization service class. It must extend org.exoplatform.services.organization.BaseOrganizationService. BaseOrganizationService class contains organization service unit handlers as protected fields, so you can initialize them in accordance to your purposes. It also has org.exoplatform.services.organization.OrganizationService interface methods' implementations. This is the class you need to mention in the configuration file if you want to use your custom organization service.
You can find example of such class at github server: JCROrganizationServiceImpl.
Make sure that your custom organization service implementation is fully compliant with Organization Service TCK tests. Tests are available as maven artifact:
groupId - org.exoplatform.core
artifactId - exo.core.component.organization.tests
You can find TCK tests package source code here
In order to be able to run unit tests you may need to configure the following maven plugins:
Check pom.xml file to find out one of the ways to configure maven project object model. More detailed description you can find in the dedicated section called "Organization Service TCK tests configuration"
Use the Organization Service Initializer to create users, groups and membership types by default.
<external-component-plugins> <target-component>org.exoplatform.services.organization.OrganizationService</target-component> <component-plugin> <name>init.service.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.organization.OrganizationDatabaseInitializer</type> <description>this listener populate organization data for the first launch</description> <init-params> <value-param> <name>checkDatabaseAlgorithm</name> <description>check database</description> <value>entry</value> </value-param> <value-param> <name>printInformation</name> <description>Print information init database</description> <value>false</value> </value-param> <object-param> <name>configuration</name> <description>description</description> <object type="org.exoplatform.services.organization.OrganizationConfig"> <field name="membershipType"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType"> <field name="type"> <string>manager</string> </field> <field name="description"> <string>manager membership type</string> </field> </object> </value> </collection> </field> <field name="group"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"> <string>platform</string> </field> <field name="parentId"> <string></string> </field> <field name="description"> <string>the /platform group</string> </field> <field name="label"> <string>Platform</string> </field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"> <string>administrators</string> </field> <field name="parentId"> <string>/platform</string> </field> <field name="description"> <string>the /platform/administrators group</string> </field> <field name="label"> <string>Administrators</string> </field> </object> </value> </collection> </field> <field name="user"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$User"> <field name="userName"> <string>root</string> </field> <field name="password"> <string>exo</string> </field> <field name="firstName"> <string>Root</string> </field> <field name="lastName"> <string>Root</string> </field> <field name="email"> <string>root@localhost</string> </field> <field name="displayName"> <string>Root</string> </field> <field name="groups"> <string> manager:/platform/administrators </string> </field> </object> </value> </collection> </field> </object> </object-param> </init-params> </component-plugin> </external-component-plugins>
Params for membership type:
type: The membership type's name.
description: The membership type's description.
Params for group:
name: The group's name
parentId: The id of the parent group. If the parent id is null, it means that the group is at the first level. The parentId should have the form: /ancestor/parent
description: The group's description
label: The group's label
Params for user:
userName: The user's name
password: The user's password
firstName: The user's first name
lastName: The user's last name
email: The user's email
displayName: The user's display name
groups: The user's membership types and groups in which he consist.
The Organization Service provides a mechanism to receive notifications when:
A User is created, deleted or modified.
A Group is created, deleted or modified.
A Membership is created or removed.
This mechanism is very useful to cascade some actions when the organization model is modified. For example, it is currently used to :
Initialize the personal portal pages.
Initialize the personal calendars, address books and mail accounts in CS.
Create drives and personal areas in ECM.
To implement your own listener, you just need to write extend some existing listener classes. These classes define hooks that are invoked before or after operations are performed on organization model.
To listen to user changes, you need to extend "org.exoplatform.services.organization.UserEventListener"
public class MyUserListener extends UserEventListener { public void preSave(User user, boolean isNew) throws Exception { System.out.println("Before " + (isNew?"creating":"updating") + " user " + user.getUserName()); } public void postSave(User user, boolean isNew) throws Exception { System.out.println("After user " + user.getUserName() + (isNew?" created":" updated")); } public void preDelete(User user) throws Exception { System.out.println("Before deleting user " + user.getUserName()); } public void postDelete(User user) throws Exception { System.out.println("After deleting user " + user.getUserName()); } }
To listen group changes, you need to extend org.exoplatform.services.organization.GroupEventListener :
public class MyGroupListener extends GroupEventListener { public void preSave(Group group, boolean isNew) throws Exception { System.out.println("Before " + (isNew?"creating":"updating") + " group " + group.getName()); } public void postSave(Group group, boolean isNew) throws Exception { System.out.println("After group " + group.getName() + (isNew?" created":" updated")); } public void preDelete(Group group) throws Exception { System.out.println("Before deleting group " + group.getName()); } public void postDelete(Group group) throws Exception { System.out.println("After deleting group " + group.getName()); } }
To listen to membership changes, you need to extend org.exoplatform.services.organization.MembershipEventListener :
public class MyMembershipListener extends MembershipEventListener { public void preSave(Membership membership, boolean isNew) throws Exception { System.out.println("Before " + (isNew?"creating":"updating") + " membership."); } public void postSave(Membership membership, boolean isNew) throws Exception { System.out.println("After membership " + (isNew?" created":" updated")); } public void preDelete(Membership membership) throws Exception { System.out.println("Before deleting membership"); } public void postDelete(Membership membership) throws Exception { System.out.println("After deleting membership"); } }
Registering the listeners is then achieved by using the ExoContainer plugin mechanism. Learn more about it on the Service Configuration for Beginners article.
To effectively register organization service's listeners you simply need to use the addListenerPlugin seer injector.
So, the easiest way to register your listeners is to pack them into a .jar and create a configuration file into it under mylisteners.jar!/conf/portal/configuration.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <configuration> <external-component-plugins> <target-component>org.exoplatform.services.organization.OrganizationService</target-component> <component-plugin> <name>myuserplugin</name> <set-method>addListenerPlugin</set-method> <type>org.example.MyUserListener</type> <description></description> </component-plugin> <component-plugin> <name>mygroupplugin</name> <set-method>addListenerPlugin</set-method> <type>org.example.MyGroupListener</type> <description></description> </component-plugin> <component-plugin> <name>mymembershipplugin</name> <set-method>addListenerPlugin</set-method> <type>org.example.MyMembershipListener</type> <description></description> </component-plugin> </external-component-plugins> <configuration>
Now, simply deploy the jar under $TOMCAT_HOME/lib and your listeners are ready!
Be aware that you need to set proper RuntimePermission to be able to add or remove Listeners. To do that you need to grant the following permission for your code
permission java.lang.RuntimePermission "manageListeners"
When a user logged in portal in ConversationRegistry added ConversationSate for this user. ConversationState keeps user's Identity that is actual for logged in time. In this case even user's Membership updated in OrganizationService ConversationState still keeps old (not actual Identity). User must logged out and loggin in again to update Identity. To fix this issue, need add special listener in configuration of OrganizationServicer. This listener is extended MembershipEventListener.
Example of configuration.
<?xml version="1.0" encoding="ISO-8859-1"?> <configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_3.xsd http://www.exoplatform.org/xml/ns/kernel_1_3.xsd" xmlns="http://www.exoplatform.org/xml/ns/kernel_1_3.xsd"> <external-component-plugins> <target-component>org.exoplatform.services.organization.OrganizationService</target-component> ..... ..... <component-plugin> <name>MembershipUpdateListener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.organization.impl.MembershipUpdateListener</type> </component-plugin> <external-component-plugins> </configuration>
DB Schema Creator is responsible for creating database schema, using a DDL script inside service configuration or in an external file, calling:
org.exoplatform.services.database.jdbc.DBSchemaCreator.createTables(String dsName, String script)
via
org.exoplatform.services.database.jdbc.CreateDBSchemaPlugin component plugin
A configuration example:
<component> <key>org.exoplatform.services.database.jdbc.DBSchemaCreator</key> <type>org.exoplatform.services.database.jdbc.DBSchemaCreator</type> <component-plugins> <component-plugin> <name>jcr.dbschema</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.database.jdbc.CreateDBSchemaPlugin</type> <init-params> <value-param> <name>data-source</name> <value>jdbcjcr</value> </value-param> <value-param> <name>script-file</name> <value>conf/storage/jcr-mjdbc.sql</value> </value-param> </init-params> </component-plugin> ........
An example of a DDL script:
CREATE TABLE JCR_MITEM( ID VARCHAR(255) NOT NULL PRIMARY KEY, VERSION INTEGER NOT NULL, PATH VARCHAR(1024) NOT NULL ); CREATE INDEX JCR_IDX_MITEM_PATH ON JCR_MITEM(PATH);
As usual, it is quite simple to use our configuration XML syntax to configure and parametrize different Databases for eXo tables but also for your own use.
The default DB configuration uses HSQLDB, a Java Database quite useful for demonstrations.
<component> <key>org.exoplatform.services.database.HibernateService</key> <jmx-name>exo-service:type=HibernateService</jmx-name> <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type> <init-params> <properties-param> <name>hibernate.properties</name> <description>Default Hibernate Service</description> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.cglib.use_reflection_optimizer" value="true"/> <property name="hibernate.connection.url" value="jdbc:hsqldb:file:../temp/data/portal"/> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.autocommit" value="true"/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.cache.region.factory_class" value="org.exoplatform.services.database.impl.ExoCacheRegionFactory"/> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.c3p0.min_size" value="5"/> <property name="hibernate.c3p0.max_size" value="20"/> <property name="hibernate.c3p0.timeout" value="1800"/> <property name="hibernate.c3p0.max_statements" value="50"/> </properties-param> </init-params> </component>
In the init parameter section, we define the default hibernate properties including the DB URL, the driver and the credentials in use.
For any portals that configuration can be overridden, depending on the needs of your environment.
Several databases have been tested and can be used in production....which is not the case of HSQLDB, HSQLDB can only be used for development environments and for demonstrations.
For MySQL
<component> <key>org.exoplatform.services.database.HibernateService</key> <jmx-name>database:type=HibernateService</jmx-name> <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type> <init-params> <properties-param> <name>hibernate.properties</name> <description>Default Hibernate Service</description> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.cglib.use_reflection_optimizer" value="true"/> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/exodb?relaxAutoCommit=true&amp;autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf8"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.autocommit" value="true"/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.cache.region.factory_class" value="org.exoplatform.services.database.impl.ExoCacheRegionFactory"/> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.c3p0.min_size" value="5"/> <property name="hibernate.c3p0.max_size" value="20"/> <property name="hibernate.c3p0.timeout" value="1800"/> <property name="hibernate.c3p0.max_statements" value="50"/> </properties-param> </init-params> </component>
Hibernate caching is enabled by default by this parameters
<property name="hibernate.cache.region.factory_class" value="org.exoplatform.services.database.impl.ExoCacheRegionFactory"/> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/>
Hibernate caching parameters are well described in hibernate documentation. There is no forced injection of eXo cache provider anymore. It's usage can be configured via standard hibernate properties (in our xml) just like any other hibernate settings.
Also it is possible to configure size of eXoCache instances via cache service configuration. Every region (eXoCache instance) created by RegionFactory has it's own prefix depending on it's type. All prefixes are:
ExoCacheRegionFactory-Entity- ExoCacheRegionFactory-NaturalId- ExoCacheRegionFactory-Collection- ExoCacheRegionFactory-QueryResults- ExoCacheRegionFactory-Timestamps-
So, names of eXoCache instances will look like "ExoCacheRegionFactory-Entity-org.exoplatform.services.organization.impl.GroupImpl", etc. Details about cache service configuration can be found in eXo Cache section.
It is possible to use the eXo hibernate service and register your annotated classes or hibernate hbm.xml files to leverage some add-on features of the service such as the table automatic creation as well as the cache of the hibernate session in a ThreadLocal object during all the request lifecycle. To do so, you just have to add a plugin and indicate the location of your files.
Registering custom XML files can be done in this way:
<?xml version="1.0" encoding="ISO-8859-1"?> <configuration> <external-component-plugins> <target-component>org.exoplatform.services.database.HibernateService</target-component> <component-plugin> <name>add.hibernate.mapping</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.database.impl.AddHibernateMappingPlugin</type> <init-params> <values-param> <name>hibernate.mapping</name> <value>org/exoplatform/services/organization/impl/UserImpl.hbm.xml</value> <value>org/exoplatform/services/organization/impl/MembershipImpl.hbm.xml</value> <value>org/exoplatform/services/organization/impl/GroupImpl.hbm.xml</value> <value>org/exoplatform/services/organization/impl/MembershipTypeImpl.hbm.xml</value> <value>org/exoplatform/services/organization/impl/UserProfileData.hbm.xml</value> </values-param> </init-params> </component-plugin> </external-component-plugins> </configuration>
Registering custom annotated classes can be done in this way:
<?xml version="1.0" encoding="ISO-8859-1"?> <configuration> <external-component-plugins> <target-component>org.exoplatform.services.database.HibernateService</target-component> <component-plugin> <name>add.hibernate.annotations</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.database.impl.AddHibernateMappingPlugin</type> <init-params> <values-param> <name>hibernate.annotations</name> <value>org.exoplatform.services.organization.impl.UserProfileData</value> <value>org.exoplatform.services.organization.impl.MembershipImpl</value> <value>org.exoplatform.services.organization.impl.GroupImpl</value> <value>org.exoplatform.services.organization.impl.MembershipTypeImpl</value> </values-param> </init-params> </component-plugin> </external-component-plugins> </configuration>
You may decide to make eXo users to be mapped to an existing directory. eXo provides a flexible implementation of its OrganizationService on top of LDAP. It can be used on any LDAP compliant directory and even Active Directory. This page will guide you how to configure eXo Platform to work with your directory.
If you just want to have a look at how eXo works with LDAP, eXo comes with a predefined LDAP configuration. You just need to activate it and eXo will create everything it needs to work at startup.
You need to have a working LDAP server and a user with write permissions.
0pen idm-configuration.xml
located in this path eXo-PLF-3.5.x-tomcat-bundle/webapps/platform-extension/WEB-INF/conf/organization comment this tag:
<value>war:/conf/organization/picketlink-idm/picketlink-idm-config.xml</value>
and add this tag:
<value>war:/conf/organization/picketlink-idm/picketlink-idm-ldap-config.xml</value>
Under eXo-PLF-3.5.x-tomcat-bundle/webapps/platform-extension/WEB-INF/conf/organization/picketlink-idm add the picketlink-idm-ldap-config.xml
file.
The content of this file is as follows:
<?xml version="1.0" encoding="UTF-8"?> <jboss-identity xmlns="urn:picketlink:idm:config:v1_0_0_ga" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:picketlink:idm:config:v1_0_0_ga identity-config.xsd"> <realms> <realm> <id>idm_realm_sample-portal</id> <repository-id-ref>DefaultPortalRepository</repository-id-ref> <identity-type-mappings> <user-mapping>USER</user-mapping> </identity-type-mappings> <options> <option> <name>cache.providerRegistryName</name> <value>apiCacheProvider</value> </option> </options> </realm> <realm> <id>idm_realm</id> <repository-id-ref>PortalRepository</repository-id-ref> <identity-type-mappings> <user-mapping>USER</user-mapping> </identity-type-mappings> <options> <option> <name>template</name> <value>true</value> </option> <option> <name>cache.providerRegistryName</name> <value>apiCacheProvider</value> </option> </options> </realm> </realms> <repositories> <repository> <id>PortalRepository</id> <class>org.picketlink.idm.impl.repository.FallbackIdentityStoreRepository</class> <external-config/> <default-identity-store-id>HibernateStore</default-identity-store-id> <default-attribute-store-id>HibernateStore</default-attribute-store-id> <identity-store-mappings> <identity-store-mapping> <identity-store-id>PortalLDAPStore</identity-store-id> <identity-object-types> <identity-object-type>USER</identity-object-type> <identity-object-type>platform_type</identity-object-type> <identity-object-type>organization_type</identity-object-type> </identity-object-types> <options/> </identity-store-mapping> </identity-store-mappings> <options> <option> <name>allowNotDefinedAttributes</name> <value>true</value> </option> </options> </repository> <repository> <id>DefaultPortalRepository</id> <class>org.picketlink.idm.impl.repository.WrapperIdentityStoreRepository</class> <external-config/> <default-identity-store-id>HibernateStore</default-identity-store-id> <default-attribute-store-id>HibernateStore</default-attribute-store-id> </repository> </repositories> <stores> <attribute-stores/> <identity-stores> <identity-store> <id>HibernateStore</id> <class>org.picketlink.idm.impl.store.hibernate.HibernateIdentityStoreImpl</class> <external-config/> <supported-relationship-types> <relationship-type>JBOSS_IDENTITY_MEMBERSHIP</relationship-type> <relationship-type>JBOSS_IDENTITY_ROLE</relationship-type> </supported-relationship-types> <supported-identity-object-types> <identity-object-type> <name>USER</name> <relationships/> <credentials> <credential-type>PASSWORD</credential-type> </credentials> <attributes/> <options/> </identity-object-type> </supported-identity-object-types> <options> <option> <name>hibernateSessionFactoryRegistryName</name> <value>hibernateSessionFactory</value> </option> <option> <name>populateRelationshipTypes</name> <value>true</value> </option> <option> <name>populateIdentityObjectTypes</name> <value>true</value> </option> <option> <name>allowNotDefinedIdentityObjectTypes</name> <value>true</value> </option> <option> <name>allowNotDefinedAttributes</name> <value>true</value> </option> <option> <name>isRealmAware</name> <value>true</value> </option> </options> </identity-store> <identity-store> <id>PortalLDAPStore</id> <class>org.picketlink.idm.impl.store.ldap.LDAPIdentityStoreImpl</class> <external-config/> <supported-relationship-types> <relationship-type>JBOSS_IDENTITY_MEMBERSHIP</relationship-type> </supported-relationship-types> <supported-identity-object-types> <identity-object-type> <name>USER</name> <relationships/> <credentials> <credential-type>PASSWORD</credential-type> </credentials> <attributes> <attribute> <name>firstName</name> <mapping>cn</mapping> <type>text</type> <isRequired>false</isRequired> <isMultivalued>false</isMultivalued> <isReadOnly>false</isReadOnly> </attribute> <attribute> <name>lastName</name> <mapping>sn</mapping> <type>text</type> <isRequired>false</isRequired> <isMultivalued>false</isMultivalued> <isReadOnly>false</isReadOnly> </attribute> <attribute> <name>email</name> <mapping>mail</mapping> <type>text</type> <isRequired>false</isRequired> <isMultivalued>false</isMultivalued> <isReadOnly>false</isReadOnly> <isUnique>true</isUnique> </attribute> </attributes> <options> <option> <name>idAttributeName</name> <value>uid</value> </option> <option> <name>passwordAttributeName</name> <value>userPassword</value> </option> <option> <name>ctxDNs</name> <value>ou=People,o=portal,o=gatein,dc=example,dc=com</value> </option> <option> <name>allowCreateEntry</name> <value>true</value> </option> <option> <name>createEntryAttributeValues</name> <value>objectClass=top</value> <value>objectClass=inetOrgPerson</value> <value>sn= </value> <value>cn= </value> </option> </options> </identity-object-type> <identity-object-type> <name>platform_type</name> <relationships> <relationship> <relationship-type-ref>JBOSS_IDENTITY_MEMBERSHIP</relationship-type-ref> <identity-object-type-ref>USER</identity-object-type-ref> </relationship> <relationship> <relationship-type-ref>JBOSS_IDENTITY_MEMBERSHIP</relationship-type-ref> <identity-object-type-ref>platform_type</identity-object-type-ref> </relationship> </relationships> <credentials/> <attributes/> <options> <option> <name>idAttributeName</name> <value>cn</value> </option> <option> <name>ctxDNs</name> <value>ou=Platform,o=portal,o=gatein,dc=example,dc=com</value> </option> <!--<option>--> <!--<name>entrySearchFilter</name>--> <!--<value></value>--> <!--</option>--> <option> <name>allowCreateEntry</name> <value>true</value> </option> <option> <name>parentMembershipAttributeName</name> <value>member</value> </option> <option> <name>isParentMembershipAttributeDN</name> <value>true</value> </option> <option> <name>allowEmptyMemberships</name> <value>true</value> </option> <option> <name>createEntryAttributeValues</name> <value>objectClass=top</value> <value>objectClass=groupOfNames</value> </option> </options> </identity-object-type> <identity-object-type> <name>organization_type</name> <relationships> <relationship> <relationship-type-ref>JBOSS_IDENTITY_MEMBERSHIP</relationship-type-ref> <identity-object-type-ref>USER</identity-object-type-ref> </relationship> <relationship> <relationship-type-ref>JBOSS_IDENTITY_MEMBERSHIP</relationship-type-ref> <identity-object-type-ref>organization_type</identity-object-type-ref> </relationship> </relationships> <credentials/> <attributes/> <options> <option> <name>idAttributeName</name> <value>cn</value> </option> <option> <name>ctxDNs</name> <value>ou=Organization,o=portal,o=gatein,dc=example,dc=com</value> </option> <!--<option>--> <!--<name>entrySearchFilter</name>--> <!--<value></value>--> <!--</option>--> <option> <name>allowCreateEntry</name> <value>true</value> </option> <option> <name>parentMembershipAttributeName</name> <value>member</value> </option> <option> <name>isParentMembershipAttributeDN</name> <value>true</value> </option> <option> <name>allowEmptyMemberships</name> <value>true</value> </option> <option> <name>createEntryAttributeValues</name> <value>objectClass=top</value> <value>objectClass=groupOfNames</value> </option> </options> </identity-object-type> </supported-identity-object-types> <options> <option> <name>providerURL</name> <value>ldap://localhost:1389</value> </option> <option> <name>adminDN</name> <value>cn=admin</value> </option> <option> <name>adminPassword</name> <value>admin</value> </option> <option> <name>searchTimeLimit</name> <value>10000</value> </option> <option> <name>createMissingContexts</name> <value>true</value> </option> <option> <name>customJNDIConnectionParameters</name> <value>com.sun.jndi.ldap.connect.pool=true</value> </option> <option> <name>customSystemProperties</name> <value>com.sun.jndi.ldap.connect.pool.maxsize=300000</value> <value>com.sun.jndi.ldap.connect.pool.protocol=plain ssl</value> </option> <option> <name>cache.providerRegistryName</name> <value>storeCacheProvider</value> </option> </options> </identity-store> </identity-stores> </stores> <options> <option> <name>defaultTemplate</name> <value>idm_realm</value> </option> </options> </jboss-identity>
Change the configuration corresponding to the configuration you put when configuring your LDAP server. You have to update the values of options providerURL, adminDN, adminPassword and all instances of ctxDNs according to your LDAP server configuration.
Delete exo-tomcat/temp/* to have a clean database and then start tomcat.
eXo starts and autocreates its organization model in your directory tree. Finally, the structure of the default LDAP schema looks like:
That's it! Now eXo uses your LDAP directory as its org model storage. Users, groups and memberships are now stored and retrieved from there. We suggest that you complete some guideline functions with eXo user management portlet and see what it changes in your directory tree.
If you have an existing LDAP server, the eXo predefined settings will likely not match your directory structure. eXo LDAP organization service implementation was written with flexibility in mind and can certainly be configured to meet your requirements.
The configuration is done in ldap-configuration.xml file, and this section will explain the numerous parameters it contains.
Firstly, start by connection settings which will tell eXo how to connect to your directory server. These settings are very close to JNDI API context parameters. This configuration is activated by the init-param ldap.config of service LDAPServiceImpl.
<component> <key>org.exoplatform.services.ldap.LDAPService</key> <type>org.exoplatform.services.ldap.impl.LDAPServiceImpl</type> <init-params> <object-param> <name>ldap.config</name> <description>Default ldap config</description> <object type="org.exoplatform.services.ldap.impl.LDAPConnectionConfig"> <field name="providerURL"><string>ldap://127.0.0.1:389,10.0.0.1:389</string></field> <field name="rootdn"><string>CN=Manager,DC=exoplatform,DC=org</string></field> <field name="password"><string>secret</string></field> <!-- field name="authenticationType"><string>simple</string></field--> <field name="version"><string>3</string></field> <field name="referralMode"><string>follow</string></field> <!-- field name="serverName"><string>active.directory</string></field--> <field name="minConnection"><int>5</int></field> <field name="maxConnection"><int>10</int></field> <field name="timeout"><int>50000</int></field> </object> </object-param> </init-params> </component>
providerURL: LDAP server URL (see PROVIDER_URL). For multiple ldap servers, use comma separated list of host:port (Ex. ldap://127.0.0.1:389,10.0.0.1:389).
rootdn: dn of user that will be used by the service to authenticate on the server (see SECURITY_PRINCIPAL).
password: password for user rootdn (see SECURITY_CREDENTIALS).
authenticationType: type of authentication to be used (see SECURITY_AUTHENTICATION). Use one of none, simple, strong. Default is simple.
version: LDAP protocol version (see java.naming.ldap.version). Set to 3 if your server supports LDAP V3.
referalMode: one of follow, ignore,throw (see REFERRAL).
serverName: you will need to set this to active.directory in order to work with Active Directory servers. Any other value will be ignore and the service will act as on a standard LDAP.
maxConnection: the maximum number of connections per connection identity that can be maintained concurrently.
minConnection: the number of connections per connection identity to create when initially creating a connection for the identity.
timeout: the number of milliseconds that an idle connection may remain in the pool without being closed and removed from the pool.
Next, you need to configure the eXo OrganizationService to tell him how the directory is structured and how to interact with it. This is managed by a couple of of init-params : ldap.userDN.key and ldap.attribute.mapping in file ldap-configuration.xml (by default located at portal.war/WEB-INF/conf/organization)
<component> <key>org.exoplatform.services.organization.OrganizationService</key> <type>org.exoplatform.services.organization.ldap.OrganizationServiceImpl</type> [...] <init-params> <value-param> <name>ldap.userDN.key</name> <description>The key used to compose user DN</description> <value>cn</value> </value-param> <object-param> <name>ldap.attribute.mapping</name> <description>ldap attribute mapping</description> <object type="org.exoplatform.services.organization.ldap.LDAPAttributeMapping"> [...] </object-param> </init-params> [...] </component>
ldap.attribute.mapping maps your ldap to eXo. At first there are two main parameters to configure in it:
<field name="baseURL"><string>dc=exoplatform,dc=org</string></field> <field name="ldapDescriptionAttr"><string>description</string></field>
baseURL: root dn for eXo organizational entities. This entry can't be created by eXo and must preexist in directory.
ldapDescriptionAttr (since core 2.2+) : Name of a common attribute that will be used as description for groups and membership types.
(since core 2.2+) : Name of a common attribute that will be used as description for groups and membership types.
Other parameters are discussed in the following sections.
Here are the main parameters to map eXo users to your directory :
<field name="userURL"><string>ou=users,ou=portal,dc=exoplatform,dc=org</string></field> <field name="userObjectClassFilter"><string>objectClass=person</string></field> <field name="userLDAPClasses"><string>top,person,organizationalPerson,inetOrgPerson</string></field>
userURL : base dn for users. Users are created in a flat structure under this base with a dn of the form: ldap.userDN.key=username,userURL
Example :
uid=john,cn=People,o=MyCompany,c=com
However, if users exist deeply under userURL, eXo will be able to retrieve them.
Example :
uid=tom,ou=France,ou=EMEA,cn=People,o=MyCompany,c=com
userObjectClassFilter: Filter used under userURL branch to distinguish eXo user entries from others.
Example : john and tom will be recognized as valid eXo users but EMEA and France entries will be ignored in the following subtree :
uid=john,cn=People,o=MyCompany,c=com objectClass: person … ou=EMEA,cn=People,o=MyCompany,c=com objectClass: organizationalUnit … ou=France,ou=EMEA,cn=People,o=MyCompany,c=com objectClass: organizationalUnit … uid=tom,ou=EMEA,cn=People,o=MyCompany,c=com objectClass: person …
userLDAPClasses : comma separated list of classes used for user creation.
When creating a new user, an entry will be created with the given objectClass attributes. The classes must at least define cn and any attribute refernced in the user mapping.
Example : Adding the user Marry Simons could produce :
uid=marry,cn=users,ou=portal,dc=exoplatform,dc=org objectclass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson …
The following parameters maps ldap attributes to eXo User java objects attributes.
<field name="userUsernameAttr"><string>uid</string></field> <field name="userPassword"><string>userPassword</string></field> <field name="userFirstNameAttr"><string>givenName</string></field> <field name="userLastNameAttr"><string>sn</string></field> <field name="userDisplayNameAttr"><string>displayName</string></field> <field name="userMailAttr"><string>mail</string></field>
userUsernameAttr: username (login)
userPassword: password (used when portal authentication is done by eXo login module)
userFirstNameAttr: firstname
userLastNameAttr: lastname
userDisplayNameAttr: displayed name
userMailAttr: email address
Example : In the previous example, user Marry Simons could produce :
uid=marry,cn=users,ou=portal,dc=exoplatform,dc=org objectclass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson …
eXo groups can be mapped to organizational or applicative groups defined in your directory.
<field name="groupsURL"><string>ou=groups,ou=portal,dc=exoplatform,dc=org</string></field> <field name="groupLDAPClasses"><string>top,organizationalUnit</string></field> <field name="groupObjectClassFilter"><string>objectClass=organizationalUnit</string></field>
groupsURL : base dn for eXo groups
Groups can be structured hierarchically under groupsURL.
Example: Groups communication, communication/marketing and communication/press would map to :
ou=communication,ou=groups,ou=portal,dc=exoplatform,dc=org … ou=marketing,ou=communication,ou=groups,ou=portal,dc=exoplatform,dc=org … ou=press,ou=communication,ou=groups,ou=portal,dc=exoplatform,dc=org …
groupLDAPClasses: comma separated list of classes used for group creation.
When creating a new group, an entry will be created with the given objectClass attributes. The classes must define at least the required attributes: ou, description and l.
l attribute corresponds to the City property in OU property editor
Example : Adding the group human-resources could produce:
ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org objectclass: top objectClass: organizationalunit ou: human-resources description: The human resources department l: Human Resources …
groupObjectClassFilter: filter used under groupsURL branch to distinguish eXo groups from other entries. You can also use a complex filter if you need.
Example : groups WebDesign, WebDesign/Graphists and Sales could be retrieved in :
l=Paris,dc=sites,dc=mycompany,dc=com … ou=WebDesign,l=Paris,dc=sites,dc=mycompany,dc=com … ou=Graphists,WebDesign,l=Paris,dc=sites,dc=mycompany,dc=com … l=London,dc=sites,dc=mycompany,dc=com … ou=Sales,l=London,dc=sites,dc=mycompany,dc=com …
Membership types are the possible roles that can be assigned to users in groups.
<field name="membershipTypeURL"><string>ou=memberships,ou=portal,dc=exoplatform,dc=org</string></field> <field name="membershipTypeLDAPClasses"><string>top,organizationalRole</string></field> <field name="membershipTypeNameAttr"><string>cn</string></field>
membershipTypeURL : base dn for membership types storage.
eXo stores membership types in a flat structure under membershipTypeURL.
Example : Roles manager, user, admin and editor could by defined by the subtree :
ou=roles,ou=portal,dc=exoplatform,dc=org … cn=manager,ou=roles,ou=portal,dc=exoplatform,dc=org … cn=user,ou=roles,ou=portal,dc=exoplatform,dc=org … cn=admin,ou=roles,ou=portal,dc=exoplatform,dc=org … cn=editor,ou=roles,ou=portal,dc=exoplatform,dc=org …
membershipTypeLDAPClasses: comma separated list of classes for membership types creation.
When creating a new membership type, an entry will be created with the given objectClass attributes. The classes must define the required attributes : description, cn
Example : Adding membership type validator would produce :
cn=validator,ou=roles,ou=portal,dc=exoplatform,dc=org objectclass: top objectClass: organizationalRole …
membershipTypeNameAttr : Attribute that will be used as the name of the role
Example : If membershipTypeNameAttr is 'cn', then role name is 'manager' for the following membership type entry :
cn=manager,ou=roles,ou=portal,dc=exoplatform,dc=org
Memberships are used to assign a role within a group. They are entries that are placed under the group entry of their scope group. Users in this role are defined as attributes of the membership entry.
Example: To designate tom as the manager of the group human-resources:
ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org … cn=manager,ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org member: uid=tom,ou=users,ou=portal,dc=exoplatform,dc=org …
The parameters to configure memberships are:
<field name="membershipLDAPClasses"><string>top,groupOfNames</string></field> <field name="membershipTypeMemberValue"><string>member</string></field> <field name="membershipTypeRoleNameAttr"><string>cn</string></field> <field name="membershipTypeObjectClassFilter"><string>objectClass=organizationalRole</string></field>
membershipLDAPClasses : comma separated list of classes used to create memberships.
When creating a new membership, an entry will be created with the given objectClass attributes. The classes must at least define the attribute designated by membershipTypeMemberValue.
Example : Adding membership validator would produce :
cn=validator,ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org objectclass: top objectClass: groupOfNames …
cn=validator,ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org objectclass: top objectClass: groupOfNames
membershipTypeMemberValue: Multivalued attribute used in memberships to reference users that have the role in the group.
Values should be a user dn.
Example: james and root have admin role within the group human-resources, would give:
cn=admin,ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org member: cn=james,ou=users,ou=portal,dc=exoplatform,dc=org member: cn=root,ou=users,ou=portal,dc=exoplatform,dc=org …
membershipTypeRoleNameAttr: Attribute of the membership entry whose value references the membership type.
Example : In the following membership entry:
cn=manager,ou=human-resources,ou=groups,ou=portal,dc=exoplatform,dc=org
'cn' attribute is used to designate the 'manager' membership type. Which could also be said : The name of the role is given by 'cn' the attribute.
membershipTypeObjectClassFilter : Filter used to distinguish membership entries under groups.
You can use rather complex filters.
Example: Here is a filter we used for a customer that needed to trigger a dynlist overlay on openldap.
(&(objectClass=ExoMembership)(membershipURL=*))
Note: Pay attention to the xml escaping of the '&' (and) operator
eXo User profiles also have entries in the ldap but the actual storage is still done with the hibernate service. You will need the following parameters:
<field name="profileURL"><string>ou=profiles,ou=portal,dc=exoplatform,dc=org</string></field> <field name="profileLDAPClasses"><string>top,organizationalPerson</string></field>
profileURL: base dn to store user profiles
profileLDAPClasses: Classes used to when creating user profiles
At startup, eXo can populate the organization model based on
eXo organizational model has User,Group,Membership and Profile entities. For each, we define a base dn that should be below baseURL. At startup, if one of userURL, groupsURL, membershipTypeURL or profileURL does not exist fully, eXo will attempt to create the missing subtree by parsing the dn and creating entries on-the-fly. To determine the classes of the created entries, the following rules are applied :
ou=... : objectClass=top,objectClass=organizationalUnit
cn=... : objectClass=top,objectClass=organizationalRole
c=... : objectClass=country
o=... : objectClass=organization
dc=.. : objectClass=top,objectClass=dcObject,objectClass=organization
Example:
If baseURL is o=MyCompany,c=com and groupsURL is dc=groups,cn=Extranet,c=France,ou=EMEA,o=MyCompany,c=com then, the following subtree will be created :
ou=EMEA,o=MyCompany,c=com objectClass: top objectClass: organizationalUnit … c=France,ou=EMEA,o=MyCompany,c=com objectClass: top objectClass: country … cn=Extranet,c=France,ou=EMEA,o=MyCompany,c=com objectClass: top objectClass: organizationalRole … dc=groups,cn=Extranet,c=France,ou=EMEA,o=MyCompany,c=com objectClass: top objectClass: dcObject objectClass: organization …
Here is an alternative configuration for active directory that you can find in activedirectory-configuration.xml
There is a microsoft limitation: password can't be set in AD via unsecured connection you have to use the ldaps protocol
here is how to use LDAPS protocol with Active Directory :
1 setup AD to use SSL: * add Active Directory Certificate Services role * install right certificate for DC machine 2 enable Java VM to use certificate from AD: * import root CA used in AD, to keystore, something like keytool -importcert -file 2008.cer -keypass changeit -keystore /home/user/java/jdk1.6/jre/lib/security/cacerts * set java options JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStorePassword=changeit -Djavax.net.ssl.trustStore=/home/user/java/jdk1.6/jre/lib/security/cacerts"
[...] <component> <key>org.exoplatform.services.ldap.LDAPService</key> [..] <object type="org.exoplatform.services.ldap.impl.LDAPConnectionConfig"> <!-- for multiple ldap servers, use comma seperated list of host:port (Ex. ldap://127.0.0.1:389,10.0.0.1:389) --> <!-- whether or not to enable ssl, if ssl is used ensure that the javax.net.ssl.keyStore & java.net.ssl.keyStorePassword properties are set --> <!-- ldap service will check protocol, if protocol is ldaps, ssl is enable (Ex. for enable ssl: ldaps://10.0.0.3:636 ;for disable ssl: ldap://10.0.0.3:389 ) --> <!-- when enable ssl, ensure server name is *.directory and port (Ex. active.directory) --> <field name="providerURL"><string>ldaps://10.0.0.3:636</string></field> <field name="rootdn"><string>CN=Administrator,CN=Users, DC=exoplatform,DC=org</string></field> <field name="password"><string>site</string></field> <field name="version"><string>3</string></field> <field name="referralMode"><string>ignore</string></field> <field name="serverName"><string>active.directory</string></field> </object> [..] <component> <key>org.exoplatform.services.organization.OrganizationService</key> [...] <object type="org.exoplatform.services.organization.ldap.LDAPAttributeMapping"> [...] <field name="userAuthenticationAttr"><string>mail</string></field> <field name="userUsernameAttr"><string>sAMAccountName</string></field> <field name="userPassword"><string>unicodePwd</string></field> <field name="userLastNameAttr"><string>sn</string></field> <field name="userDisplayNameAttr"><string>displayName</string></field> <field name="userMailAttr"><string>mail</string></field> [..] <field name="membershipTypeLDAPClasses"><string>top,group</string></field> <field name="membershipTypeObjectClassFilter"><string>objectClass=group</string></field> [..] <field name="membershipLDAPClasses"><string>top,group</string></field> <field name="membershipObjectClassFilter"><string>objectClass=group</string></field> </object> [...] </component>
If you use OpenLDAP, you may want to use the overlays. Here is how you can use the dynlist overlay to have memberships dynamically populated.
The main idea is to have your memberships populated dynamically by an ldap query. Thus, you no longer have to maintain manually the roles on users.
To configure the dynlist, add the following to your slapd.conf :
dynlist-attrset ExoMembership membershipURL member
This snipet means : On entries that have ExoMembership class, use the URL defined in the value of attribute membershipURL as a query and populate results under the multivalues attribute member.
Now let's declare the corresponding schema (replace XXXXX to adapt to your own IANA code):
attributeType ( 1.3.6.1.4.1.XXXXX.1.59 NAME 'membershipURL' SUP memberURL )
membershipURL inherits from memberURL.
objectClass ( 1.3.6.1.4.1.XXXXX.2.12 NAME 'ExoMembership' SUP top MUST ( cn ) MAY (membershipURL $ member $ description ) )
ExoMembership must define cn and can have attributes :
membershipURL: trigger for the dynlist
member : attribute populated by the dynlist
description : used by eXo for display
# the TestGroup group dn: ou=testgroup,ou=groups,ou=portal,o=MyCompany,c=com objectClass: top objectClass: organizationalUnit ou: testgroup l: TestGroup description: the Test Group
On this group, we can bind an eXo membership where the overlay will occur:
# the manager membership on group TestGroup dn: cn=manager, ou=TestGroup,ou=groups,ou=portal,o=MyCompany,c=com objectClass: top objectClass: ExoMembership membershipURL: ldap:///ou=users,ou=portal,o=MyCompany,c=com??sub?(uid=*) cn: manager
This dynlist assigns the role manager:/testgroup to any user.
You may decide to make eXo users to be mapped on top of JCR. eXo provides an implementation of its OrganizationService on top of JCR.
This is an implementation of the exo.core.component.organization.api API. The information will be stored in the root node exo:organization of the workspace. The workspace name has to be configured in the configuration file (see below)
Open exo-tomcat/webapps/portal/WEB-INF/conf/configuration.xml and replace:
<import>war:/conf/organization/idm-configuration.xml</import>
With
<import>war:/conf/organization/exo/jcr-configuration.xml</import>
Create jcr-configuration.xml file in exo-tomcat/webapps/portal/WEB-INF/conf/organization/exo directory and fill it with this content:
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_3.xsd http://www.exoplaform.org/xml/ns/kernel_1_3.xsd" xmlns="http://www.exoplaform.org/xml/ns/kernel_1_3.xsd"> <component> <key>org.exoplatform.services.organization.OrganizationService</key> <type>org.exoplatform.services.jcr.ext.organization.JCROrganizationServiceImpl</type> <init-params> <value-param> <name>storage-workspace</name> <description>Workspace in default repository where organization storage will be created</description> <value>collaboration</value> </value-param> </init-params> </component> <external-component-plugins> <target-component>org.exoplatform.services.jcr.RepositoryService</target-component> <component-plugin> <name>add.namespaces</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.jcr.impl.AddNamespacesPlugin</type> <init-params> <properties-param> <name>namespaces</name> <property name="jos" value="http://www.exoplatform.com/jcr-services/organization-service/1.0/"/> </properties-param> </init-params> </component-plugin> <component-plugin> <name>add.nodeType</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.jcr.impl.AddNodeTypePlugin</type> <init-params> <values-param> <name>autoCreatedInNewRepository</name> <description>Node types configuration file</description> <value>jar:/conf/organization-nodetypes.xml</value> </values-param> </init-params> </component-plugin> </external-component-plugins> </configuration>
where storage-workspace is the name of the workspace in the default repository where the organization storage will be created. If storage-workspace is absent in configuration then default workspace will be selected in current repository.
Delete exo-tomcat/temp/* to have a clean database and then start tomcat.
eXo starts and autocreates its organization model in /exo:organization node
That's it! Now eXo uses your JCR node as its organization model storage. Users, groups and memberships are now stored and retrieved from there.
Sice eXo JCR 1.11 you can add two new params:
<value-param> <name>repository</name> <description>The name of repository where organization storage will be created</description> <value>db1</value> </value-param> <value-param> <name>storage-path</name> <description>The relative path where organization storage will be created</description> <value>/exo:organization</value> </value-param>
where repository is the name of the repository where the organization storage will be created storage-path - the relative path to the stored data
Register JCR Organization service namespace and nodetypes via RepositoryService's plugins:
<component> <key>org.exoplatform.services.jcr.RepositoryService</key> <type>org.exoplatform.services.jcr.impl.RepositoryServiceImpl</type> <component-plugins> <component-plugin> <name>add.namespaces</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.jcr.impl.AddNamespacesPlugin</type> <init-params> <properties-param> <name>namespaces</name> <property name="jos" value="http://www.exoplatform.com/jcr-services/organization-service/1.0/"/> </properties-param> </init-params> </component-plugin> <component-plugin> <name>add.nodeType</name> <set-method>addPlugin</set-method> <type>org.exoplatform.services.jcr.impl.AddNodeTypePlugin</type> <init-params> <values-param> <name>autoCreatedInNewRepository</name> <description>Node types configuration file</description> <value>jar:/conf/organization-nodetypes.xml</value> </values-param> </init-params> </component-plugin> </component-plugins> </component>
Inner representation of JCR organization service has been changed in JCR 1.15. eXo provides a migration tool for that users which used JCR organization service with JCR 1.14. Migration process will start automatically after upgrade to JCR 1.15.
Please avoid abort of migration process. Migration tool was designed to be failover, but this is not recommended.
The process of launching the Organization Service TCK tests against your Organization Service is quite easy. For instance you may add TCK tests to your maven project and launch them during unit testing phase. To do that you need to complete the next two steps:
Configure your maven pom.xml file
Configure standalone container and Organization Service
If you need more profound information you can find Organization Service TCK test sources at Git
Organization Service TCK tests are available as a separate maven artifact, so the first thing you need to do is to add this artifact as a dependency to your pom.xml file
<dependency> <groupId>org.exoplatform.core</groupId> <artifactId>exo.core.component.organization.tests</artifactId> <version>2.4.3-GA</version> <classifier>sources</classifier> <scope>test</scope> </dependency>
You will also need to unpack tests as they are archieved within jar file. For this purpose you may use maven-dependency-plugin
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack</id> <phase>generate-test-sources</phase> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>org.exoplatform.core</groupId> <artifactId>exo.core.component.organization.tests</artifactId> <classifier>sources</classifier> <type>jar</type> <overWrite>false</overWrite> </artifactItem> </artifactItems> <outputDirectory>${project.build.directory}/org-service-tck-tests</outputDirectory> </configuration> </execution> </executions> </plugin>
Remember the value of outputDirectory parameter as you will need it later.
After you have unpacked the tests you need to add the tests sources and resources, use build-helper-maven-plugin
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>1.3</version> <executions> <execution> <id>add-test-resource</id> <phase>generate-test-sources</phase> <goals> <goal>add-test-resource</goal> </goals> <configuration> <resources> <resource> <directory>${project.build.directory}/org-service-tck-tests</directory> </resource> </resources> </configuration> </execution> <execution> <id>add-test-source</id> <phase>generate-test-sources</phase> <goals> <goal>add-test-source</goal> </goals> <configuration> <sources> <source>${project.build.directory}/org-service-tck-tests</source> </sources> </configuration> </execution> </executions> </plugin>
directory and source parameter should point to the location you've specified in outputDirectory parameter just above.
You also need to include all TCK tests using maven-surefire-plugin
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> ... <includes> <include>org/exoplatform/services/tck/organization/Test*.java</include> </includes> ... </configuration> </plugin>
As a result you should have TCK being launched during your next maven clean install. Example of configured pom.xml file you can find at Git server
TCK tests use standalone container, so to launch TCK tests propertly you will also need to add Organization Service as a standalone component. For that purpose use configuration file, which is to be located in 'src/test/java/conf/standalone/test-configuration.xml' by default, but its location can be changed by system property called orgservice.test.configuration.file. Add your Organization Service configuration with all needed components there.
In addition you need to populate your Organization Service with organization data (TCK tests are designed to use this data):
<external-component-plugins> <target-component>org.exoplatform.services.organization.OrganizationService</target-component> <component-plugin> <name>init.service.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.organization.OrganizationDatabaseInitializer</type> <description>this listener populate organization data for the first launch</description> <init-params> <value-param> <name>checkDatabaseAlgorithm</name> <description>check database</description> <value>entry</value> </value-param> <value-param> <name>printInformation</name> <description>Print information init database</description> <value>false</value> </value-param> <object-param> <name>configuration</name> <description>description</description> <object type="org.exoplatform.services.organization.OrganizationConfig"> <field name="membershipType"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType"> <field name="type"><string>manager</string></field> <field name="description"><string>manager membership type</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType"> <field name="type"><string>member</string></field> <field name="description"><string>member membership type</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType"> <field name="type"><string>validator</string></field> <field name="description"><string>validator membership type</string></field> </object> </value> </collection> </field> <field name="group"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>platform</string></field> <field name="parentId"><string></string></field> <field name="description"><string>the /platform group</string></field> <field name="label"><string>Platform</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>administrators</string></field> <field name="parentId"><string>/platform</string></field> <field name="description"><string>the /platform/administrators group</string></field> <field name="label"><string>Administrators</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>users</string></field> <field name="parentId"><string>/platform</string></field> <field name="description"><string>the /platform/users group</string></field> <field name="label"><string>Users</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>guests</string></field> <field name="parentId"><string>/platform</string></field> <field name="description"><string>the /platform/guests group</string></field> <field name="label"><string>Guests</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>organization</string></field> <field name="parentId"><string></string></field> <field name="description"><string>the organization group</string></field> <field name="label"><string>Organization</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>management</string></field> <field name="parentId"><string>/organization</string></field> <field name="description"><string>the /organization/management group</string></field> <field name="label"><string>Management</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>executive-board</string></field> <field name="parentId"><string>/organization/management</string></field> <field name="description"><string>the /organization/management/executive-board group</string></field> <field name="label"><string>Executive Board</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>human-resources</string></field> <field name="parentId"><string>/organization/management</string></field> <field name="description"><string>the /organization/management/human-resource group</string></field> <field name="label"><string>Human Resources</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>communication</string></field> <field name="parentId"><string>/organization</string></field> <field name="description"><string>the /organization/communication group</string></field> <field name="label"><string>Communication</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>marketing</string></field> <field name="parentId"><string>/organization/communication</string></field> <field name="description"><string>the /organization/communication/marketing group</string></field> <field name="label"><string>Marketing</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>press-and-media</string></field> <field name="parentId"><string>/organization/communication</string></field> <field name="description"><string>the /organization/communication/press-and-media group</string></field> <field name="label"><string>Press and Media</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>operations</string></field> <field name="parentId"><string>/organization</string></field> <field name="description"><string>the /organization/operations and media group</string></field> <field name="label"><string>Operations</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>sales</string></field> <field name="parentId"><string>/organization/operations</string></field> <field name="description"><string>the /organization/operations/sales group</string></field> <field name="label"><string>Sales</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>finances</string></field> <field name="parentId"><string>/organization/operations</string></field> <field name="description"><string>the /organization/operations/finances group</string></field> <field name="label"><string>Finances</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>customers</string></field> <field name="parentId"><string></string></field> <field name="description"><string>the /customers group</string></field> <field name="label"><string>Customers</string></field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"><string>partners</string></field> <field name="parentId"><string></string></field> <field name="description"><string>the /partners group</string></field> <field name="label"><string>Partners</string></field> </object> </value> </collection> </field> <field name="user"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$User"> <field name="userName"><string>root</string></field> <field name="password"><string>exo</string></field> <field name="firstName"><string>Root</string></field> <field name="lastName"><string>Root</string></field> <field name="email"><string>root@localhost</string></field> <field name="groups"> <string> manager:/platform/administrators,member:/platform/users, member:/organization/management/executive-board </string> </field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$User"> <field name="userName"><string>john</string></field> <field name="password"><string>exo</string></field> <field name="firstName"><string>John</string></field> <field name="lastName"><string>Anthony</string></field> <field name="email"><string>john@localhost</string></field> <field name="groups"> <string> member:/platform/administrators,member:/platform/users, manager:/organization/management/executive-board </string> </field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$User"> <field name="userName"><string>marry</string></field> <field name="password"><string>exo</string></field> <field name="firstName"><string>Marry</string></field> <field name="lastName"><string>Kelly</string></field> <field name="email"><string>marry@localhost</string></field> <field name="groups"> <string>member:/platform/users</string> </field> </object> </value> <value> <object type="org.exoplatform.services.organization.OrganizationConfig$User"> <field name="userName"><string>demo</string></field> <field name="password"><string>exo</string></field> <field name="firstName"><string>Demo</string></field> <field name="lastName"><string>exo</string></field> <field name="email"><string>demo@localhost</string></field> <field name="groups"> <string>member:/platform/guests,member:/platform/users</string> </field> </object> </value> </collection> </field> </object> </object-param> </init-params> </component-plugin> </external-component-plugins> <external-component-plugins> <target-component>org.exoplatform.services.organization.OrganizationService</target-component> <component-plugin> <name>tester.membership.type.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.organization.MembershipTypeEventListener</type> <description>Membership type listerner for testing purpose</description> </component-plugin> </external-component-plugins>
Ultimately you will have a configuration file which determines standalone container and consists of Organization Service configuration and initialization data. You can find prepared test-configuration.xml file at Git
DocumentReaderService provides API to retrieve DocumentReader by mimetype.
DocumentReader lets the user fetch content of document as String or, in case of TikaDocumentReader, as Reader.
Basicaly, DocumentReaderService is a container for all registered DocumentReaders. So, you can register DocumentReader (method addDocumentReader(ComponentPlugin reader)) and fetch DocumentReader by mimeType (method getDocumentReader(String mimeType)).
TikaDocumentReaderServiceImpl extends DocumentReaderService with simple goal - read Tika configuration and lazy register each Tika Parser as TikaDocumentReader.
By default, all Tikas Parsers are not registered in readers <mimetype, DocumentReader> map. When user tries to fetch a DocumentReader by unknown mimetype. Than TikaDocumentReaderService checks tika configuration, and register a new mimetype-DocumentReader pair.
How TikaDocumentReaderService Impl configuration looks like:
<component> <key>org.exoplatform.services.document.DocumentReaderService</key> <type>org.exoplatform.services.document.impl.tika.TikaDocumentReaderServiceImpl</type> <!-- Old-style document readers --> <component-plugins> <component-plugin> <name>pdf.document.reader</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.PDFDocumentReader</type> <description>to read the pdf inputstream</description> </component-plugin> <component-plugin> <name>document.readerMSWord</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.MSWordDocumentReader</type> <description>to read the ms word inputstream</description> </component-plugin> <component-plugin> <name>document.readerMSXWord</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.MSXWordDocumentReader</type> <description>to read the ms word inputstream</description> </component-plugin> <component-plugin> <name>document.readerMSExcel</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.MSExcelDocumentReader</type> <description>to read the ms excel inputstream</description> </component-plugin> <component-plugin> <name>document.readerMSXExcel</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.MSXExcelDocumentReader</type> <description>to read the ms excel inputstream</description> </component-plugin> <component-plugin> <name>document.readerMSOutlook</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.MSOutlookDocumentReader</type> <description>to read the ms outlook inputstream</description> </component-plugin> <component-plugin> <name>PPTdocument.reader</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.PPTDocumentReader</type> <description>to read the ms ppt inputstream</description> </component-plugin> <component-plugin> <name>MSXPPTdocument.reader</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.MSXPPTDocumentReader</type> <description>to read the ms pptx inputstream</description> </component-plugin> <component-plugin> <name>document.readerHTML</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.HTMLDocumentReader</type> <description>to read the html inputstream</description> </component-plugin> <component-plugin> <name>document.readerXML</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.XMLDocumentReader</type> <description>to read the xml inputstream</description> </component-plugin> <component-plugin> <name>TPdocument.reader</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.TextPlainDocumentReader</type> <description>to read the plain text inputstream</description> <init-params> <!-- values-param> <name>defaultEncoding</name> <description>description</description> <value>UTF-8</value> </values-param --> </init-params> </component-plugin> <component-plugin> <name>document.readerOO</name> <set-method>addDocumentReader</set-method> <type>org.exoplatform.services.document.impl.OpenOfficeDocumentReader</type> <description>to read the OO inputstream</description> </component-plugin> </component-plugins> <init-params> <value-param> <name>tika-configuration</name> <value>jar:/conf/portal/tika-config.xml</value> </value-param> </init-params> </component> </configuration>
Table 3.1. Fields description
tika-configuration | This parameter refers to the path of the tika configuration file to use. By default it uses the default configuration of tika available from TikaConfig.getDefaultConfig(). |
tika-config.xml example:
<properties> <mimeTypeRepository magic="false"/> <parsers> <parser name="parse-dcxml" class="org.apache.tika.parser.xml.DcXMLParser"> <mime>application/xml</mime> <mime>image/svg+xml</mime> <mime>text/xml</mime> <mime>application/x-google-gadget</mime> </parser> <parser name="parse-office" class="org.apache.tika.parser.microsoft.OfficeParser"> <mime>application/excel</mime> <mime>application/xls</mime> <mime>application/msworddoc</mime> <mime>application/msworddot</mime> <mime>application/powerpoint</mime> <mime>application/ppt</mime> <mime>application/x-tika-msoffice</mime> <mime>application/msword</mime> <mime>application/vnd.ms-excel</mime> <mime>application/vnd.ms-excel.sheet.binary.macroenabled.12</mime> <mime>application/vnd.ms-powerpoint</mime> <mime>application/vnd.visio</mime> <mime>application/vnd.ms-outlook</mime> </parser> <parser name="parse-ooxml" class="org.apache.tika.parser.microsoft.ooxml.OOXMLParser"> <mime>application/x-tika-ooxml</mime> <mime>application/vnd.openxmlformats-package.core-properties+xml</mime> <mime>application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</mime> <mime>application/vnd.openxmlformats-officedocument.spreadsheetml.template</mime> <mime>application/vnd.ms-excel.sheet.macroenabled.12</mime> <mime>application/vnd.ms-excel.template.macroenabled.12</mime> <mime>application/vnd.ms-excel.addin.macroenabled.12</mime> <mime>application/vnd.openxmlformats-officedocument.presentationml.presentation</mime> <mime>application/vnd.openxmlformats-officedocument.presentationml.template</mime> <mime>application/vnd.openxmlformats-officedocument.presentationml.slideshow</mime> <mime>application/vnd.ms-powerpoint.presentation.macroenabled.12</mime> <mime>application/vnd.ms-powerpoint.slideshow.macroenabled.12</mime> <mime>application/vnd.ms-powerpoint.addin.macroenabled.12</mime> <mime>application/vnd.openxmlformats-officedocument.wordprocessingml.document</mime> <mime>application/vnd.openxmlformats-officedocument.wordprocessingml.template</mime> <mime>application/vnd.ms-word.document.macroenabled.12</mime> <mime>application/vnd.ms-word.template.macroenabled.12</mime> </parser> <parser name="parse-html" class="org.apache.tika.parser.html.HtmlParser"> <mime>text/html</mime> </parser> <parser mame="parse-rtf" class="org.apache.tika.parser.rtf.RTFParser"> <mime>application/rtf</mime> </parser> <parser name="parse-pdf" class="org.apache.tika.parser.pdf.PDFParser"> <mime>application/pdf</mime> </parser> <parser name="parse-txt" class="org.apache.tika.parser.txt.TXTParser"> <mime>text/plain</mime> <mime>script/groovy</mime> <mime>application/x-groovy</mime> <mime>application/x-javascript</mime> <mime>application/javascript</mime> <mime>text/javascript</mime> </parser> <parser name="parse-openoffice" class="org.apache.tika.parser.opendocument.OpenOfficeParser"> <mime>application/vnd.oasis.opendocument.database</mime> <mime>application/vnd.sun.xml.writer</mime> <mime>application/vnd.oasis.opendocument.text</mime> <mime>application/vnd.oasis.opendocument.graphics</mime> <mime>application/vnd.oasis.opendocument.presentation</mime> <mime>application/vnd.oasis.opendocument.spreadsheet</mime> <mime>application/vnd.oasis.opendocument.chart</mime> <mime>application/vnd.oasis.opendocument.image</mime> <mime>application/vnd.oasis.opendocument.formula</mime> <mime>application/vnd.oasis.opendocument.text-master</mime> <mime>application/vnd.oasis.opendocument.text-web</mime> <mime>application/vnd.oasis.opendocument.text-template</mime> <mime>application/vnd.oasis.opendocument.graphics-template</mime> <mime>application/vnd.oasis.opendocument.presentation-template</mime> <mime>application/vnd.oasis.opendocument.spreadsheet-template</mime> <mime>application/vnd.oasis.opendocument.chart-template</mime> <mime>application/vnd.oasis.opendocument.image-template</mime> <mime>application/vnd.oasis.opendocument.formula-template</mime> <mime>application/x-vnd.oasis.opendocument.text</mime> <mime>application/x-vnd.oasis.opendocument.graphics</mime> <mime>application/x-vnd.oasis.opendocument.presentation</mime> <mime>application/x-vnd.oasis.opendocument.spreadsheet</mime> <mime>application/x-vnd.oasis.opendocument.chart</mime> <mime>application/x-vnd.oasis.opendocument.image</mime> <mime>application/x-vnd.oasis.opendocument.formula</mime> <mime>application/x-vnd.oasis.opendocument.text-master</mime> <mime>application/x-vnd.oasis.opendocument.text-web</mime> <mime>application/x-vnd.oasis.opendocument.text-template</mime> <mime>application/x-vnd.oasis.opendocument.graphics-template</mime> <mime>application/x-vnd.oasis.opendocument.presentation-template</mime> <mime>application/x-vnd.oasis.opendocument.spreadsheet-template</mime> <mime>application/x-vnd.oasis.opendocument.chart-template</mime> <mime>application/x-vnd.oasis.opendocument.image-template</mime> <mime>application/x-vnd.oasis.opendocument.formula-template</mime> </parser> <parser name="parse-image" class="org.apache.tika.parser.image.ImageParser"> <mime>image/bmp</mime> <mime>image/gif</mime> <mime>image/jpeg</mime> <mime>image/png</mime> <mime>image/tiff</mime> <mime>image/vnd.wap.wbmp</mime> <mime>image/x-icon</mime> <mime>image/x-psd</mime> <mime>image/x-xcf</mime> </parser> <parser name="parse-class" class="org.apache.tika.parser.asm.ClassParser"> <mime>application/x-tika-java-class</mime> </parser> <parser name="parse-mp3" class="org.apache.tika.parser.mp3.Mp3Parser"> <mime>audio/mpeg</mime> </parser> <parser name="parse-midi" class="org.apache.tika.parser.audio.MidiParser"> <mime>application/x-midi</mime> <mime>audio/midi</mime> </parser> <parser name="parse-audio" class="org.apache.tika.parser.audio.AudioParser"> <mime>audio/basic</mime> <mime>audio/x-wav</mime> <mime>audio/x-aiff</mime> </parser> </parsers> </properties>
As you see configuration above, there is both old-style DocumentReaders and new Tika parsers registered.
But MSWordDocumentReader and org.apache.tika.parser.microsoft.OfficeParser both refer to same "application/msword" mimetype, exclaims attentive reader. And he is right. But only one DocumentReader will be fetched.
Old-style DocumentReader registered in configuration become registered into DocumentReaderService. So, mimetypes that is supported by those DocumentReaders will have a registered pair, and user will always fetch this DocumentReaders with getDocumentReader(..) method. Tika configuration will be checked for Parsers only if there is no already registered DocumentReader.
You can make you own DocumentReader in two ways.
Old-Style Document Reader:
extend BaseDocumentReader
public class MyDocumentReader extends BaseDocumentReader { public String[] getMimeTypes() { return new String[]{"mymimetype"}; } ... }
register it as component-plugin
<component-plugin> <name>my.DocumentReader</name> <set-method>addDocumentReader</set-method> <type>com.mycompany.document.MyDocumentReader</type> <description>to read my own file format</description> </component-plugin>
Tika Parser:
implement Parser
public class MyParser implements Parser { ... }
register it in tika-config.xml
<parser name="parse-mydocument" class="com.mycompany.document.MyParser"> <mime>mymimetype</mime> </parser>
TikaDocumentReader features and notes:
TikaDocumentReader may return document content as Reader object. Old-Style DocumentReader does not;
TikaDocumentReader do not detects document mimetipe. You will get exact parser as configured in tika-config;
All readers methods closes InputStream at final.
Digest access authentication is one of the agreed methods a web server can use to negotiate credentials with a web user's browser. It uses encryption to send the password over the network which is safer than the Basic access authentication that sends plaintext.
Technically digest authentication is an application of MD5 cryptographic hashing with usage of nonce values to discourage cryptanalysis. It uses the HTTP protocol.
To configure you server to use DIGEST authentication we need to edit serverside JAAS module implementation configuration file.
You need to fulfill a couple of steps. Firstly change login configuration:
Edit config file located here: exo-tomcat/webapps/rest.war/WEB-INF/web.xml
Replace
<login-config> <auth-method>BASIC</auth-method> <realm-name>eXo REST services</realm-name> </login-config>
for
<login-config> <auth-method>DIGEST</auth-method> <realm-name>eXo REST services</realm-name> </login-config>
More information about tomcat configuration can be found at Apache Tomcat Configuration Reference.
Secondly you also need to specify new login module for JAAS:
Edit config file located here: exo-tomcat/conf/jaas.conf
Replace
exo-domain { org.exoplatform.services.security.j2ee.TomcatLoginModule required; };
for
exo-domain { org.exoplatform.services.security.j2ee.DigestAuthenticationTomcatLoginModule required; };
You need to fulfill a couple of steps. Firstly change login configuration:
Edit config file located here: exo-jetty/webapps/rest.war/WEB-INF/web.xml
Replace
<login-config> <auth-method>BASIC</auth-method> <realm-name>eXo REST services</realm-name> </login-config>
for
<login-config> <auth-method>DIGEST</auth-method> <realm-name>eXo REST services</realm-name> </login-config>
Secondly you also need to specify new login module for JAAS:
Edit config file located here: exo-jetty/jaas.conf
Replace
exo-domain { org.exoplatform.services.security.j2ee.JettyLoginModule required; };
for
exo-domain { org.exoplatform.services.security.j2ee.DigestAuthenticationJettyLoginModule required; };
Edit config file located here: exo-jboss/server/default/deploy/exo.jcr.ear.ear/rest.war/WEB-INF/web.xml
Replace
<login-config> <auth-method>BASIC</auth-method> <realm-name>eXo REST services</realm-name> </login-config>
for
<login-config> <auth-method>DIGEST</auth-method> <realm-name>eXo REST services</realm-name> </login-confi
You also need to edit login configuration file located here: exo-jboss/server/default/conf/login-config.xml
<application-policy name="exo-domain"> <authentication> <login-module code="org.exoplatform.services.security.j2ee.DigestAuthenticationJbossLoginModule" flag="required"> <module-option name="usersProperties">props/jmx-console-users.properties</module-option> <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option> <module-option name="hashAlgorithm">MD5</module-option> <module-option name="hashEncoding">rfc2617</module-option> <module-option name="hashUserPassword">false</module-option> <module-option name="hashStorePassword">true</module-option> <module-option name="passwordIsA1Hash">true</module-option> <module-option name="storeDigestCallback"> org.jboss.security.auth.spi.RFC2617Digest </module-option> </login-module> </authentication> </application-policy>
You probably should define users.properties and role.properties according to your own needs.
More information about jboss server Digest authentication configuration can be found at JBoss guide chapter.
To make your own
org.exoplatform.services.organization.OrganizationService
implementation able to use DIGEST authentication you need to make your
UserHandler implementation also implement
org.exoplatform.services.organization.DigestAuthenticator
interface which provide more flexible authenticate method. As it is called
from
org.exoplatform.services.organization.auth.OrganizationAuthenticatorImpl
it receive a
org.exoplatform.services.security.Credential
instances,
you can get more information from
org.exoplatform.services.security.PasswordCredential.getPasswordContext()
.
It can be used to calculate md5 digest of original password to compare it
with recieved from clientside.