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 LiveCycle administrator configures User Management to
retrieve records from LDAP using Administration Console. (see LiveCycle 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 tableThe following illustration shows the users table.
Groups tableThe following illustration shows the groups table.
Membership tableThe following illustration shows the membership table.
LiveCycle usersAfter the directory service provider retrieves
user data from the MySQL database, the users shown in the users table
become LiveCycle 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 LiveCycle 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 stepsTo 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 filesTo see sample files that represent a directory service
provider, see the <install directory>\sdk\samples\LiveCycleESAPI\FoundationSPI\UserManagementDirectoryManager
folder.
Setting up your development environmentTo
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 ES3/sdk\spi folder.
The following JAR files must be added to your project’s classpath:
Defining your application logicTo 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 recordsTo
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 LiveCycle 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 membersYour
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 implementationThe 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 implementationThe 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 providerCreate
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 providerBefore 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 providerAfter 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.
|
|
|