You can create directory service providers for User Management
to store user information. Creating Directory Service Providers
explains how to use the User Management SPI to create a directory
service provider that you can integrate with LiveCycle.
Typically User Management retrieves user data from an LDAP repository.
a AEM forms administrator configures User Management to
retrieve records from LDAP using administration console. (see
administration help
.)
If your organization uses a non-LDAP repository to store user
records, you can create a directory service provider that retrieves
user data from your user repository. For example, assume that your
organization uses a relational database such as MySQL to store user
data. You can create a directory service provider to retrieve user
data from the MySQL database.
The sample MySQL database, named
spi
, consists of three tables:
-
users
: Stores user records. This table contains
the following fields:
userid
,
firstName
,
lastName
,
email
,
and
user_password
. The
user_password
field
is encrypted.
-
groups
: Stores group records. This table contains
the following fields:
name
,
desc
,
email
,
email
,
and
aliases
.
-
membership
: Stores membership information for each group.
This table contains the following fields:
userid
and
groupName
.
Users table
The following illustration shows the
users
table.
Groups table
The following illustration shows the
groups
table.
Membership table
The following illustration shows the
membership
table.
AEM forms users
After the directory service provider retrieves
user data from the MySQL database, the users shown in the
users
table
become AEM forms users.
Directory
service providers retrieve records from a user repository (for example,
the MySQL relational database) at the request of User Management. LiveCycle caches user data to improve performance. You can programmatically
synchronize users by using the User Management API. (See
Programmatically Synchronizing Users
.)
To perform LiveCycle operations using these users, develop a custom authentication handler
that is able to authenticate the user. For example, assume that
you attempt to log in to administration console using
tblue
.
The custom authication handler authenticates
tblue
by
checking the
spi.users
database. (See
Creating Authentication Providers
.)
Note:
In addition
to authenticating a user, a user has to have the Administration
Console User role to log in to administration console.
A
directory service provider retrieves data from both a user repository
and group repostory. That is, the directory service provider retrieves
data from the
users
table and the
groups
and
membership
tables.
Summary of steps
To develop a directory service provider, perform
the following steps:
-
Set up your development environment.
-
Define your application logic.
-
Define the component XML file.
-
Package the component.
-
Deploy and test your component.
Sample files
To see sample files that represent a directory service
provider, see the <install directory>\sdk\samples\LiveCycleESAPI\FoundationSPI\UserManagementDirectoryManager
folder.
Setting up your development environment
To
create a directory service provider, create an Eclipse Java project.
The version of Eclipse that is supported is 3.2.1 or later. The User
Management SPI requires the um-spi.jar file to exist in your project’s
class path. If you do not reference this JAR file, you cannot use
the User Management SPI in your Java project. This JAR file is installed
with the LiveCycle SDK in the <install directory>/Adobe/Adobe
LiveCycle ES4/sdk
\spi
folder.
The following JAR files must be added to your project’s classpath:
Defining your application logic
To define a directory service provider, create Java classes
that implement the following interfaces:
These interfaces implement the
com.adobe.idp.um.spi.directoryservices.DirectoryPrincipalProvider
interface.
Both the
DirectoryUserProvider
and
DirectoryGroupProvider
interfaces
are used for retrieving user and group records. User Management
requires an implementation of the
DirectoryUserProvider
interface
to synchronize its database with the user repository. User Management
also requires an implementation of the
DirectoryGroupProvider
interface
when an action is performed on a group, such as adding a group to
a policy.
Implement the following methods that User Management invokes
when it performs user and group synchronization:
-
DirectoryUserProvider.getPrincipals()
-
DirectoryUserProvider.testConfiguration()
-
DirectoryGroupProvider.getGroupMembers()
Retrieving user records
To
create an implementation that allows for the retrieval of user and
group records (principals), implement the
DirectoryUserProvider.getPrincipals
method.
User Management invokes this method repeatedly until all of the specified
records are retrieved. The
getPrincipals
method
requires the following parameters:
-
config
: A
com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig
object
that contains information about the records to retrieve. Information
within this object include the domain and other information specific
to the implementation. This object provides access to either a
com.adobe.idp.um.spi.directoryservices.UserConfigBO
or
com.adobe.idp.um.spi.directoryservices.GroupConfigBO
object.
-
state
: An
Object
that contains
the state returned from the previous call to the method, or
null
for
the initial call. You can create an object of any type as long as
it is serializable and available to the server. Each time it is updated,
you pass it back to LiveCycle using the
com.adobe.idp.um.spi.directoryservices.DSPrincipalCollection.setState
method.
The
getPrincipals
method returns a
com.adobe.idp.um.spi.directoryservices.DSPrincipalCollection
object
that contains either the retrieved user records and state, or
null
if
records were not returned.
Within your implementation of the
getPrincipals
method,
you can create application logic that retrieves user records from
the use repository. For example, you can create application logic
that retrieves records from the
spi.users
database.
The directory service provider uses classes located in the
java.sql.*
package to
retrieve records from the
spi.users
database.
To successfully retrieve records from a user repository, create
a
DSPrincipalRecord
instance for each user that
you retrieve. A
DSPrincipalRecord
object represents
a user (or group) that becomes a AEM forms user (or group).
Using methods that belong to the
DSPrincipalRecord
object,
you can set user information such as the user’s common name, email, identifier
value, and so on. For example, to set the user’s email value, invoke
the
DSPrincipalRecord
instance’s
setEmail
method.
You can retrieve values to pass from the result set from the database
query. Add each
DSPrincipalRecord
instance to the
DSPrincipalCollection
object
by invoking its
addDSPrincipalRecord
method.
The following application logic shows a database query. The results
are passed as parameters when invoking methods that belong to the
DSPrincipalRecord
instance.
//Prepare the return collection
DSPrincipalCollection collection = new DSPrincipalCollection();
//Create connection to mySQL server
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://hsmithk-380:3306/spi", "root", "password");
Statement statement = con.createStatement();
//Branch to the correct type of principal for the sync
if(principalType.equals(UMConstants.PrincipalTypes.PRINCIPALTYPE_USER)){
//Select all users from the Users table
ResultSet res = statement.executeQuery("SELECT * FROM users");
while(res.next()){
//Create a record and set general properties
DSPrincipalRecord record = new DSPrincipalRecord();
record.setDomainName(domainName);
record.setPrincipalType(principalType);
record.setDisabled(false);
record.setVisibility(2);
//Retrieve data from the SQL query
String userid = res.getString("userid");
String firstName = res.getString("firstName");
String lastName = res.getString("lastName");
String email = res.getString("email");
//Mandatory field for the record
record.setCanonicalName(firstName + " " + lastName);
//Set other info pulled from the database
record.setCommonName(firstName + " " + lastName);
record.setUserid(userid);
record.setGivenName(firstName);
record.setFamilyName(lastName);
record.setEmail(email);
//Add the record to the return collection
collection.addDSPrincipalRecord(record);
}
}
Retrieving group members
Your
implementation of the
DirectoryGroupProvider
interface
must include a
getGroupMembers
method that is used
to retrieve associations between users and groups. The
getGroupMembers
method
requires the following parameters:
-
config
: A
com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig
object
that contains information about the records to retrieve. This object
also contains the
GroupConfigBO
object as configured
by User Management.
-
group
: A
com.adobe.idp.um.spi.directoryservices.DSPrincipalIdRecord
that
identifies the group whose members are retrieved. This object contains
the domain name and canonical name of the group.
The
getGroupMembers
method returns a
com.adobe.idp.um.spi.directoryservices.DSGroupContainmentRecord
object
that contains the retrieved user records of the group.
To successfully retrieve group records, create a
DSPrincipalRecord
instance
for each group record that you retrieve. Create a
DSPrincipalRecord
object that
represents the group. Using methods that belong to the
DSPrincipalRecord
object,
you can set group information such as the group name, description,
and email. For example, to set the group’s email value, invoke the
DSPrincipalRecord
instance’s
setEmail
method.
You can retrieve values to pass from the result set from the database
query. Add each
DSPrincipalRecord
instance to the
DSPrincipalCollection
object
by invoking its
addDSPrincipalRecord
method.
The sample directory service provider consists of two classes:
-
SampleDirectoryManager
: Handles the
requests from LiveCycle to retrieve user and group data.
This class implements the implements
DirectoryUserProvider
and
DirectoryGroupProvider
classes.
This class must contain the following methods:
getPrincipals
,
getGroupMembers
,
and
testConfiguration
.
-
DirectoryController
: Retrieves user and group
data from the MySQL database. This class retrieves data from the
MySQL database by using classes located in the
java.sql.*
package.
Defining the SampleDirectoryManager implementation
The following application logic represents the
SampleDirectoryManager
class.
package com.adobe.livecycle.samples.directory.provider;
import com.adobe.idp.common.errors.exception.IDPException;
import com.adobe.idp.um.api.UMConstants;
import com.adobe.idp.um.spi.directoryservices.DSGroupContainmentRecord;
import com.adobe.idp.um.spi.directoryservices.DSPrincipalCollection;
import com.adobe.idp.um.spi.directoryservices.DSPrincipalIdRecord;
import com.adobe.idp.um.spi.directoryservices.DirectoryGroupProvider;
import com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig;
import com.adobe.idp.um.spi.directoryservices.DirectoryUserProvider;
import com.adobe.livecycle.samples.directory.provider.DirectoryController;
/**
* This class provides a sample implementation of User and Group provider SPIs.
*/
public class SampleDirectoryManager implements DirectoryUserProvider,
DirectoryGroupProvider {
/* This method is used to obtain all the Users and Groups from any directory. The config object passed to this
* method would determine whether to return Users or groups. If config.getUserConfig() is not null then users would
* be returned and if config.getGroupConfig() is not null then groups would be returned.
*/
public DSPrincipalCollection getPrincipals(DirectoryProviderConfig config,
Object state) throws IDPException {
if (config.getUserConfig() == null && config.getGroupConfig() == null) {
return null;
}
String principalType = UMConstants.PrincipalTypes.PRINCIPALTYPE_USER;
if (config.getGroupConfig() != null) {
principalType = UMConstants.PrincipalTypes.PRINCIPALTYPE_GROUP;
}
/* Because the getPrincipals method is continuously called by the sync application
* we must tell it to stop after the first pass.
* In DirectoryController we set the state event to "Done" so that the next time
* this method is called, it will return null, meaning the end of the sync.
*/
if (state != null) {
return null;
}
DirectoryController directoryController = new DirectoryController(principalType, config.getDomain());
return directoryController.getPrincipalsBatch();
}
/*
* Once all the users & groups have been retrieved by calling getPrincipals, LiveCycle will call this method
* to obtain user-group associations. Here it just need to pass all those users which belong to the group
* being sent here.
*/
public DSGroupContainmentRecord getGroupMembers(
DirectoryProviderConfig config, DSPrincipalIdRecord group)
throws IDPException {
System.out.println("Sending Members of Group : " + group.getCanonicalName());
DSGroupContainmentRecord dsGroupContainmentRecord = DirectoryController.getGroupMembers(group);
return dsGroupContainmentRecord;
}
/*
* This is a utility method that could be used to validate the settings of
* the custom directory whether it's running as expected or not.
*/
public boolean testConfiguration(DirectoryProviderConfig config) {
return DirectoryController.testConfiguration(config);
}
}
Defining the DirectoryController implementation
The following application logic represents the
DirectoryController
class.
package com.adobe.livecycle.samples.directory.provider;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Arrays;
import com.adobe.idp.common.errors.exception.IDPException;
import com.adobe.idp.um.api.UMConstants;
import com.adobe.idp.um.spi.directoryservices.DSGroupContainmentRecord;
import com.adobe.idp.um.spi.directoryservices.DSPrincipalCollection;
import com.adobe.idp.um.spi.directoryservices.DSPrincipalIdRecord;
import com.adobe.idp.um.spi.directoryservices.DSPrincipalRecord;
import com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig;
/**
* This class is an intermediary module which will talk to user repository
* to retrieve users and groups in a domain. Hence, it will act as a Controller
* interacting with custom directories.
*/
public class DirectoryController {
private String principalType;
private String domainName;
public DirectoryController( String principalType, String domainName) {
super();
this.principalType = principalType;
this.domainName = domainName;
}
/**
* This method returns a batch of principals (users or groups) from the directory.
*/
public DSPrincipalCollection getPrincipalsBatch() {
//Prepare the return collection
DSPrincipalCollection collection = new DSPrincipalCollection();
try{
//Create connection to mySQL server
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://hsmithk-380:3306/spi", "root", "password");
Statement statement = con.createStatement();
//Branch to the correct type of principal for the sync
if(principalType.equals(UMConstants.PrincipalTypes.PRINCIPALTYPE_USER)){
//Select all users from the Users table
ResultSet res = statement.executeQuery("SELECT * FROM users");
while(res.next()){
//Create a record and set general properties
DSPrincipalRecord record = new DSPrincipalRecord();
record.setDomainName(domainName);
record.setPrincipalType(principalType);
record.setDisabled(false);
record.setVisibility(2);
//Retrieve data from the SQL query
String userid = res.getString("userid");
String firstName = res.getString("firstName");
String lastName = res.getString("lastName");
String email = res.getString("email");
//Mandatory field for the record
record.setCanonicalName(firstName + " " + lastName);
//Set other info pulled from the database
record.setCommonName(firstName + " " + lastName);
record.setUserid(userid);
record.setGivenName(firstName);
record.setFamilyName(lastName);
record.setEmail(email);
//Add the record to the return collection
collection.addDSPrincipalRecord(record);
}
}else{
//Select all groups from the Groups table
ResultSet res = statement.executeQuery("SELECT * FROM groups ");
while(res.next()){
//Create a record and set general properties
DSPrincipalRecord record = new DSPrincipalRecord();
record.setDomainName(domainName);
record.setPrincipalType(principalType);
record.setDisabled(false);
record.setVisibility(2);
//Retrieve data from the SQL query
String name = res.getString("name");
String desc = res.getString("desc");
String email = res.getString("email");
String aliases = res.getString("aliases");
//Mandatory field for the record
record.setCanonicalName(name);
//Set other info pulled from the database
record.setCommonName(name);
record.setDescription(desc);
record.setEmail(email);
//The setEmailAliases requires a list as parameter, so we split the string
//containing the comma-seperated email addresses.
record.setEmailAliases(Arrays.asList(aliases.split(",")));
//Add the record to the return collection
collection.addDSPrincipalRecord(record);
}
}
}catch(Exception e){
e.printStackTrace();
}
//Set state to done so that we won't iterate through the list again.
collection.setState("DONE");
return collection;
}
/**
* This method will return all the users which belong to a particular group. The group members are
* formed by randomly selecting users.
*/
public static DSGroupContainmentRecord getGroupMembers(DSPrincipalIdRecord group)
throws IDPException {
//Create the record and set the group properties
DSGroupContainmentRecord record = new DSGroupContainmentRecord();
record.setDomainName(group.getDomainName());
record.setCanonicalName(group.getCanonicalName());
try{
//Create a connection to the database
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://hsmithk-380:3306/spi", "root", "password");
Statement statement = con.createStatement();
//Query the membership, to get the users who are in the group
ResultSet res = statement.executeQuery("SELECT * FROM users where userid IN (select userid from membership where groupName='"+group.getCanonicalName()+"')");
while(res.next()){
//Retrieve data form resultSet
String firstName = res.getString("firstName");
String lastName = res.getString("lastName");
//Create a new DSPrincipalIdRecord member and match the info from the database
DSPrincipalIdRecord member = new DSPrincipalIdRecord();
member.setDomainName(group.getDomainName());
member.setCanonicalName(firstName + " "+ lastName);
//Add the user to the list of members
record.addPrincipalMember(member);
}
}catch(Exception e){
e.printStackTrace();
}
//return the list of members
return record;
}
/**
* This method can be used to validate the settings of the custom directory
* whether it's running as expected or not
*
*/
public static boolean testConfiguration(DirectoryProviderConfig config) {
System.out.println("Testing configuration unimplemented");
return true;
}
}
Defining the component XML file for the directory service provider
Create
a component XML file in order to deploy the directory service provider
component. A component XML file exists for each component and provides
metadata about the component. For more information about the component
XML file, see the
Component XML Reference
.
The following component.xml file is used for the directory service
provider. Notice that the service name is
SampleDirectoryManagerService
and
the operations this service exposes are named
getPrincipals
,
testConfiguration
,
and
getGroupMembers
.
Defining the component XML file for the directory service provider
<component xmlns="http://adobe.com/idp/dsc/component/document">
<component-id>com.adobe.livecycle.samples.directory.SampleDirectoryManagerService</component-id>
<version>1.0</version>
<search-order>PARENT_FIRST</search-order>
<class-path>um-spi.jar</class-path>
<dynamic-import-packages>
<package version="1.0" isOptional="true">*</package>
</dynamic-import-packages>
<services>
<service name="SampleDirectoryManagerService">
<implementation-class>com.adobe.livecycle.samples.directory.provider.SampleDirectoryManager</implementation-class>
<specifications>
<specification spec-id="com.adobe.idp.um.spi.directoryservices.directoryGroupProvider"/>
<specification spec-id="com.adobe.idp.um.spi.directoryservices.directoryUserProvider"/>
</specifications>
<operations>
<operation name="getPrincipals" method="getPrincipals" >
<input-parameter name="config" type="com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig" />
<input-parameter name="state" type="java.lang.Object" />
<output-parameter name="result" type="com.adobe.idp.um.spi.directoryservices.DSPrincipalCollection"/>
</operation>
<operation name="testConfiguration" method="testConfiguration" >
<input-parameter name="config" type="com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig" />
<output-parameter name="result" type="boolean"/>
</operation>
<operation name="getGroupMembers" method="getGroupMembers" >
<input-parameter name="config" type="com.adobe.idp.um.spi.directoryservices.DirectoryProviderConfig" />
<input-parameter name="group" type="com.adobe.idp.um.spi.directoryservices.DSPrincipalIdRecord" />
<output-parameter name="result" type="com.adobe.idp.um.spi.directoryservices.DSGroupContainmentRecord"/>
</operation>
</operations>
</service>
</services>
</component>
Packaging the directory service provider
Before deploying the directory service provider to LiveCycle,
package your Eclipse project into a JAR file. Also, the component
XML file and external JAR files must be located at the root of the
JAR file.
The following illustration shows the Eclipse project’s content
that is packaged into the external directory service provider’s
JAR file.
Package the directory service provider into a JAR file named
directoryServiceProviderSample-dsc.jar. In the previous diagram,
notice that .JAVA files are listed. Once packaged into a JAR file,
the corresponding .CLASS files must also be specified. Without the
.CLASS files, the directory service provider does not work.
Note:
After you package the directory service provider
into a JAR file, you can deploy the component to LiveCycle.
(See
Deploying your component
.)
Deploying and testing the directory service provider
After you package the directory service provider, deploy
and test it.
To deploy and test the directory service provider:
-
Log in to the administration console.
-
Click
Settings
>
User Management
>
Domain Management
>
New Enterprise Domain
.
-
Click
Add Directory
and provide the profile name.
-
Select
Custom SPI Provider
, and click
Next
.
A list of all of the custom SPI providers is displayed and your
custom directory provider component is included.
-
Select your component and click
OK
. When you start synchronization
for this domain, the users and groups are retrieved using your component.
|
|
|