chap-samples.xml revision 786c8a76edf4275c0c85b200563c8f318641b088
<?xml version="1.0" encoding="UTF-8"?>
<!--
! CCPL HEADER START
!
! This work is licensed under the Creative Commons
! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
! To view a copy of this license, visit
! http://creativecommons.org/licenses/by-nc-nd/3.0/
! or send a letter to Creative Commons, 444 Castro Street,
! Suite 900, Mountain View, California, 94041, USA.
!
! You can also obtain a copy of the license at
! legal/CC-BY-NC-ND.txt.
! See the License for the specific language governing permissions
! and limitations under the License.
!
! If applicable, add the following below this CCPL HEADER, with the fields
! enclosed by brackets "[]" replaced with your own identifying information:
! Portions Copyright [yyyy] [name of copyright owner]
!
! CCPL HEADER END
!
! Copyright 2011-2014 ForgeRock AS
!
-->
<chapter xml:id='chap-samples'
xmlns='http://docbook.org/ns/docbook'
version='5.0' xml:lang='en'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://docbook.org/ns/docbook
http://docbook.org/xml/5.0/xsd/docbook.xsd'
xmlns:xlink='http://www.w3.org/1999/xlink'>
<title>More OpenIDM Samples</title>
<para>The current distribution of OpenIDM comes with a variety of samples
in <filename>openidm/samples/</filename>. Sample 1 is described in
<link xlink:href="install-guide#chap-sample"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>First OpenIDM
Sample</citetitle></link>. This chapter describes the remaining OpenIDM
samples.</para>
<section xml:id="before-you-begin-samples">
<title>Before You Begin</title>
<para>Install OpenIDM, as described in <link
xlink:href="install-guide#chap-install"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Installing
OpenIDM Services</citetitle></link>.</para>
<para>OpenIDM comes with an internal noSQL database, OrientDB, for use as
the internal repository out of the box. This makes it easy to get started
with OpenIDM. OrientDB is not yet supported for production use, however,
so use a supported JDBC database when moving to production.</para>
<section xml:id="install-samples">
<title>Installing the Samples</title>
<indexterm>
<primary>Installing</primary>
<secondary>Samples</secondary>
</indexterm>
<para>
Each sample directory in <filename>openidm/samples/</filename> contains a
number of subdirectories, such as <filename>conf/</filename> and
<filename>script/</filename>. To start OpenIDM with a sample configuration,
navigate to the <filename>/path/to/openidm</filename> directory and use the
<literal>-p</literal> option of the <command>startup</command> command to
point to the sample whose configuration you want to use. Some, but not all
samples require additional software, such as an external LDAP server or
database.
</para>
<para>
When you move from one sample to the next, bear in mind that you are
changing the OpenIDM configuration. For information on how configuration
changes work, see <link xlink:href="integrators-guide#when-configuring-notes"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Changing the
Configuration</citetitle></link> in the <citetitle>Integrator's
Guide</citetitle>.
</para>
<para>
The command-line examples in this chapter (and throughout the OpenIDM
documentation) assume a UNIX shell. If you are running these samples on
Windows, adjust the command-line examples accordingly. For an indication of
what the corresponding Windows command would look like, see the examples in
the <link xlink:show="new" xlink:href="install-guide#chap-sample"
xlink:role="http://docbook.org/xlink/role/olink" ><citetitle>First OpenIDM
Sample</citetitle></link>.
</para>
</section>
<section xml:id="preparing-openidm">
<title>Preparing OpenIDM</title>
<para>Install an instance of OpenIDM specifically to try the samples. That
way you can experiment as much as you like, and discard the result if you
are not satisfied.</para>
<para>
If you are using the same instance of OpenIDM for multiple samples, it is
helpful to clear out the repository created for an earlier sample. To do so,
shut down OpenIDM and delete the <filename>openidm/db/openidm</filename>
directory.
</para>
<screen>$ rm -rf /path/to/openidm/db/openidm</screen>
<para>
OpenIDM should now be ready to start with a new sample. For a number of
the following samples, users are created either at the UI or with a
commons REST call. Once added, and when reconciliation is complete, such
users should be able to log into the UI.
</para>
</section>
</section>
<section xml:id="more-sample1">
<title>Sample 1 - XML File</title>
<para>Sample 1 is described in the chapter, <link
xlink:href="install-guide#chap-sample"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>First OpenIDM
Sample</citetitle></link>.</para>
</section>
<section xml:id="more-sample2">
<title>Sample 2 - LDAP One Way</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 2 - LDAP one way</secondary>
</indexterm>
<para>
Sample 2 resembles the first sample, but in sample 2 OpenIDM is connected to
a local LDAP server. The sample has been tested with <link xlink:show="new"
xlink:href="http://www.forgerock.org/opendj.html">OpenDJ</link>, but should
work with any LDAPv3 compliant server.
</para>
<para>
Sample 2 demonstrates how OpenIDM can pick up new or changed objects from an
external resource. The sample contains only one mapping, from the external
LDAP server resource to the OpenIDM repository. The sample therefore does not
push any changes made to OpenIDM managed user objects out to the LDAP server.
</para>
<section xml:id="external-ldap-config-2">
<title>LDAP Server Configuration</title>
<itemizedlist>
<para>
Sample 2 expects the following configuration for the external LDAP server:
</para>
<listitem>
<para>
The LDAP server runs on the local host.
</para>
</listitem>
<listitem>
<para>
The LDAP server listens on port 1389.
</para>
</listitem>
<listitem>
<para>
A user with DN <literal>cn=Directory Manager</literal> and password
<literal>password</literal> has read access to the LDAP server.
</para>
</listitem>
<listitem>
<para>
Directory data for that server is stored under base DN
<literal>dc=example,dc=com</literal>.
</para>
</listitem>
<listitem>
<para>
User objects for that server are stored under base DN
<literal>ou=People,dc=example,dc=com</literal>.
</para>
</listitem>
<listitem>
<para>
User objects have the object class <literal>inetOrgPerson</literal>.
</para>
</listitem>
<listitem>
<para>
User objects have the following attributes:
</para>
<itemizedlist>
<listitem>
<para>
<literal>cn</literal>
</para>
</listitem>
<listitem>
<para>
<literal>description</literal>
</para>
</listitem>
<listitem>
<para>
<literal>givenName</literal>
</para>
</listitem>
<listitem>
<para>
<literal>mail</literal>
</para>
</listitem>
<listitem>
<para>
<literal>sn</literal>
</para>
</listitem>
<listitem>
<para>
<literal>telephoneNumber</literal>
</para>
</listitem>
<listitem>
<para>
<literal>uid</literal>
</para>
</listitem>
<listitem>
<para>
<literal>userPassword</literal>
</para>
</listitem>
</itemizedlist>
<para>
An example user object follows.
</para>
<programlisting language="ldif">
dn: uid=jdoe,ou=People,dc=example,dc=com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
givenName: John
uid: jdoe
cn: John Doe
telephoneNumber: 1-415-523-0772
sn: Doe
mail: jdoe@example.com
description: Created by OpenIDM
userPassword: password</programlisting>
</listitem>
</itemizedlist>
<para>
A sample LDIF file, with two users, is provided in
<filename>openidm/samples/sample2/data/Example.ldif</filename>.
</para>
<para>
The following steps provide setup instructions for an OpenDJ server. Adjust
these instructions if you are using an alternative LDAP server.
</para>
<procedure>
<step>
<para>
Download and extract the OpenDJ zip archive.
</para>
<para>
Find a link to the OpenDJ download page at <link xlink:show="new"
xlink:href="http://forgerock.com/download-stack/" />.
</para>
</step>
<step>
<para>
Install OpenDJ using the command-line setup. During the install, import
the LDIF data required for this sample:
</para>
<screen><userinput>$ cd /path/to/opendj
$ /setup --cli \
--hostname localhost \
--ldapPort 1389 \
--rootUserDN "cn=Directory Manager" \
--rootUserPassword password \
--adminConnectorPort 4444 \
--baseDN dc=com \
--ldifFile /path/to/openidm/samples/sample2/data/Example.ldif \
--acceptLicense \
--no-prompt</userinput>
<computeroutput>...
Configuring Directory Server ..... Done.
Importing LDIF file /path/to/openidm/samples/sample2/data/Example.ldif ...... Done.
Starting Directory Server ...... Done..
...</computeroutput></screen>
</step>
</procedure>
</section>
<section xml:id="install-sample2">
<title>Install the Sample</title>
<para>
Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>, then
start OpenIDM with the configuration for sample 2.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample2</screen>
</section>
<section xml:id="run-sample2">
<title>Reconcile the Repository</title>
<para>
The mapping configuration file (<filename>sync.json</filename>) for this
sample includes the mapping <literal>systemLdapAccounts_managedUser</literal>,
which synchronize users from the source LDAP server with the target
OpenIDM repository.
</para>
<para>
You can run this part of the sample by using the <literal>curl</literal>
command-line utility, or by using the OpenIDM Administration UI. This
section provides instructions for both methods.
</para>
<procedure>
<title>To Run the Sample Using the Command Line</title>
<step>
<para>
Reconcile the repository by running the following command:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemLdapAccounts_managedUser&amp;waitForCompletion=true"</userinput>
<computeroutput>{
"state": "SUCCESS",
"_id": "f3c618aa-cc3b-49ed-9a3a-00b012db2513"
}</computeroutput>
</screen>
<para>
The reconciliation operation creates the two users from the LDAP server in
the OpenIDM repository, assigning the new objects random unique IDs.
</para>
</step>
<step>
<para>
To retrieve the users from the repository, query their IDs as follows:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 2,
"result": [
{
"_rev": "0",
"_id": "0a5546d6-149b-4f8b-b3be-4afa8a267d45"
},
{
"_rev": "0",
"_id": "840394b9-ced0-4f13-a5ad-6a0d825f0705"
}
]
}</computeroutput></screen>
</step>
<step>
<para>
To retrieve individual user objects, include the ID in the URL, for
example:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user/0a5546d6-149b-4f8b-b3be-4afa8a267d45"</userinput>
<computeroutput>{
"displayName": "Barbara Jensen",
"stateProvince": "",
"userName": "bjensen",
"postalAddress": "",
"effectiveAssignments": {},
"roles": [
"openidm-authorized"
],
"country": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "Barbara",
"address2": "",
"lastPasswordAttempt": "Mon Nov 24 2014 21:17:19 GMT+0200 (SAST)",
"passwordAttempts": "0",
"sn": "Jensen",
"mail": "bjensen@example.com",
"city": "",
"lastPasswordSet": "",
"postalCode": "",
"_id": "0a5546d6-149b-4f8b-b3be-4afa8a267d45",
"_rev": "1",
"accountStatus": "active",
"description": "Created for OpenIDM",
"telephoneNumber": "1-360-229-7105"
}</computeroutput> </screen>
</step>
</procedure>
<procedure>
<title>To Run the Sample Using the Admin UI</title>
<step>
<para>
Log in to the Admin UI at the URL
<literal>https://localhost:8443/admin</literal> as the default
administrative user (<literal>openidm-admin</literal>) with password
<literal>openidm-admin</literal>.
</para>
<para>
The first time you log into the Admin UI, you are prompted to change your
password. If you do not want to change your password at this time, simply
click X to close this window, and continue with the sample.
</para>
</step>
<step>
<para>
Select the Mappings tab.
</para>
<para>
This tab shows one configured mapping, from the <literal>ldap</literal>
server to the OpenIDM repository (<literal>managed/user</literal>).
</para>
<mediaobject>
<alt>Mappings tab showing the mapping for Sample 2</alt>
<imageobject>
<imagedata fileref="images/sample2-mappings.png" format="PNG"/>
</imageobject>
</mediaobject>
</step>
<step>
<para>
Click anywhere on the mapping and click Reconcile Now.
</para>
<para>
The reconciliation operation creates the two users from the LDAP server in
the OpenIDM repository.
</para>
</step>
<step>
<para>
Retrieve the users in the repository by clicking the Data Management View
link at the top right of the Admin UI.
</para>
<para>
This link opens the Data Management UI. If you did not change your
password in the first step, you are prompted to change your password
again. You can bypass this by clicking X to close the password
prompt window.
</para>
</step>
<step>
<para>
Select the Users tab.
</para>
<mediaobject>
<alt>Users tab showing the users in the repo for Sample 2</alt>
<imageobject>
<imagedata fileref="images/sample2-users.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The two users from the LDAP server have been reconciled to the OpenIDM
repository.
</para>
</step>
<step>
<para>
To retrieve the details of a specific user, click that username on the
Users tab.
</para>
<para>
The following image shows the details of user <literal>bjensen</literal>.
</para>
<mediaobject>
<alt>User details for a specific user</alt>
<imageobject>
<imagedata fileref="images/sample2-bjensen.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
</procedure>
</section>
</section>
<section xml:id="more-sample2b">
<title>Sample 2b - LDAP Two Way</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 2b - LDAP two way</secondary>
</indexterm>
<para>
Like sample 2, sample 2b connects to an external LDAP server. However,
sample 2b has two mappings configured, one from the LDAP server to the
OpenIDM repository, and the other from the OpenIDM repository to the LDAP
server.
</para>
<section xml:id="external-ldap-config-2b">
<title>External LDAP Configuration</title>
<para>
Configure the LDAP server as for sample 2,
<xref linkend="external-ldap-config-2" />. The LDAP user must have write
access to create users from OpenIDM on the LDAP server. When you configure
the LDAP server, import the appropriate LDIF file
(<filename>openidm/samples/sample2b/data/Example.ldif).</filename>
</para>
</section>
<section xml:id="install-sample2b">
<title>Install the Sample</title>
<para>
Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>, then
start OpenIDM with the configuration for sample 2b.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample2b</screen>
</section>
<section xml:id="run-sample2b">
<title>Run the Sample</title>
<para>
The mapping configuration file (<filename>sync.json</filename>) for this
sample includes two mappings, <literal>systemLdapAccounts_managedUser</literal>,
which synchronizes users from the source LDAP server with the target
OpenIDM repository, and <literal>managedUser_systemLdapAccounts</literal>,
which synchronizes changes from the OpenIDM repository to the LDAP server.
</para>
<para>
You can run this part of the sample by using the <literal>curl</literal>
command-line utility, or by using the OpenIDM Administration UI. This
section provides instructions for both methods.
</para>
<procedure>
<title>To Run the Sample Using the Command Line</title>
<step>
<para>
Reconcile the repository over the REST interface by running the following
command:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemLdapAccounts_managedUser&amp;waitForCompletion=true"</userinput>
<computeroutput>{
"state": "SUCCESS",
"_id": "027e25e3-7a33-4858-9080-161c2b40a6bf"
}</computeroutput></screen>
<para>
The reconciliation operation returns a reconciliation run ID and the
status of the operation. Reconciliation creates user objects from LDAP in
the OpenIDM repository, assigning the new objects random unique IDs.
</para>
</step>
<step>
<para>
To retrieve the users from the repository, query their IDs as follows:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 2,
"result": [
{
"_rev": "0",
"_id": "2cb34789-ee67-4f50-88f2-022460c37253"
},
{
"_rev": "0",
"_id": "625942ac-3cb8-47aa-9de1-217d24967b3b"
}
]
}</computeroutput></screen>
</step>
<step>
<para>
To retrieve individual user objects, include the ID in the URL, for
example:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user/625942ac-3cb8-47aa-9de1-217d24967b3b"</userinput>
<computeroutput>{
"displayName": "Barbara Jensen",
"stateProvince": "",
"userName": "bjensen",
"postalAddress": "",
"effectiveAssignments": {},
"roles": [
"openidm-authorized"
],
"country": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "Barbara",
"address2": "",
"lastPasswordAttempt": "Tue Nov 25 2014 12:29:15 GMT+0200 (SAST)",
"passwordAttempts": "0",
"sn": "Jensen",
"mail": "bjensen@example.com",
"city": "",
"lastPasswordSet": "",
"postalCode": "",
"_id": "625942ac-3cb8-47aa-9de1-217d24967b3b",
"_rev": "1",
"description": "Created for OpenIDM",
"accountStatus": "active",
"telephoneNumber": "1-360-229-7105"
}</computeroutput></screen>
</step>
<step>
<para>
Test the second mapping by creating a user in the OpenIDM repository.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"mail":"fdoe@example.com",
"sn":"Doe",
"telephoneNumber":"555-1234",
"userName":"fdoe",
"givenName":"Felicitas",
"description":"Felicitas Doe",
"displayName":"fdoe"}' \
"https://localhost:8443/openidm/managed/user?_action=create"</userinput>
<computeroutput>{
"displayName": "fdoe",
"stateProvince": "",
"userName": "fdoe",
"postalAddress": "",
"effectiveAssignments": {},
"roles": [
"openidm-authorized"
],
"country": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "Felicitas",
"address2": "",
"lastPasswordAttempt": "Tue Nov 25 2014 12:49:06 GMT+0200 (SAST)",
"passwordAttempts": "0",
"sn": "Doe",
"mail": "fdoe@example.com",
"city": "",
"_rev": "1",
"lastPasswordSet": "",
"postalCode": "",
"_id": "c462fac4-528a-45c5-ba76-d2744952d418",
"description": "Felicitas Doe",
"accountStatus": "active",
"telephoneNumber": "555-1234"
}</computeroutput></screen>
</step>
<step>
<para>
By default, <emphasis>implicit synchronization</emphasis> is enabled for
mappings <emphasis>from</emphasis> the <literal>managed/user</literal>
repository <emphasis>to</emphasis> any external resource. This means that
when you update a managed object, any mappings defined in the
<filename>sync.json</filename> file that have the managed object as the
source are automatically executed to update the target system. For more
information, see <link xlink:href="integrators-guide#synchronization-mappings-file"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Synchronization
Mappings File</citetitle></link> in the <citetitle>Integrator's
Guide</citetitle>.
</para>
<para>
Test that the implicit reconciliation has been successful by querying the
users in the LDAP directory over REST, as follows:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/ldap/account?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 3,
"result": [
{
"_id": "uid=jdoe,ou=People,dc=example,dc=com",
"dn": "uid=jdoe,ou=People,dc=example,dc=com"
},
{
"_id": "uid=bjensen,ou=People,dc=example,dc=com",
"dn": "uid=bjensen,ou=People,dc=example,dc=com"
},
{
"_id": "uid=fdoe,ou=People,dc=example,dc=com",
"dn": "uid=fdoe,ou=People,dc=example,dc=com"
}
]
}</computeroutput></screen>
<para>
Note the new entry for user <literal>fdoe</literal>.
</para>
</step>
<step>
<para>
Query the complete entry by including <literal>fdoe</literal>'s ID in
the URL.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/ldap/account/uid=fdoe,ou=People,dc=example,dc=com"</userinput>
<computeroutput>{
"_id": "uid=fdoe,ou=People,dc=example,dc=com",
"dn": "uid=fdoe,ou=People,dc=example,dc=com",
"mail": "fdoe@example.com",
"uid": "fdoe",
"ldapGroups": [],
"cn": "fdoe",
"givenName": "Felicitas",
"telephoneNumber": "555-1234",
"description": "Felicitas Doe",
"sn": "Doe"
}</computeroutput></screen>
</step>
</procedure>
<procedure>
<title>To Run the Sample Using the Admin UI</title>
<step>
<para>
Log in to the Admin UI at the URL
<literal>https://localhost:8443/admin</literal> as the default
administrative user (<literal>openidm-admin</literal>) with password
<literal>openidm-admin</literal>.
</para>
<para>
The first time you log into the Admin UI, you are prompted to change your
password. If you do not want to change your password at this time, simply
click X to close this window, and continue with the sample.
</para>
</step>
<step>
<para>
Select the Mappings tab.
</para>
<para>
This tab shows two configured mappings, one from the
<literal>ldap</literal> server to the OpenIDM repository
(<literal>managed/user</literal>) and one from the OpenIDM repository to
the <literal>ldap</literal> server.
</para>
<mediaobject>
<alt>Mappings tab showing the mapping for Sample 2b</alt>
<imageobject>
<imagedata fileref="images/sample2b-mappings.png" format="PNG"/>
</imageobject>
</mediaobject>
</step>
<step>
<para>
Click anywhere on the first mapping and click Reconcile Now.
</para>
<mediaobject>
<alt>Mappings tab showing reconciliation for Sample 2b</alt>
<imageobject>
<imagedata fileref="images/sample2b-reconcile.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The reconciliation operation creates the two users from the LDAP server in
the OpenIDM repository.
</para>
</step>
<step>
<para>
Retrieve the users in the repository by clicking the Data Management View
link at the top right of the Admin UI.
</para>
<para>
This link opens the Data Management UI. If you did not change your
password in the first step, you are prompted to change your password
again. You can bypass this by clicking X to close the password prompt
window.
</para>
</step>
<step>
<para>
Select the Users tab.
</para>
<mediaobject>
<alt>Users tab showing the users in the repo for Sample 2b</alt>
<imageobject>
<imagedata fileref="images/sample2-users.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The two users from the LDAP server have been reconciled to the OpenIDM
repository.
</para>
</step>
<step>
<para>
To retrieve the details of a specific user, click that username on the
Users tab.
</para>
<para>
The following image shows the details of user <literal>bjensen</literal>.
</para>
<mediaobject>
<alt>User details for a specific user</alt>
<imageobject>
<imagedata fileref="images/sample2-bjensen.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
<step>
<para>
Add a new user in the OpenIDM repository by clicking Add User on the Users
tab.
</para>
<para>
Complete the user details and click Create.
</para>
<para>
The following image shows a new record being created for user
<literal>fdoe</literal>.
</para>
<mediaobject>
<alt>New user record for fdoe</alt>
<imageobject>
<imagedata fileref="images/sample2b-new-user.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
<step>
<para>
By default, <emphasis>implicit synchronization</emphasis> is enabled for
mappings <emphasis>from</emphasis> the <literal>managed/user</literal>
repository <emphasis>to</emphasis> any external resource. This means that
when you update a managed object, any mappings defined in the
<filename>sync.json</filename> file that have the managed object as the
source are automatically executed to update the target system. For more
information, see <link xlink:href="integrators-guide#synchronization-mappings-file"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Synchronization
Mappings File</citetitle></link> in the <citetitle>Integrator's
Guide</citetitle>.
</para>
<para>
To test that the implicit reconciliation has been successful, look at
<literal>fdoe</literal>'s record in the Data Management UI. At the
bottom of the user profile, the Linked Systems panel indicates the
external resource to which this user entry is mapped.
</para>
<para>
The following image shows that <literal>fdoe</literal> now exists in the
linked resource <literal>ldap account</literal>, and shows her entry as it
appears on the LDAP server.
</para>
<mediaobject>
<alt>New user record for fdoe</alt>
<imageobject>
<imagedata fileref="images/sample2b-fdoe.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
</procedure>
</section>
</section>
<section xml:id="more-sample2c">
<title>Sample 2c - Synchronizing LDAP Group Membership</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 2c - Synchronizing LDAP Group Membership</secondary>
</indexterm>
<para>
Like sample 2b, sample 2c connects to an external LDAP server and has
mappings from the LDAP server to the OpenIDM repository, and from the
OpenIDM repository to the LDAP server. However, in sample 2c, LDAP group
memberships are synchronized, in addition to user entries.
</para>
<section xml:id="external-ldap-config-2c">
<title>External LDAP Configuration</title>
<para>
Configure the LDAP server as for sample 2,
<xref linkend="external-ldap-config-2" />. The LDAP user must have write
access to create users from OpenIDM on the LDAP server. When you configure
the LDAP server, import the appropriate LDIF file
(<filename>openidm/samples/sample2c/data/Example.ldif</filename>). This file
includes the following two LDAP groups:
</para>
<programlisting language="ldif">
dn: ou=Groups,dc=example,dc=com
ou: Groups
objectClass: organizationalUnit
objectClass: top
dn: cn=openidm,ou=Groups,dc=example,dc=com
uniqueMember: uid=jdoe,ou=People,dc=example,dc=com
cn: openidm
objectClass: groupOfUniqueNames
objectClass: top
dn: cn=openidm2,ou=Groups,dc=example,dc=com
uniqueMember: uid=bjensen,ou=People,dc=example,dc=com
cn: openidm2
objectClass: groupOfUniqueNames
objectClass: top</programlisting>
<para>
The users with DNs <literal>uid=jdoe,ou=People,dc=example,dc=com</literal>
and <literal>uid=bjensen,ou=People,dc=example,dc=com</literal> are also
imported with the <filename>Example.ldif</filename> file.
</para>
</section>
<section xml:id="install-sample2c">
<title>Install the Sample</title>
<para>
Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>,
then start OpenIDM with the configuration for sample 2c.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample2c</screen>
</section>
<section xml:id="run-sample2c">
<title>Run the Sample</title>
<para>
The mapping configuration file (<filename>sync.json</filename>) for this
sample includes two mappings, <literal>systemLdapAccounts_managedUser</literal>,
which synchronizes users from the source LDAP server with the target
OpenIDM repository, and <literal>managedUser_systemLdapAccounts</literal>,
which synchronizes changes from the OpenIDM repository to the LDAP server.
</para>
<para>
You can run this part of the sample by using the <literal>curl</literal>
command-line utility, or by using the OpenIDM Administration UI. This
section provides instructions for both methods.
</para>
<procedure>
<title>To Run the Sample Using the Command Line</title>
<step>
<para>
Reconcile the repository over the REST interface by running the following
command:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemLdapAccounts_managedUser&amp;waitForCompletion=true"</userinput>
<computeroutput>{
"state": "SUCCESS",
"_id": "8a25d1ca-c0f3-4e0f-82a0-b5c346282782"
}</computeroutput></screen>
<para>
The reconciliation operation returns a reconciliation run ID and the
status of the operation. Reconciliation creates user objects from LDAP in
the OpenIDM repository, assigning the new objects random unique IDs.
</para>
</step>
<step>
<para>
To retrieve the users from the repository, query their IDs as follows:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 2,
"result": [
{
"_rev": "0",
"_id": "40f41f52-44a0-40fe-83ab-a95b92a25805"
},
{
"_rev": "0",
"_id": "cec9e2d7-cb6c-4622-94a8-2cb43695476f"
}
]
}</computeroutput></screen>
</step>
<step>
<para>
To retrieve individual user objects, include the ID in the URL. The
following request retrieves the user object for John Doe:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user/cec9e2d7-cb6c-4622-94a8-2cb43695476f"</userinput>
<computeroutput>{
"displayName": "John Doe",
"userName": "jdoe",
"stateProvince": "",
"postalAddress": "",
"ldapGroups": [
"cn=openidm,ou=Groups,dc=example,dc=com"
],
"effectiveAssignments": {},
"roles": [
"openidm-authorized"
],
"country": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "John",
"address2": "",
"lastPasswordAttempt": "Wed Nov 26 2014 14:30:58 GMT+0200 (SAST)",
"passwordAttempts": "0",
"sn": "Doe",
"mail": "jdoe@example.com",
"city": "",
"lastPasswordSet": "",
"postalCode": "",
"_id": "cec9e2d7-cb6c-4622-94a8-2cb43695476f",
"_rev": "1",
"description": "Created for OpenIDM",
"accountStatus": "active",
"telephoneNumber": "1-415-599-1100"
}</computeroutput></screen>
<para>
Note that John Doe's user object contains an <literal>ldapGroups</literal>
property, the value of which indicates his groups on the LDAP server:
</para>
<programlisting>"ldapGroups":["cn=openidm,ou=Groups,dc=example,dc=com"]</programlisting>
</step>
<step>
<para>
Update John Doe's <literal>ldapGroups</literal> property, to change his
membership from the <literal>openidm</literal> group to the
<literal>openidm2</literal> group.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '[
{
"operation":"replace",
"field":"/ldapGroups",
"value": ["cn=openidm2,ou=Groups,dc=example,dc=com"]
}
]' \
"https://localhost:8443/openidm/managed/user?_action=patch&amp;_queryId=for-userName&amp;uid=jdoe"</userinput>
<computeroutput>{
"displayName": "John Doe",
"userName": "jdoe",
"stateProvince": "",
"postalAddress": "",
"effectiveAssignments": {},
"ldapGroups": [
"cn=openidm2,ou=Groups,dc=example,dc=com"
],
"roles": [
"openidm-authorized"
],
"city": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "John",
"lastPasswordAttempt": "Wed Nov 26 2014 14:30:58 GMT+0200 (SAST)",
"address2": "",
"passwordAttempts": "0",
"sn": "Doe",
"mail": "jdoe@example.com",
"country": "",
"_rev": "2",
"lastPasswordSet": "",
"postalCode": "",
"_id": "cec9e2d7-cb6c-4622-94a8-2cb43695476f",
"description": "Created for OpenIDM",
"accountStatus": "active",
"telephoneNumber": "1-415-599-1100"
}</computeroutput> </screen>
<para>
This command changes John Doe's <literal>ldapGroups</literal> property in
the OpenIDM repository, from
<literal>"cn=openidm,ou=Groups,dc=example,dc=com"</literal> to
<literal>"cn=openidm2,ou=Groups,dc=example,dc=com"</literal>. As a result
of implicit synchronization, the change is propagated to the LDAP server.
John Doe is removed from the first LDAP group and added to the second LDAP
group in OpenDJ.
</para>
</step>
<step>
<para>
You can verify this change by querying John Doe's record on the LDAP
server, as follows:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/ldap/account/uid=jdoe,ou=People,dc=example,dc=com"</userinput>
<computeroutput>{
"_id": "uid=jdoe,ou=People,dc=example,dc=com",
"telephoneNumber": "1-415-599-1100",
"description": "Created for OpenIDM",
"sn": "Doe",
"dn": "uid=jdoe,ou=People,dc=example,dc=com",
"ldapGroups": [
"cn=openidm2,ou=Groups,dc=example,dc=com"
],
"uid": "jdoe",
"cn": "John Doe",
"givenName": "John",
"mail": "jdoe@example.com"
}</computeroutput>
</screen>
</step>
</procedure>
<procedure>
<title>To Run the Sample Using the Admin UI</title>
<step>
<para>
Log in to the Admin UI at the URL
<literal>https://localhost:8443/admin</literal> as the default
administrative user (<literal>openidm-admin</literal>) with password
<literal>openidm-admin</literal>.
</para>
<para>
The first time you log into the Admin UI, you are prompted to change your
password. If you do not want to change your password at this time, simply
click X to close this window, and continue with the sample.
</para>
</step>
<step>
<para>
Select the Mappings tab.
</para>
<para>
This tab shows two configured mappings, one from the
<literal>ldap</literal> server to the OpenIDM repository
(<literal>managed/user</literal>) and one from the OpenIDM repository to
the <literal>ldap</literal> server.
</para>
<mediaobject>
<alt>Mappings tab showing the mapping for Sample 2c</alt>
<imageobject>
<imagedata fileref="images/sample2b-mappings.png" format="PNG"/>
</imageobject>
</mediaobject>
</step>
<step>
<para>
Click anywhere on the first mapping and click Reconcile Now.
</para>
<mediaobject>
<alt>Mappings tab showing reconciliation for Sample 2c</alt>
<imageobject>
<imagedata fileref="images/sample2b-reconcile.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The reconciliation operation creates the two users from the LDAP server in
the OpenIDM repository.
</para>
</step>
<step>
<para>
Retrieve the users in the repository by clicking the Data Management View
link at the top right of the Admin UI.
</para>
<para>
This link opens the Data Management UI. If you did not change your
password in the first step, you are prompted to change your password
again. You can bypass this by clicking X to close the password prompt
window.
</para>
</step>
<step>
<para>
Select the Users tab.
</para>
<mediaobject>
<alt>Users tab showing the users in the repo for Sample 2c</alt>
<imageobject>
<imagedata fileref="images/sample2-users.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The two users from the LDAP server have been reconciled to the OpenIDM
repository.
</para>
</step>
<step>
<para>
To retrieve the details of a specific user, click that username on the
Users tab.
</para>
<para>
The following image shows the details of user <literal>jdoe</literal>.
</para>
<mediaobject>
<alt>User details for a specific user</alt>
<imageobject>
<imagedata fileref="images/sample2c-jdoe.png" format="PNG" />
</imageobject>
</mediaobject>
<para>
Note the Linked Systems section of John Doe's profile. The Linked Resource
item indicates the external resource on which John Doe's managed object
is mapped, in this case, <literal>ldap account</literal>.
</para>
<para>
In this linked resource, John Doe's <literal>ldapGroups</literal> are
displayed. Currently, John Doe is a member of
<literal>cn=openidm,ou=Groups,dc=example,dc=com</literal>.
</para>
</step>
<step>
<para>
Update John Doe's <literal>ldapGroups</literal> property, in the
repository, to change his membership from the <literal>openidm</literal>
group to the <literal>openidm2</literal> group.
</para>
<para>
In the current default version of the Data Management UI, only specific
properties of a managed object can be edited. It is therefore not possible
to update John Doe's <literal>ldapGroups</literal> property using the UI.
</para>
<para>
Instead, update his managed/user object over REST, as follows:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '[
{
"operation":"replace",
"field":"/ldapGroups",
"value": ["cn=openidm2,ou=Groups,dc=example,dc=com"]
}
]' \
"https://localhost:8443/openidm/managed/user?_action=patch&amp;_queryId=for-userName&amp;uid=jdoe"</userinput>
<computeroutput>{
"displayName": "John Doe",
"userName": "jdoe",
"stateProvince": "",
"postalAddress": "",
"effectiveAssignments": {},
"ldapGroups": [
"cn=openidm2,ou=Groups,dc=example,dc=com"
],
"roles": [
"openidm-authorized"
],
"city": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "John",
"lastPasswordAttempt": "Wed Nov 26 2014 14:30:58 GMT+0200 (SAST)",
"address2": "",
"passwordAttempts": "0",
"sn": "Doe",
"mail": "jdoe@example.com",
"country": "",
"_rev": "2",
"lastPasswordSet": "",
"postalCode": "",
"_id": "cec9e2d7-cb6c-4622-94a8-2cb43695476f",
"description": "Created for OpenIDM",
"accountStatus": "active",
"telephoneNumber": "1-415-599-1100"
}</computeroutput> </screen>
<para>
This command changes John Doe's <literal>ldapGroups</literal> property in
the OpenIDM repository, from
<literal>"cn=openidm,ou=Groups,dc=example,dc=com"</literal> to
<literal>"cn=openidm2,ou=Groups,dc=example,dc=com"</literal>. As a result
of implicit synchronization, the change is propagated to the LDAP server.
John Doe is removed from the first LDAP group and added to the second LDAP
group in OpenDJ.
</para>
</step>
<step>
<para>
You can verify this change by reloading John Doe's user profile in the
Data Management UI, and looking at the value of his
<literal>ldapGroups</literal> property in the Linked Systems panel.
</para>
<para>
The following image shows the updated record for John Doe, with a new
<literal>ldapGroups</literal> value of
<literal>cn=openidm2,ou=Groups,dc=example,dc=com</literal>.
</para>
<mediaobject>
<alt>Updated ldapGroups property for John Doe</alt>
<imageobject>
<imagedata fileref="images/sample2c-jdoe2.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
</procedure>
</section>
</section>
<section xml:id="more-sample2d">
<title>Sample 2d - Synchronizing LDAP Groups</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 2d - Synchronizing LDAP Groups</secondary>
</indexterm>
<para>Sample 2d also connects to an external LDAP server. This sample
focuses on LDAP Group synchronization.</para>
<section xml:id="external-ldap-config-2d">
<title>External LDAP Configuration</title>
<para>Configure the LDAP server as for sample 2,
<xref linkend="external-ldap-config-2" />. The LDAP user must have write
access to create users from OpenIDM on the LDAP server.</para>
<para>In addition, two LDAP Groups should be created, which can be found in
the LDIF file: <filename>openidm/samples/sample2d/data/Example.ldif</filename>
(if they have not already been added through sample 2c):</para>
<programlisting language="ldif">
dn: ou=Groups,dc=example,dc=com
ou: Groups
objectClass: organizationalUnit
objectClass: top
dn: cn=openidm,ou=Groups,dc=example,dc=com
uniqueMember: uid=jdoe,ou=People,dc=example,dc=com
cn: openidm
objectClass: groupOfUniqueNames
objectClass: top
dn: cn=openidm2,ou=Groups,dc=example,dc=com
uniqueMember: uid=bjensen,ou=People,dc=example,dc=com
uniqueMember: uid=jdoe,ou=People,dc=example,dc=com
cn: openidm2
objectClass: groupOfUniqueNames
objectClass: top
</programlisting>
<para>The user with dn <literal>uid=jdoe,ou=People,dc=example,dc=com</literal>
is also imported with the <filename>Example.ldif</filename> file.</para>
<para>There is an additional user, <literal>bjensen</literal> in the
sample LDIF file. This user is essentially a "dummy" user, provided for
compliance with RFC 4519, which stipulates that every
<literal>groupOfUniqueNames</literal> object must contain at least one
<literal>uniqueMember</literal>. <literal>bjensen</literal> is not actually
used in this sample.</para>
</section>
<section xml:id="install-sample2d">
<title>Install the Sample</title>
<para>Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>,
then start OpenIDM with the configuration for sample 2d.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample2d</screen>
</section>
<section xml:id="run-sample2d">
<title>Running the Sample</title>
<para>
The mapping configuration file (<filename>sync.json</filename>) for this
sample includes three mappings:
</para>
<itemizedlist>
<listitem>
<para><literal>systemLdapAccounts_managedUser</literal></para>
<para>
Synchronizes users from the source LDAP server with the target
OpenIDM repository,
</para>
</listitem>
<listitem>
<para><literal>managedUser_systemLdapAccounts</literal></para>
<para>
Synchronizes changes from the OpenIDM repository to the LDAP server.
</para>
</listitem>
<listitem>
<para><literal>systemLdapGroups_managedGroup</literal></para>
<para>
Synchronizes groups from the source LDAP server with the target OpenIDM
repository.
</para>
</listitem>
</itemizedlist>
<para>
Due to the similarity with other OpenIDM samples, especially samples 2b
and 2c, the focus of this sample is on the mapping unique to this sample,
<literal>systemLdapGroups_managedGroup</literal>.
</para>
<para>
You can run this part of the sample by using the <literal>curl</literal>
command-line utility, or by using the OpenIDM Administration UI. This
section provides instructions for both methods.
</para>
<procedure>
<title>To Run the Sample Using the Command Line</title>
<step>
<para>
Reconcile the repository over the REST interface for the group mapping,
<literal>systemLdapGroups_managedGroup</literal> by running the following
command:
</para>
<screen>$ <userinput> curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemLdapGroups_managedGroup&amp;waitForCompletion=true"</userinput></screen>
<para>
The reconciliation operation returns a reconciliation run ID, and the status
of the operation.
</para>
</step>
<step>
<para>
With the configuration of sample 2d, OpenIDM creates group objects from
LDAP in OpenIDM. To list group objects by ID, run a query over the REST
interface.
</para>
<screen>$ <userinput> curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/group?_queryFilter=true"</userinput></screen>
<para>
The resulting JSON object should include content similar to the
following.
</para>
<programlisting language="javascript">
{
"result" : [ {
"dn" : "cn=openidm,ou=Groups,dc=example,dc=com",
"_id" : "837df489-35d6-48d1-81a5-23792b49838a",
"_rev" : "1",
"description" : [ ],
"uniqueMember" : [ "uid=jdoe,ou=People,dc=example,dc=com" ],
"name" : [ "openidm" ]
}, {
"dn" : "cn=openidm2,ou=Groups,dc=example,dc=com",
"_id" : "7575c1c7-86cf-43bc-bf1d-5c9cfc539124",
"_rev" : "1",
"description" : [ ],
"uniqueMember" : [ "uid=bjensen,ou=People,dc=example,dc=com" ],
"name" : [ "openidm2" ]
} ],
"resultCount" : 2,
"pagedResultsCookie" : null,
"remainingPagedResults" : -1
}</programlisting>
</step>
</procedure>
<procedure>
<title>To Run the Sample Using the Admin UI</title>
<step>
<para>
Log in to the Admin UI at the URL
<literal>https://localhost:8443/admin</literal> as the default
administrative user (<literal>openidm-admin</literal>) with password
<literal>openidm-admin</literal>.
</para>
<para>
The first time you log into the Admin UI, you are prompted to change your
password. If you do not want to change your password at this time,
click X to close this window, and continue with the sample.
</para>
</step>
<step>
<para>
Select the Mappings tab.
</para>
<para>
This tab shows three configured mappings, from the
<literal>ldap</literal> server accounts repository to the OpenIDM
repository (<literal>managed/user</literal>), from the OpenIDM repository
back to the <literal>ldap</literal> server, and from the
<literal>ldap</literal> server group accounts repository to the OpenIDM
<literal>managed/group</literal> repository.
</para>
<mediaobject>
<alt>Mappings tab showing the mapping for Sample 2d</alt>
<imageobject>
<imagedata fileref="images/sample2d-mappings.png" format="PNG"/>
</imageobject>
</mediaobject>
</step>
<step>
<para>
Click anywhere on the third mapping and click Reconcile Now.
</para>
<mediaobject>
<alt>Mappings tab showing reconciliation for Sample 2b</alt>
<imageobject>
<imagedata fileref="images/sample2d-reconcile.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The reconciliation operation creates the two groups from the LDAP server in
the OpenIDM repository.
</para>
</step>
<step>
<para>
Retrieve the groups in the repository by clicking the Correlation link
below the mapping. Scroll down to Data Association Management.
</para>
<mediaobject>
<alt>Data Association Management showing groups for Sample 2d</alt>
<imageobject>
<imagedata fileref="images/sample2d-groups.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The two groups from the LDAP server (source) have been reconciled to the
OpenIDM repository (target).
</para>
</step>
</procedure>
</section>
</section>
<section xml:id="samples-groovy-connector-toolkit">
<title>Using the Groovy Connector Toolkit to Create Scripted Connectors</title>
<para>
OpenICF 1.4 introduces a generic Groovy Connector Toolkit that enables you to
run Groovy scripts on any external resource.
</para>
<para>
The Groovy Connector Toolkit is not a complete connector, in the traditional
sense. Rather, it is a framework within which you must write your own Groovy
scripts to address the requirements of your implementation. Specific scripts
are provided within these samples, which demonstrate how the Groovy Connector
Toolkit can be used. These scripts cannot be used "as is" in your deployment,
but are a good starting point on which to base your customization.
</para>
<section xml:id="more-sample3">
<title>Sample 3 - Using the Groovy Connector Toolkit to Connect to MySQL With ScriptedSQL</title>
<para>
This sample uses a ScriptedSQL implementation of the Groovy Connector
Toolkit in which an instance of the ScriptedSQL framework is made to work
with the <literal>hrdb</literal> schema. The "connector" in this sample is
therefore really an <literal>hrdb</literal> connector that is implemented
with the help of the Groovy Connector Toolkit.
</para>
<para>
The Groovy Connector Toolkit is bundled with OpenIDM ${docTargetVersion}, in
the JAR <filename>openidm/connectors/groovy-connector-${openicf.groovyconnector.version}.jar</filename>.
The connector configuration file for this sample
(<filename>sample3/conf/provisioner.openicf-scriptedsql.json</filename>)
indicates the ScriptedSQL implementation of the Groovy connector as follows:
</para>
<programlisting language="javascript">{
"name" : "scriptedsql",
"connectorRef" : {
"bundleName" : "org.forgerock.openicf.connectors.groovy-connector",
"bundleVersion" : "[1.4.0.0,2.0.0.0)",
"connectorName" : "org.forgerock.openicf.connectors.scriptedsql.ScriptedSQLConnector"
},
... </programlisting>
<para>
This sample demonstrates the use of <firstterm>complex data types</firstterm>.
Complex data types can be stored, retrieved and synchronized like any other
object property. They are stored in the managed data as JSON, represented as
a string, but can be mapped to external resources in any format required.
You can customize the mapping to do additional work with or transformations
on those data types. The sample defines one complex data type,
<literal>cars</literal>, discussed in more detail later in this section.
</para>
<para>
The mapping in this sample shows the use of event hooks to perform an
action. The mapping configuration from the internal repository to the
external <literal>hrdb</literal> database system
(<literal>managedUser_systemHrdb</literal>), defined in the
<filename>sync.json</filename> file, includes two script hooks. The first is
for an <literal>onCreate</literal> event and the second for an
<literal>onUpdate</literal> event. For both events, OpenIDM logs a statement
to the log when a user is created or updated in the external system. The
script source is included in the mapping, but can also be called from an
external file. For more information, see the <link xlink:show="new"
xlink:href="integrators-guide#appendix-scripting"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Scripting
Appendix</citetitle></link> in the <citetitle>Integrators Guide</citetitle>.
</para>
<para>
This sample also demonstrates the configuration of custom scripted
endpoints, defined in the connector configuration
(<filename>provisioner.openicf-scriptedsql.json</filename>).
</para>
<caution>
<para>
Because MySQL cannot "un-hash" user passwords there is no way for a
reconciliation operation to retrieve and store the password from MySQL and
store it in the managed user object. This issue might impact
configurations that support multiple external resources in that passwords
might not be synchronized immediately after reconciliation from MySQL to
the managed/user repository. Users who are missing from managed/user will
be created by the reconciliation but their passwords will be empty. When
those users are synchronized to other external resources, they will have
empty passwords in those resources. Additional scripting might be required
to handle this situation, depending on the requirements of your
deployment.
</para>
</caution>
<para>
This sample configuration assumes a MySQL server, running on the localhost,
and listening on port 3306. The sample database, <literal>hrdb</literal>,
is created over REST.
</para>
<para>
The Groovy scripts required for the sample are located in the
<filename>sample3/tools</filename> directory. You will need to customize
these scripts to address the requirements of your specific deployment,
however, the sample scripts are a good starting point on which to base your
customization.
</para>
<para>
Prepare a fresh installation of OpenIDM before trying this sample.
</para>
<section xml:id="setup-sample3">
<title>Setting Up the Sample</title>
<para>
This sample requires an installed, running MySQL instance. The connection to
the MySQL server is defined in the connector configuration file
(<filename>sample3/conf/provisioner.openicf-scriptedsql.json</filename>).
The default connector configuration file assumes the following connection
details:
</para>
<programlisting language="javascript">"configurationProperties" : {
"username" : "root",
"password" : "password",
"driverClassName" : "com.mysql.jdbc.Driver",
"url" : "jdbc:mysql://localhost:3306/hrdb",
</programlisting>
<itemizedlist>
<para>
As indicated in the preceding excerpt of the connector configuration file,
the sample expects the following MySQL configuration:
</para>
<listitem>
<para>
The database is available on the localhost.
</para>
</listitem>
<listitem>
<para>
The database listens on port 3306.
</para>
</listitem>
<listitem>
<para>
You can connect over the network to the database with user
<literal>root</literal> and password <literal>password</literal>.
</para>
</listitem>
</itemizedlist>
<para>
If you have an existing MySQL instance, running on a different host, or
port, adjust the connector configuration accordingly.
</para>
<procedure>
<step>
<para>
Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>.
</para>
</step>
<step>
<para>
Download the <link xlink:show="new"
xlink:href="http://dev.mysql.com/downloads/connector/j/5.1.html">MySQL
Driver</link>, (MySQL Connector/J, version 5.1 or later) from the MySQL
website. Unpack the download and copy the .jar into the
<filename>openidm/bundle</filename> directory.
</para>
<screen>$ cp mysql-connector-java-<replaceable>version</replaceable>-bin.jar /path/to/openidm/bundle/</screen>
</step>
<step>
<para>
Create an empty <literal>hrdb</literal> database.
</para>
<screen>$ <userinput>mysql -u root -p</userinput>
<computeroutput>Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.25a MySQL Community Server (GPL)</computeroutput>
mysql> <userinput>CREATE DATABASE hrdb CHARACTER SET utf8 COLLATE utf8_bin;</userinput>
<computeroutput>Query OK, 1 row affected (0.00 sec)</computeroutput>
mysql> <userinput>quit</userinput>
<computeroutput>Bye</computeroutput></screen>
</step>
<step>
<para>
Start OpenIDM with the configuration for sample 3.
</para>
<screen>$ <userinput>cd /path/to/openidm</userinput>
$ <userinput>/startup.sh -p samples/sample3</userinput>
<computeroutput>Executing /startup.sh...
Using OPENIDM_HOME: /path/to/openidm
Using PROJECT_HOME: /path/to/openidm/samples/sample3/
Using OPENIDM_OPTS: -Xmx1024m -Xms1024m
Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/samples/sample3/
/conf/logging.properties
Using boot properties at /path/to/openidm/samples/sample3/conf/boot/boot.properties
OpenIDM version "${docTargetVersion}" (revision: 0) null null
-> OpenIDM ready</computeroutput></screen>
</step>
<step>
<para>
Populate the MySQL database with sample data.
</para>
<para>
This step executes a custom script, over the REST interface, that resets
and populates the <literal>hrdb</literal> database.
</para>
<para>
The script is referenced in a system action, defined in the connector
configuration file
(<filename>conf/provisioner.openicf-scriptedsql.json</filename>) as
follows:
</para>
<programlisting language="json">"systemActions" : [
{
"scriptId" : "ResetDatabase",
"actions" : [
{
"systemType" : ".*ScriptedSQLConnector",
"actionType" : "Groovy",
"actionFile" : "tools/ResetDatabaseScript.groovy" }
]
}
... </programlisting>
<para>
You can run this script again, at any point, to reset the database.
Currently, only Groovy script is supported for these types of actions.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system/scriptedsql?_action=script&amp;scriptId=ResetDatabase"</userinput>
<computeroutput>{
"actions": [
{
"result": "Database reset successful."
}
]
}</computeroutput></screen>
<para>
The <literal>hrdb</literal> database should now be populated with sample
data.
</para>
<para>
You can review the contents of the database as follows:
</para>
<screen><userinput>$ mysql -u root -p</userinput>
mysql &gt; <userinput>use hrdb;</userinput>
<computeroutput>Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed</computeroutput>
mysql &gt; <userinput>select * from users;</userinput>
<computeroutput>
+----+--------+--------------+-----------+----------+---------------+--------...
| id | uid | password | firstname | lastname | fullname | email ...
+----+--------+------------------------------------------+-----------+-------...
| 1 | bob | e38ad2149... | Bob | Fleming | Bob Fleming | Bob.Fle...
| 2 | rowley | 2aa60a8ff... | Rowley | Birkin | Rowley Birkin | Rowley....
| 3 | louis | 1119cfd37... | Louis | Balfour | Louis Balfour | Louis.B...
| 4 | john | a1d7584da... | John | Smith | John Smith | John.Sm...
| 5 | jdoe | edba955d0... | John | Doe | John Doe | John.Do...
+----+--------+------------------------------------------+-----------+-------...
5 rows in set (0.00 sec)
</computeroutput></screen>
<note>
<para>
The passwords in the output shown above are hashed to the SHA-1 standard,
as they cannot be read into OpenIDM as clear text. The SHA-1 Hash
function is used for compatibility reasons. Use a more secure algorithm
in a production database.
</para>
</note>
</step>
</procedure>
</section>
<section xml:id="run-sample3">
<title>Reconciling the Repository</title>
<procedure>
<step>
<para>
The mapping configuration file (<filename>sync.json</filename>) for this
sample includes the mapping <literal>systemHrdb_managedUser</literal>,
which synchronize users from the source <literal>hrdb</literal> database
with the target OpenIDM repository.
</para>
<para>
You can test this part of the sample by using the <literal>curl</literal>
command-line utility, or the OpenIDM Administration UI.
</para>
<stepalternatives>
<step>
<para>
To reconcile the repository by using the Administration UI:
</para>
<orderedlist>
<listitem>
<para>
Log in to the Admin UI at the URL
<literal>https://localhost:8443/admin</literal> as the default
administrative user (<literal>openidm-admin</literal>) with password
<literal>openidm-admin</literal>.
</para>
<para>
The first time you log into the Admin UI, you are prompted to change
your password. If you do not want to change your password at this
time, simply click X to close this window, and continue with the
sample.
</para>
</listitem>
<listitem>
<para>
Select the Mappings tab.
</para>
<para>
This tab shows two configured mappings, one from the
<literal>hrdb</literal> database to the OpenIDM repository
(<literal>managed/user</literal>), and one in the opposite direction.
</para>
<mediaobject>
<alt>Mappings tab showing the two mappings for Sample 3</alt>
<imageobject>
<imagedata fileref="images/sample3-mappings.png" format="PNG"/>
</imageobject>
</mediaobject>
</listitem>
<listitem>
<para>
Click on the first mapping (the mapping from
<literal>system/scriptedsql/account</literal> to
<literal>managed/user</literal>).
</para>
</listitem>
<listitem>
<para>
Click Reconcile Now.
</para>
</listitem>
</orderedlist>
</step>
<step>
<para>
To reconcile the repository by using the command-line, launch the
reconciliation operation with the following command:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemHrdb_managedUser&amp;waitForCompletion=true"</userinput>
<computeroutput>{
"state": "SUCCESS",
"_id": "f3c618aa-cc3b-49ed-9a3a-00b012db2513"
}</computeroutput></screen>
</step>
</stepalternatives>
<para>
The reconciliation operation creates the five users from the MySQL
database in the OpenIDM repository.
</para>
</step>
<step>
<para>
Retrieve the list of users from the repository.
</para>
<stepalternatives>
<step>
<para>
To retrieve the users in the repository by using the Administration UI:
</para>
<orderedlist>
<listitem>
<para>
Click the Data Management View link at the top right of the Admin UI.
</para>
<para>
This opens the Data Management UI. If you did not change your password
in the first step, you are prompted to change your password again.
You can bypass this by simply clicking X to close the password prompt
window.
</para>
</listitem>
<listitem>
<para>
Select the Users tab.
</para>
<mediaobject>
<alt>Mappings tab showing the users in the repo for Sample 3</alt>
<imageobject>
<imagedata fileref="images/sample3-users.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The five users from the <literal>hrdb</literal> database have been
reconciled to the OpenIDM repository.
</para>
</listitem>
<listitem>
<para>
To retrieve the details of a specific user, click that username on the
Users tab.
</para>
<para>
The following image shows the details for the user Rowley Birkin.
</para>
<mediaobject>
<alt>Mappings tab showing the users in the repo for Sample 3</alt>
<imageobject>
<imagedata fileref="images/sample3-rowley.png" format="PNG"/>
</imageobject>
</mediaobject>
</listitem>
</orderedlist>
</step>
<step>
<para>
To retrieve the users from the repository by using the command-line,
query the IDs in the repository as follows:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 5,
"result": [
{
"_rev": "0",
"_id": "9ee75d24-899c-4221-97a0-0aca298febd6"
},
{
"_rev": "0",
"_id": "25be1c4c-0c2a-48b6-96f6-58d2e4d2357d"
},
{
"_rev": "0",
"_id": "2850c77b-f51a-4fb2-8cc4-4c1d03108ac2"
},
{
"_rev": "0",
"_id": "126d74e1-1c03-4774-b18d-bd4d1dfdf884"
},
{
"_rev": "0",
"_id": "46570045-0644-45c6-af10-c88ad3df93cc"
}
]
}</computeroutput></screen>
<para>
To retrieve a complete user record, query for an individual user, by his
userName. The following example returns the record for the user
<literal>Rowley Birkin</literal>.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user/?_queryId=for-userName&amp;uid=rowley"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 1,
"result": [
{
"stateProvince": "",
"userName": "rowley",
"postalAddress": "",
"effectiveAssignments": null,
"roles": [
"openidm-authorized"
],
"telephoneNumber": "",
"country": "",
"effectiveRoles": [
"openidm-authorized"
],
"givenName": "Rowley",
"address2": "",
"lastPasswordAttempt": "Wed Nov 05 2014 10:02:02 GMT+0200 (SAST)",
"passwordAttempts": "0",
"sn": "Birkin",
"mail": "Rowley.Birkin@example.com",
"city": "",
"lastPasswordSet": "",
"organization": "SALES",
"postalCode": "",
"_id": "126d74e1-1c03-4774-b18d-bd4d1dfdf884",
"_rev": "1",
"cars": [
{
"model": "328ci",
"year": "2013",
"make": "BMW"
},
{
"model": "ES300",
"year": "2010",
"make": "Lexus"
}
],
"accountStatus": "active"
}
]
} </computeroutput></screen>
</step>
</stepalternatives>
<para>
Regardless of how you have retrieved Rowley Birkin's entry, note the
<literal>"cars"</literal> property in this user's entry. This property
demonstrates a complex object, stored in JSON format in the user
entry, as a list that contains multiple objects. In the MySQL database,
the <literal>car</literal> table joins to the <literal>users</literal>
table through a <literal>cars.users_id</literal> column. The Groovy
scripts read this data from MySQL and repackage it in a way that OpenIDM
can understand. With support for complex objects, the data is passed
through to OpenIDM as a list of <literal>car</literal> objects. Data is
synchronized from OpenIDM to MySQL in the same way. Complex objects can
also be nested to any depth.
</para>
<para>
Group membership (not demonstrated here) is maintained with a traditional
"join table" in MySQL (<literal>groups_users</literal>). OpenIDM does not
maintain group membership in this way, so the Groovy scripts do the work
to translate membership between the two resources.
</para>
</step>
</procedure>
</section>
<section xml:id="sample3-paging">
<title>Using Paging With Sample 3</title>
<para>
All OpenICF ${openicfBundleVersion} connectors support the use of paging
parameters to restrict query results. The following command indicates that
only two records should be returned (<literal>_pageSize=2</literal>) and
that the records should be sorted according to their
<literal>timestamp</literal> and <literal>_id</literal>
(<literal>_sortKeys=timestamp,id</literal>). Including the
<literal>timestamp</literal> in the sort ensures that, as you page through
the set, changes to records that have already been visited are not lost.
Instead, those records are pushed onto the last page.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
'https://localhost:8443/openidm/system/scriptedsql/account?_queryFilter=uid+sw+""&amp;_fields=id,uid&amp;_pageSize=2&amp;_sortKeys=timestamp,id'</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": "2014-09-10 12:18:46.0,2",
"resultCount": 2,
"result": [
{
"_id": "1",
"uid": "bob"
},
{
"_id": "2",
"uid": "rowley"
}
]
}
</computeroutput></screen>
<para>
The <literal>"pagedResultsCookie"</literal> is used by the server to keep
track of the position in the search results. You can ignore the
<literal>"remainingPagedResults": -1</literal> in the output. The real value
of this property is not returned because the scripts that the connector uses
do not do any counting of the records in the resource.
</para>
<para>
Using the <literal>"pagedResultsCookie"</literal> from the previous step,
run a similar query, to retrieve the following set of records in the
database. You must URL-encode the space character between the date and time
in the <literal>"pagedResultsCookie"</literal> value, with
<literal>%20</literal>, as shown in the following example:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/scriptedsql/account?_queryId=query-all-ids&amp;_pageSize=2&amp;_sortKeys=timestamp,id&amp;_pagedResultsCookie2014-09-10%2012:18:46.0,2"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": "2014-09-10 12:18:46.0,4",
"resultCount": 2,
"result": [
{
"_id": "3",
"uid": "louis"
},
{
"_id": "4",
"uid": "john"
}
]
}</computeroutput></screen>
<para>
For more information about paging support, see <link xlink:show="new"
xlink:href="integrators-guide#paging-query-results"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Paging Query
Results</citetitle></link> in the <citetitle>Integrator's Guide</citetitle>.
</para>
</section>
<section xml:id="sample3-updating-mysql">
<title>Reconciling the MySQL Database</title>
<procedure>
<para>
The second mapping defined in the mapping configuration file for this
sample (<literal>managedUser_systemHrdb</literal>) synchronizes users from
the source OpenIDM repository to the target <literal>hrdb</literal>
database. Instead of running a manual reconciliation operation to
synchronize these resources, edit one of the user records in the
OpenIDM UI.
</para>
<step>
<para>
Log in to the OpenIDM UI (https://localhost:8443/openidmui) as any one of
the users that was created in the repository by the previous
reconciliation operation.
</para>
<para>
The initial clear text passwords of these users can be seen in the
<literal>sample3/tools/ResetDatabaseScript.groovy</literal> script and are
provided here for ease of reference:
<literallayout>"bob", "password1"
"rowley", "password2"
"louis", "password3"
"john", "password4"
"jdoe", "password5"</literallayout>
</para>
<para>
Regular users can update their profiles or passwords. Any changes are
automatically synchronized back to the <literal>hrdb</literal> database,
through the implicit synchronization mechanism.
</para>
<para>
For example, log into the UI as user <literal>john</literal> with the
password <literal>password4</literal>.
</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/john-login.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
<step>
<para>
Update the password or profile of the user <literal>john</literal> (or the
user as whom you logged in in the previous step).
</para>
<para>
The following example updates the email address for user
<literal>john</literal> to <literal>John.P.Smith@example.com</literal>.
</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/john-address-change.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
<step>
<para>
Changes are automatically synchronized to the <literal>hrdb</literal>
database. Retrieve user <literal>john</literal>'s record from the database
by including his <literal>_id</literal> in a query on the system endpoint,
for example:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/scriptedsql/account/4"</userinput>
<computeroutput>{
"_id": "4",
"email": "John.P.Smith@example.com",
"organization": "SUPPORT",
"firstName": "John",
"lastName": "Smith",
"uid": "john",
"fullName": "John Smith"
}</computeroutput></screen>
</step>
</procedure>
</section>
</section>
<section xml:id="sample-scripted-rest">
<title>Sample - Using the Groovy Connector Toolkit to Connect to OpenDJ With ScriptedREST</title>
<indexterm>
<primary>Samples</primary>
<secondary>ScriptedREST Connector</secondary>
</indexterm>
<para>
This sample uses the Groovy Connector Toolkit to implement a ScriptedREST
connector, which interacts with the OpenDJ REST API.
</para>
<para>
The Groovy Connector Toolkit is bundled with OpenIDM ${docTargetVersion}, in
the JAR <filename>openidm/connectors/groovy-connector-${openicf.groovyconnector.version}.jar</filename>.
</para>
<para>
The connector configuration file for this sample
(<filename>samples/scriptedrest2dj/conf/provisioner.openicf-scriptedrest.json</filename>)
indicates the ScriptedREST implementation of the Groovy connector as follows:
</para>
<programlisting language="javascript">{
"name": "scriptedrest",
"connectorRef": {
"connectorHostRef": "#LOCAL",
"connectorName": "org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConnector",
"bundleName": "org.forgerock.openicf.connectors.groovy-connector",
"bundleVersion": "[1.4.0.0,2.0.0.0)"
},
... </programlisting>
<para>
The Groovy scripts required for the sample are located in the
<filename>samples/scriptedrest2dj/tools</filename> directory. You will need to
customize these scripts to address the requirements of your specific
deployment, however, the sample scripts are a good starting point on which to
base your customization.
</para>
<section xml:id="sample-scripted-rest-opendj">
<title>Setting Up OpenDJ</title>
<para>
This sample assumes an OpenDJ server, running on the localhost. Follow these
steps to install and configure an OpenDJ instance.
</para>
<procedure>
<step>
<para>
Download and extract the OpenDJ zip archive.
</para>
<para>
Find a link to the OpenDJ download page from the <link xlink:show="new"
xlink:href="http://forgerock.com/download-stack/" />.
</para>
</step>
<step>
<para>
Install OpenDJ using the command-line setup, as follows:
</para>
<screen><userinput>$ cd /path/to/opendj
$ /setup --cli \
--hostname localhost \
--ldapPort 1389 \
--rootUserDN "cn=Directory Manager" \
--rootUserPassword password \
--adminConnectorPort 4444 \
--addBaseEntry \
--baseDN dc=com \
--acceptLicense \
--no-prompt</userinput>
<computeroutput>...
Configuring Directory Server ..... Done.
Creating Base Entry dc=com ..... Done.
Starting Directory Server ....... Done.
...</computeroutput>
</screen>
<para>
The sample assumes the following configuration:
</para>
<itemizedlist>
<listitem>
<para>
The server is installed on the localhost.
</para>
</listitem>
<listitem>
<para>
The server listens for LDAP connections on port 1389.
</para>
</listitem>
<listitem>
<para>
The administration connector port is 4444.
</para>
</listitem>
<listitem>
<para>
The root user DN is <literal>cn=Directory Manager</literal>.
</para>
</listitem>
<listitem>
<para>
The root user password is <literal>password</literal>.
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Configure the OpenDJ server for replication.
</para>
<para>
To enable LiveSync, this server must be configured for replication, even
if it does not actually participate in a replication topology. The
following commands configure the server for replication.
</para>
<screen><userinput>$ cd /path/to/opendj/bin
$ /dsconfig create-replication-server \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name "Multimaster Synchronization" \
--set replication-port:8989 \
--set replication-server-id:2 \
--type generic \
--trustAll \
--no-prompt
$ /dsconfig create-replication-domain \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name "Multimaster Synchronization" \
--domain-name example_com \
--set base-dn:dc=example,dc=com \
--set replication-server:localhost:8989 \
--set server-id:3 \
--type generic \
--trustAll \
--no-prompt</userinput>
</screen>
</step>
<step>
<para>
Enable HTTP access to the OpenDJ directory server as follows:
</para>
<screen><userinput>$ /dsconfig set-connection-handler-prop \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name "HTTP Connection Handler" \
--set enabled:true \
--set listen-port:8090 \
--no-prompt \
--trustAll</userinput>
</screen>
</step>
<step>
<para>
Enable the OpenDJ HTTP access log.
</para>
<screen><userinput>$ /dsconfig set-log-publisher-prop \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--publisher-name "File-Based HTTP Access Logger" \
--set enabled:true \
--no-prompt \
--trustAll</userinput></screen>
</step>
<step>
<para>
Import the LDIF data required for the sample.
</para>
<screen><userinput>$ /ldapmodify \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--hostname localhost \
--port 1389 \
--filename /path/to/openidm/samples/scriptedrest2dj/data/ldap.ldif</userinput>
<computeroutput>Processing ADD request for dc=example,dc=com
ADD operation successful for DN dc=example,dc=com
Processing ADD request for ou=Administrators,dc=example,dc=com
ADD operation successful for DN ou=Administrators,dc=example,dc=com
Processing ADD request for uid=idm,ou=Administrators,dc=example,dc=com
ADD operation successful for DN uid=idm,ou=Administrators,dc=example,dc=com
Processing ADD request for ou=People,dc=example,dc=com
ADD operation successful for DN ou=People,dc=example,dc=com
Processing ADD request for ou=Groups,dc=example,dc=com
ADD operation successful for DN ou=Groups,dc=example,dc=com</computeroutput>
</screen>
</step>
<step>
<para>
To configure the mapping between JSON resources and LDAP entries, copy the
the configuration file for the HTTP connection handler
(<filename>/path/to/openidm/samples/scriptedrest2dj/data/http-config.json</filename>)
to OpenDJ's configuration directory.
</para>
<screen><userinput>$ cd /path/to/opendj
$ cp /path/to/openidm/samples/scriptedrest2dj/data/http-config.json config/</userinput></screen>
</step>
<step>
<para>
Restart OpenDJ for the configuration change to take effect.
</para>
<screen><userinput>$ cd /path/to/opendj/bin
$ /stop-ds --restart</userinput>
<computeroutput>Stopping Server...
The Directory Server has started successfully</computeroutput></screen>
</step>
</procedure>
<para>
OpenDJ is now configured for this sample.
</para>
</section>
<section xml:id="sample-scripted-rest-running">
<title>Running the Sample</title>
<para>
This section illustrates the basic CRUD operations on users and groups using
the ScriptedREST connector and the OpenDJ REST API. Note that the power of
the Groovy connector is in the associated Groovy scripts, and their
application in your specific deployment. The scripts provided with this
sample are specific to the sample and customization of the scripts is
required.
</para>
<procedure>
<step>
<para>
Start OpenIDM with the configuration for the ScriptedREST sample.
</para>
<screen><userinput>$ cd /path/to/openidm
$ /startup.sh -p samples/scriptedrest2dj/</userinput></screen>
</step>
<step>
<para>
Check the connector configuration is correct by obtaining the status of
the connector, over REST.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system/scriptedrest?_action=test"</userinput>
<computeroutput>{
"ok": true,
"connectorRef": {
"bundleVersion": "[1.4.0.0,2.0.0.0)",
"bundleName": "org.forgerock.openicf.connectors.groovy-connector",
"connectorName": "org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConnector"
},
"objectTypes": [
"group",
"account"
],
"config": "config/provisioner.openicf/scriptedrest",
"enabled": true,
"name": "scriptedrest"
}</computeroutput>
</screen>
</step>
<step>
<para>
Create a group entry on the OpenDJ server.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"_id" : "group1"
}' \
"https://localhost:8443/openidm/system/scriptedrest/group?_action=create"</userinput>
<computeroutput>{
"_id": "group1",
"cn": "group1",
"members": null,
"lastModified": null,
"created": "2014-09-24T17:34:27Z",
"displayName": "group1"
}</computeroutput></screen>
</step>
<step>
<para>
Create a user entry on the OpenDJ server.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"givenName" : "Steven",
"familyName" : "Carter",
"emailAddress" : "scarter@example.com",
"telephoneNumber" : "444-444-4444",
"password" : "Passw0rd",
"displayName" : "Steven.Carter",
"uid" : "scarter"
}' \
https://localhost:8443/openidm/system/scriptedrest/account?_action=create</userinput>
<computeroutput>{
"_id": "scarter",
"displayName": "Steven.Carter",
"uid": "scarter",
"groups": null,
"familyName": "Carter",
"emailAddress": "steven.carter@example.com",
"givenName": "Steven",
"created": "2014-09-24T17:35:46Z",
"telephoneNumber": "444-444-4444"
}</computeroutput></screen>
<para>
Notice that at this stage, the user is not a member of any group.
</para>
</step>
<step>
<para>
Update Steven Carter's entry, by modifying his telephone number.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"givenName" : "Steven",
"familyName" : "Carter",
"emailAddress" : "scarter@example.com",
"telephoneNumber" : "555-555-5555",
"password" : "Passw0rd",
"displayName" : "Steven.Carter",
"uid" : "scarter"
}' \
https://localhost:8443/openidm/system/scriptedrest/account/scarter</userinput>
<computeroutput>{
"_id": "scarter",
"displayName": "Steven.Carter",
"uid": "scarter",
"groups": null,
"familyName": "Carter",
"emailAddress": "steven.carter@example.com",
"givenName": "Steven",
"created": "2014-09-24T17:35:46Z",
"telephoneNumber": "555-555-5555"
}</computeroutput></screen>
</step>
<step>
<para>
Add Steven Carter to the group you created previously, by updating the
group entry.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"_id" : "group1",
"members" : [{"_id" : "scarter"}]
}' \
https://localhost:8443/openidm/system/scriptedrest/group/group1</userinput>
<computeroutput>{
"_id": "group1",
"cn": "group1",
"members": [
{
"displayName": "Steven.Carter",
"_id": "scarter"
}
],
"lastModified": "2014-09-24T17:31:42Z",
"created": "2014-09-24T17:27:37Z",
"displayName": "group1"
}</computeroutput></screen>
</step>
<step>
<para>
Read Steven Carter's entry, to verify that he is now a member of group1.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
https://localhost:8443/openidm/system/scriptedrest/account/scarter</userinput>
<computeroutput>{
"_id": "scarter",
"displayName": "Steven.Carter",
"uid": "scarter",
"groups": [
{
"_id": "group1"
}
],
"familyName": "Carter",
"emailAddress": "steven.carter@example.com",
"givenName": "Steven",
"created": "2014-09-24T17:31:04Z",
"telephoneNumber": "555-555-5555"
}</computeroutput></screen>
</step>
<step>
<para>
Read the group entry to verify its members.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
https://localhost:8443/openidm/system/scriptedrest/group/group1</userinput>
<computeroutput>{
"_id": "group1",
"cn": "group1",
"members": [
{
"displayName": "Steven.Carter",
"_id": "scarter"
}
],
"lastModified": "2014-09-24T17:31:42Z",
"created": "2014-09-24T17:27:37Z",
"displayName": "group1"
}</computeroutput></screen>
</step>
<step>
<para>
Delete the user and group entries, returning the OpenDJ server to its
initial state.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
https://localhost:8443/openidm/system/scriptedrest/account/scarter</userinput>
<computeroutput>{
"_id": "scarter"
}</computeroutput>
<userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
https://localhost:8443/openidm/system/scriptedrest/group/group1</userinput>
<computeroutput>{
"_id": "group1"
}</computeroutput>
</screen>
</step>
</procedure>
</section>
</section>
<section xml:id="sample-scripted-crest">
<title>Sample - Using the Groovy Connector Toolkit to Connect to OpenDJ With ScriptedCREST</title>
<indexterm>
<primary>Samples</primary>
<secondary>ScriptedCREST Connector</secondary>
</indexterm>
<para>
This sample uses the Groovy Connector Toolkit to implement a ScriptedCREST
connector, which interacts with the ForgeRock Commons REST (CREST) API to
connect to an OpenDJ instance. The main difference between a CREST-based API
and a generic REST API is that the CREST API is inherently recognizable by
all ForgeRock products. As such, the sample can leverage CREST resources in
the groovy scripts, to create CREST requests.
<!--TODO link to CREST vs REST section when OPENIDM-2339 is resolved
For more information about the differences between CREST and generic REST,
see <link xlink:show="new" xlink:href="integrators-guide#CRESTvsREST"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Differences
between the CREST API and a Generic REST API</citetitle></link>. -->
</para>
<para>
The Groovy Connector Toolkit is bundled with OpenIDM ${docTargetVersion}, in
the JAR <filename>openidm/connectors/groovy-connector-${openicf.groovyconnector.version}.jar</filename>.
</para>
<para>
The connector configuration file for this sample
(<filename>samples/scriptedcrest2dj/conf/provisioner.openicf-scriptedcrest.json</filename>)
indicates the ScriptedCREST implementation of the Groovy Connector Toolkit
as follows:
</para>
<programlisting language="javascript">{
"name": "scriptedcrest",
"connectorRef": {
"connectorHostRef": "#LOCAL",
"connectorName": "org.forgerock.openicf.connectors.scriptedcrest.ScriptedCRESTConnector",
"bundleName": "org.forgerock.openicf.connectors.groovy-connector",
"bundleVersion": "[1.4.0.0,2.0.0.0)"
},
... </programlisting>
<para>
The Groovy scripts required for the sample are located in the
<filename>samples/scriptedcrest2dj/tools</filename> directory. You will need
to customize these scripts to address the requirements of your specific
deployment, however, the sample scripts are a good starting point on which to
base your customization.
</para>
<section xml:id="sample-scripted-crest-opendj">
<title>Setting Up OpenDJ</title>
<para>
This sample assumes an OpenDJ server, running on the localhost. Follow these
steps to install and configure an OpenDJ instance.
</para>
<procedure>
<step>
<para>
Download and extract the OpenDJ zip archive.
</para>
<para>
Find a link to the OpenDJ download page from the <link xlink:show="new"
xlink:href="http://forgerock.com/download-stack/" />.
</para>
</step>
<step>
<para>
Install OpenDJ using the command-line setup, as follows:
</para>
<screen><userinput>$ cd /path/to/opendj
$ /setup --cli \
--hostname localhost \
--ldapPort 1389 \
--rootUserDN "cn=Directory Manager" \
--rootUserPassword password \
--adminConnectorPort 4444 \
--addBaseEntry \
--baseDN dc=com \
--acceptLicense \
--no-prompt</userinput>
<computeroutput>...
Configuring Directory Server ..... Done.
Creating Base Entry dc=com ..... Done.
Starting Directory Server ....... Done.
...</computeroutput>
</screen>
<para>
The sample assumes the following configuration:
</para>
<itemizedlist>
<listitem>
<para>
The server is installed on the localhost.
</para>
</listitem>
<listitem>
<para>
The server listens for LDAP connections on port 1389.
</para>
</listitem>
<listitem>
<para>
The administration connector port is 4444.
</para>
</listitem>
<listitem>
<para>
The root user DN is <literal>cn=Directory Manager</literal>.
</para>
</listitem>
<listitem>
<para>
The root user password is <literal>password</literal>.
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Configure the OpenDJ server for replication.
</para>
<para>
To enable liveSync, this server must be configured for replication, even
if it does not actually participate in a replication topology. The
following commands configure the server for replication.
</para>
<screen><userinput>$ cd /path/to/opendj/bin
$ /dsconfig create-replication-server \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name "Multimaster Synchronization" \
--set replication-port:8989 \
--set replication-server-id:2 \
--type generic \
--trustAll \
--no-prompt
$ /dsconfig create-replication-domain \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name "Multimaster Synchronization" \
--domain-name example_com \
--set base-dn:dc=example,dc=com \
--set replication-server:localhost:8989 \
--set server-id:3 \
--type generic \
--trustAll \
--no-prompt</userinput>
</screen>
</step>
<step>
<para>
Enable HTTP access to the OpenDJ directory server as follows:
</para>
<screen><userinput>$ /dsconfig set-connection-handler-prop \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--handler-name "HTTP Connection Handler" \
--set enabled:true \
--set listen-port:8090 \
--no-prompt \
--trustAll</userinput>
</screen>
</step>
<step>
<para>
Enable the OpenDJ HTTP access log.
</para>
<screen><userinput>$ /dsconfig set-log-publisher-prop \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--publisher-name "File-Based HTTP Access Logger" \
--set enabled:true \
--no-prompt \
--trustAll</userinput></screen>
</step>
<step>
<para>
Import the LDIF data required for the sample.
</para>
<screen><userinput>$ /ldapmodify \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--hostname localhost \
--port 1389 \
--filename /path/to/openidm/samples/scriptedcrest2dj/data/ldap.ldif</userinput>
<computeroutput>Processing ADD request for dc=example,dc=com
ADD operation successful for DN dc=example,dc=com
Processing ADD request for ou=Administrators,dc=example,dc=com
ADD operation successful for DN ou=Administrators,dc=example,dc=com
Processing ADD request for uid=idm,ou=Administrators,dc=example,dc=com
ADD operation successful for DN uid=idm,ou=Administrators,dc=example,dc=com
Processing ADD request for ou=People,dc=example,dc=com
ADD operation successful for DN ou=People,dc=example,dc=com
Processing ADD request for ou=Groups,dc=example,dc=com
ADD operation successful for DN ou=Groups,dc=example,dc=com</computeroutput>
</screen>
</step>
<step>
<para>
To configure the mapping between JSON resources and LDAP entries, copy the
the configuration file for the HTTP connection handler
(<filename>/path/to/openidm/samples/scriptedcrest2dj/data/http-config.json</filename>)
to OpenDJ's configuration directory.
</para>
<screen><userinput>$ cd /path/to/opendj
$ cp /path/to/openidm/samples/scriptedcrest2dj/data/http-config.json config/</userinput></screen>
</step>
<step>
<para>
Restart OpenDJ for the configuration change to take effect.
</para>
<screen><userinput>$ cd /path/to/opendj/bin
$ /stop-ds --restart</userinput>
<computeroutput>Stopping Server...
The Directory Server has started successfully</computeroutput></screen>
</step>
</procedure>
<para>
OpenDJ is now configured for this sample.
</para>
</section>
<section xml:id="sample-scripted-crest-running">
<title>Running the Sample</title>
<para>
This section illustrates the basic CRUD operations on users and groups using
the ScriptedCREST connector implementation and the OpenDJ REST API. Note
that the power of the Groovy connector is in the associated Groovy scripts,
and their application in your specific deployment. The scripts provided with
this sample are specific to the sample and customization of the scripts is
required.
</para>
<procedure>
<step>
<para>
Start OpenIDM with the configuration for the ScriptedCREST sample.
</para>
<screen><userinput>$ cd /path/to/openidm
$ /startup.sh -p samples/scriptedcrest2dj/</userinput></screen>
</step>
<step>
<para>
Check the connector configuration is correct by obtaining the status of
the connector, over REST.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system/scriptedcrest?_action=test"</userinput>
<computeroutput>{
"ok": true,
"connectorRef": {
"bundleVersion": "[1.4.0.0,2.0.0.0)",
"bundleName": "org.forgerock.openicf.connectors.groovy-connector",
"connectorName": "org.forgerock.openicf.connectors.scriptedcrest.ScriptedCRESTConnector"
},
"objectTypes": [
"groups",
"users"
],
"config": "config/provisioner.openicf/scriptedcrest",
"enabled": true,
"name": "scriptedcrest"
}</computeroutput>
</screen>
</step>
<step>
<para>
Create a group entry on the OpenDJ server.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"_id" : "group1"
}' \
"https://localhost:8443/openidm/system/scriptedcrest/groups?_action=create"</userinput>
<computeroutput>{
"_rev": "0000000028f53bdf",
"_id": "group1",
"displayName": "group1",
"meta": {
"created": "2014-10-17T07:43:13Z"
}
}</computeroutput></screen>
</step>
<step>
<para>
Create a user entry on the OpenDJ server.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request POST \
--data '{
"name": {
"familyName": "Carter",
"givenName" : "Steven"
},
"contactInformation": {
"emailAddress" : "scarter@example.com",
"telephoneNumber" : "444-444-4444"
},
"password" : "TestPassw0rd",
"displayName" : "Steven.Carter",
"_id" : "scarter"
}' \
"https://localhost:8443/openidm/system/scriptedcrest/users?_action=create"</userinput>
<computeroutput>{
"_rev": "00000000d84482de",
"meta": {
"created": "2014-10-17T08:07:46Z"
},
"userName": "scarter@example.com",
"contactInformation": {
"emailAddress": "scarter@example.com",
"telephoneNumber": "444-444-4444"
},
"name": {
"givenName": "Steven",
"familyName": "Carter"
},
"displayName": "Steven.Carter",
"_id": "scarter"
}</computeroutput></screen>
<para>
Notice that at this stage, the user is not a member of any group.
</para>
</step>
<step>
<para>
Update Steven Carter's entry, by modifying his telephone number.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"name": {
"familyName": "Carter",
"givenName" : "Steven"
},
"contactInformation": {
"emailAddress" : "scarter@example.com",
"telephoneNumber" : "555-555-5555"
},
"password" : "TestPassw0rd",
"displayName" : "Steven.Carter",
"_id" : "scarter"
}' \
"https://localhost:8443/openidm/system/scriptedcrest/users/scarter"</userinput>
<computeroutput>{
"_rev": "00000000eb8ba31c",
"meta": {
"created": "2014-10-17T08:07:46Z",
"lastModified": "2014-10-17T08:25:05Z"
},
"userName": "scarter@example.com",
"contactInformation": {
"emailAddress": "scarter@example.com",
"telephoneNumber": "555-555-5555"
},
"name": {
"givenName": "Steven",
"familyName": "Carter"
},
"displayName": "Steven.Carter",
"_id": "scarter"
}</computeroutput></screen>
</step>
<step>
<para>
Add Steven Carter to the group you created previously, by updating the
members of the group entry.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"_id" : "group1",
"members" : [{"_id" : "scarter"}]
}' \
"https://localhost:8443/openidm/system/scriptedcrest/groups/group1"</userinput>
<computeroutput>{
"_rev": "0000000011ed6ea1",
"members": [
{
"displayName": "Steven.Carter",
"_id": "scarter"
}
],
"_id": "group1",
"displayName": "group1",
"meta": {
"created": "2014-10-17T07:43:13Z",
"lastModified": "2014-10-17T08:26:41Z"
}
}</computeroutput></screen>
</step>
<step>
<para>
Read Steven Carter's entry, to verify that he is now a member of group1.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/scriptedcrest/users/scarter"</userinput>
<computeroutput>{
"_rev": "00000000eb8ba31c",
"groups": [
{
"_id": "group1"
}
],
"meta": {
"created": "2014-10-17T08:07:46Z",
"lastModified": "2014-10-17T08:25:05Z"
},
"userName": "scarter@example.com",
"contactInformation": {
"emailAddress": "scarter@example.com",
"telephoneNumber": "555-555-5555"
},
"name": {
"givenName": "Steven",
"familyName": "Carter"
},
"displayName": "Steven.Carter",
"_id": "scarter"
}</computeroutput></screen>
</step>
<step>
<para>
Read the group entry to verify its members.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/scriptedcrest/groups/group1"</userinput>
<computeroutput>{
"_rev": "0000000011ed6ea1",
"members": [
{
"displayName": "Steven.Carter",
"_id": "scarter"
}
],
"_id": "group1",
"displayName": "group1",
"meta": {
"created": "2014-10-17T07:43:13Z",
"lastModified": "2014-10-17T08:26:41Z"
}
}</computeroutput></screen>
</step>
<step>
<para>
Delete the user and group entries, returning the OpenDJ server to its
initial state.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
"https://localhost:8443/openidm/system/scriptedcrest/users/scarter"</userinput>
<computeroutput>{
"_id": "scarter"
}</computeroutput>
<userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
"https://localhost:8443/openidm/system/scriptedcrest/groups/group1"</userinput>
<computeroutput>{
"_id": "group1"
}</computeroutput>
</screen>
</step>
</procedure>
</section>
</section>
<section xml:id="samples-azure">
<title>Sample - Using the Groovy Connector Toolkit to Connect to
Microsoft Azure AD</title>
<indexterm>
<primary>Samples</primary>
<secondary>Microsoft Azure AD Connector</secondary>
</indexterm>
<!-- ref http://azure.microsoft.com/en-us/overview/what-is-azure/ -->
<para>
This sample uses the Groovy Connector Toolkit to implement a Microsoft Azure
AD connector, which interacts with the ForgeRock Commons REST (CREST) API to
connect to an Azure-based instance of Active Directory. The main difference
between a CREST-based API and a generic REST API is that the CREST API is
inherently recognizable by all ForgeRock products. As such, the sample can
leverage CREST resources in the groovy scripts, to create CREST requests.
</para>
<para>
As noted on the <link xlink:show="new"
xlink:href="http://azure.microsoft.com/en-us/overview/what-is-azure/">
<citetitle>Microsoft Azure AD web site</citetitle></link>, Azure is
Microsoft's cloud platform for building, deploying, and
managing applications and services.
</para>
<para>
This section assumes that you have a working knowledge of the Microsoft
Azure AD platform, and can adjust and modify the configuration as needed to
get the information needed to configure a Microsoft Azure AD connector.
</para>
<para>
You will also need to set up OpenIDM as a Microsoft Azure AD website and as
a web application. When you do, retain the values of the URI of the website,
<literal>redirectURI</literal>, the <literal>APP ID URI</literal>, which
corresponds to the <literal>resourceURI</literal>, the
<literal>client ID</literal>, and the <literal>client Secret</literal>.
</para>
<para>
The Groovy Connector Toolkit is bundled with OpenIDM ${docTargetVersion}, in
the following JAR file:
<filename>openidm/connectors/groovy-connector-${openicf.groovyconnector.version}.jar</filename>.
</para>
<para>
The connector configuration file for this sample
(<filename>samples/scriptedazure/conf/provisioner.openicf-scriptedazure.json</filename>)
indicates the Scripted Azure AD implementation of the Groovy Connector
Toolkit as follows:
</para>
<programlisting language="javascript">{
"name": "scriptedazure",
"connectorRef": {
"connectorHostRef": "#LOCAL",
"connectorName": "org.forgerock.openicf.connectors.groovy.ScriptedConnector",
"bundleName": "org.forgerock.openicf.connectors.groovy-connector",
"bundleVersion": "[1.4.0.0,2.0.0.0)"
},
... </programlisting>
<para>
The Groovy scripts required for the sample are located in the
<filename>samples/scriptedazure/tools</filename> directory. You will need
to customize these scripts to address the requirements of your specific
deployment, however, the sample scripts are a good starting point on which to
base your customization.
</para>
<section xml:id="azure-setup">
<title>Identifying Appropriate Information from Microsoft Azure AD</title>
<para>
Microsoft Azure AD supports RESTful communication with the <link xlink:show="new"
xlink:href="http://msdn.microsoft.com/en-us/library/azure/hh974476.aspx">
<citetitle>Azure AD Graph API</citetitle></link>. As suggested by the name,
the API supports access to the Azure-based instance of Active Directory.
</para>
<para>
To get an OAuth 2 access token, you will need to add information to the
Scripted Azure AD provisioner file,
<filename>provisioner.openicf-scriptedazure.json</filename>. Substitute
for the <literal>__configureme__</literal> entries shown.
</para>
<programlisting language="javascript">...
"customConfiguration":"
graphServiceUrl = 'https://graph.windows.net/__configureme__';
oauth2 {
authority = 'https://login.windows.net/__configureme__';
redirectURI = '__configureme__';
resourceURI = 'https://graph.windows.net'
}",
"customSensitiveConfiguration": "
oauth2 {
clientId = '__configureme__';
clientSecret = '__configureme__';
username = '__configureme__';
password = '__configureme__'
}",... </programlisting>
<para>
The following list assumes that you have configured
<literal>contoso.com</literal> as the Azure AD domain tenant. Substitute
appropriately. Except for the first item, the following list of items are
required for an OAuth 2 access token.
</para>
<para>
You should be able to get some of the items needed for the OAuth 2 token
in the Azure AD configuration menu related to the OpenIDM web application.
</para>
<itemizedlist>
<listitem>
<para><literal>graphServiceUrl</literal></para>
<para>
For a domain tenant of <literal>contoso.com</literal>, set this to
<literal>'https://graph.windows.net/contoso.onmicrosoft.com</literal>
</para>
</listitem>
<listitem>
<para><literal>authority</literal></para>
<para>
Set to <literal>'https://login.windows.net/contoso.onmicrosoft.com'</literal>
</para>
</listitem>
<listitem>
<para><literal>redirectURI</literal></para>
<para>
Set to <literal>'https://localhost:8443/admin/index.html#azureCallback'</literal>
</para>
</listitem>
<listitem>
<para><literal>resourceURI</literal></para>
<para>
Keep the following Graph API setting:
<literal>'https://graph.windows.net'</literal>
</para>
</listitem>
<listitem>
<para><literal>clientId</literal></para>
<para>
Set to the Client ID shown in the Configure menu of the Azure AD web
application.
</para>
</listitem>
<listitem>
<para><literal>clientSecret</literal></para>
<para>
Set to the Client Secret shown when you added or configured the web
application. In Azure AD, the Client Secret is the <literal>key</literal>
associated with the Client ID.
</para>
</listitem>
<listitem>
<para><literal>username</literal></para>
<para>
Set to the administrative username for Azure AD.
</para>
</listitem>
<listitem>
<para><literal>password</literal></para>
<para>
Set to the administrative password for Azure AD.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sample-azure-running">
<title>Running the Sample</title>
<para>
This section illustrates basic CRUD operations on users and groups using
the scripted Azure AD connector. Note that the power of the Groovy
connector is in the associated Groovy scripts, and their application in
your specific deployment. The scripts provided with this sample are
specific to the sample and customization of the scripts is required.
</para>
<procedure>
<step>
<para>
Start OpenIDM with the configuration for the scripted Azure AD sample.
</para>
<screen><userinput>$ cd /path/to/openidm
$ /startup.sh -p samples/scriptedazure/</userinput></screen>
</step>
<step>
<para>
Check the connector configuration. Obtain its status over REST.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system/scriptedazure?_action=test"</userinput>
<computeroutput>
{
"ok": true,
"connectorRef": {
"bundleVersion": "[1.4.0.0,2.0.0.0)",
"bundleName": "org.forgerock.openicf.connectors.groovy-connector",
"connectorName": "org.forgerock.openicf.connectors.groovy.ScriptedConnector"
},
"objectTypes": [
"User",
"Device",
"Contact",
"DeviceConfiguration",
"ServicePrincipal",
"Group",
"ExtensionProperty",
"OAuth2PermissionGrant",
"DirectoryRole",
"DirectoryObject",
"DirectoryRoleTemplate",
"AppRoleAssignment",
"DirectoryLinkChange",
"Application",
"__ALL__",
"SubscribedSku",
"TenantDetail"
],
"config": "config/provisioner.openicf/scriptedazure",
"enabled": true,
"name": "scriptedazure"
}</computeroutput>
</screen>
</step>
<step>
<para>
Create a group entry on the Azure AD server.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"_id": "fake placeholder value",
"mailEnabled": false,
"displayName": "Azure AD Test Group",
"mailNickname": "Test_Group",
"securityEnabled": "true",
"description": "Azure AD Test Group"
}' \
"https://localhost:8443/openidm/system/scriptedazure/group?_action=create"
</userinput>
<computeroutput>
{
"mailEnabled":false,
"__NAME__":"ab347420f-4e8e-f045-a437b62c0f3-636e",
"mailNickname":"Test_Group",
"displayName":"Azure AD Test Group",
"mail":null,
"onPremisesSecurityIdentifier":null,
"lastDirSyncTime":null,
"dirSyncEnabled":null,
"description":"Azure AD Test Group",
"proxyAddresses":[
],
"securityEnabled":true,
"provisioningErrors":[
],
"_id":"ab347420f-4e8e-f045-a437b62c0f3-636e"
}</computeroutput>
</screen>
</step>
<step>
<para>
Create a user entry on the Azure AD server.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"_id": "fake placeholder value",
"accountEnabled": true,
"displayName": "Alex Wu",
"mailNickname": "AlexW",
"passwordProfile": { "password" : "Test1234", "forceChangePasswordNextLogin": false },
"userPrincipalName": "Alex@contoso.onmicrosoft.com"
}' \
"https://localhost:8443/openidm/system/scriptedazure/User?_action=create"
</userinput><computeroutput>
{
"userType":"Member",
"assignedLicenses":[
],
"passwordPolicies":null,
"immutableId":null,
"surname":null,
"provisionedPlans":[
],
"state":null,
"accountEnabled":true,
"telephoneNumber":null,
"physicalDeliveryOfficeName":null,
"mail":null,
"__NAME__":"744aa30f-55e0-4559-b956-985b77d80814",
"proxyAddresses":[
],
"otherMails":[
],
"passwordProfile":null,
"assignedPlans":[
],
"facsimileTelephoneNumber":null,
"displayName":"Alex Wu",
"onPremisesSecurityIdentifier":null,
"userPrincipalName":"Alex@contoso.onmicrosoft.com",
"mobile":null,
"dirSyncEnabled":null,
"jobTitle":null,
"givenName":null,
"department":null,
"usageLocation":null,
"preferredLanguage":null,
"sipProxyAddress":null,
"country":null,
"mailNickname":"AlexW",
"postalCode":null,
"lastDirSyncTime":null,
"city":null,
"provisioningErrors":[
],
"streetAddress":null,
"_id":"744aa30f-55e0-4559-b956-985b77d80814"
}</computeroutput></screen>
</step>
<step>
<para>
Update Alex Wu's entry, by modifying his telephone number.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"telephoneNumber" : "555-1234"
}' \
"https://localhost:8443/openidm/system/scriptedazure/User/744aa30f-55e0-4559-b956-985b77d80814"</userinput>
<computeroutput>...
"telephoneNumber" : "555-1234"
...</computeroutput>
</screen>
</step>
<step>
<para>
Confirm the result. Get the data for Alex Wu, by <literal>_id</literal>.
</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/scriptedazure/User/744aa30f-55e0-4559-b956-985b77d80814"</screen>
</step>
<step>
<para>
Get information on the test group, also by <literal>_id</literal>.
</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/scriptedazure/Group/ab347420f-4e8e-f045-a437b62c0f3-636e"</screen>
</step>
<step>
<para>
Delete the entry for user Alex Wu.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
"https://localhost:8443/openidm/system/scriptedazure/User/744aa30f-55e0-4559-b956-985b77d80814"
</userinput>
<computeroutput>{
"_id": "744aa30f-55e0-4559-b956-985b77d80814"
}</computeroutput>
</screen>
</step>
<step>
<para>
Delete the group created earlier.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
"https://localhost:8443/openidm/system/scriptedazure/Group/ab347420f-4e8e-f045-a437b62c0f3-636e"
</userinput>
<computeroutput>{
"_id": "ab347420f-4e8e-f045-a437b62c0f3-636e"
}</computeroutput></screen>
</step>
</procedure>
</section>
</section>
</section>
<section xml:id="samples-powershell">
<title>Sample - Using the PowerShell Connector Toolkit to Create Scripted Connectors</title>
<para>
OpenICF 1.4 introduces a generic PowerShell Connector Toolkit that enables
you to run PowerShell scripts on any external resource.
</para>
<para>
The PowerShell Connector Toolkit is not a complete connector, in the
traditional sense. Rather, it is a framework within which you must write
your own PowerShell scripts to address the requirements of your Microsoft
Windows ecosystem. You can use the PowerShell Connector Toolkit to create
connectors that can provision any Microsoft system.
</para>
<para>
The PowerShell Connector Toolkit is available, with a subscription, from
<link xlink:show="new" xlink:href="https://backstage.forgerock.com/">ForgeRock
Backstage</link>.
</para>
<para>
This sample provides a number of PowerShell scripts that enable you to
perform basic CRUD (create, read, update, delete) operations on an Active
Directory server. The samples use the MS Active Directory PowerShell module.
For more information on this module, see the corresponding <link xlink:show="new"
xlink:href="http://technet.microsoft.com/en-us/library/hh852274.aspx">Microsoft
documentation</link>.
</para>
<para>
The sample assumes that OpenIDM is running on the localhost, and that Active
Directory runs on a remote server. The remote server also runs the OpenICF
.NET connector server.
</para>
<section xml:id="powershell-setup">
<title>Setting Up the PowerShell Sample</title>
<procedure>
<step>
<para>
Install, configure, and start the .NET connector server on the machine
that is running an Active Directory Domain Controller or on a workstation
on which the Microsoft Active Directory PowerShell module is installed.
</para>
<para>
For instructions on installing the .NET connector server, see <link
xlink:show="new" xlink:href="integrators-guide#net-connector-install"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Installing the
.NET Connector Server</citetitle></link> in the <emphasis>Integrator's
Guide</emphasis>.
</para>
</step>
<step>
<para>
Configure OpenIDM to connect to the .NET connector server.
</para>
<para>
To do so, copy the remote connector provisioner file from the
<literal>openidm/samples/provisioners</literal> directory to the
<literal>openidm/conf</literal> directory, and edit the file according to
your configuration.
</para>
<screen>$ <userinput>cd /path/to/openidm</userinput>
$ <userinput>cp samples/provisioners/provisioner.openicf.connectorinfoprovider.json conf/</userinput></screen>
<para>
For instructions on editing this file, see <link
xlink:show="new" xlink:href="integrators-guide#net-connector-openidm"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Configuring
OpenIDM to Connect to the .NET Connector Server</citetitle></link> in the
<emphasis>Integrator's Guide</emphasis>.
</para>
</step>
<step>
<para>
Download the PowerShell Connector Toolkit archive
(<filename>mspowershell-connector-1.4.1.0.zip</filename>) from <link
xlink:show="new" xlink:href="https://backstage.forgerock.com/">ForgeRock
Backstage</link>.
</para>
<para>
Extract the archive and move the <filename>MsPowerShell.Connector.dll</filename>
to the folder in which the connector server application
(<filename>connectorserver.exe</filename>) is located.
</para>
</step>
<step>
<para>
Copy the PowerShell scripts from the <literal>tools</literal> folder in
this sample directory, to the machine on which the connector server is
installed.
</para>
<screen>C:\><userinput>dir openidm\powershell2AD\tools</userinput>
<computeroutput>Directory of C:\openidm\powershell2AD\tools
11/27/2014 12:53 PM &lt;DIR> .
11/27/2014 12:53 PM &lt;DIR> ..
11/26/2014 02:32 PM 2,813 ADAuthenticate.ps1
11/26/2014 02:32 PM 8,546 ADCreate.ps1
11/26/2014 02:32 PM 2,530 ADDelete.ps1
11/26/2014 02:32 PM 2,617 ADResolveUsername.ps1
11/26/2014 02:32 PM 8,644 ADSchema.ps1
11/26/2014 02:32 PM 3,458 ADSearch.ps1
11/26/2014 02:32 PM 4,585 ADSync.ps1
11/26/2014 02:32 PM 2,075 ADTest.ps1
11/26/2014 02:32 PM 8,488 ADUpdate.ps1
9 File(s) 43,756 bytes
2 Dir(s) 23,696,863,232 bytes free
C:\></computeroutput></screen>
</step>
<step>
<para>
Edit the PowerShell search and sync scripts (<filename>ADSearch.ps1</filename>)
and (<filename>ADSync.ps1</filename>) to specify the base suffix for your
Active Directory Domain Controller.
</para>
<para>
Specifically, change the value of the <literal>$searchBase</literal>
parameter in these files to match the suffix in your environment. The
default suffix is:
</para>
<screen>$searchBase = 'CN=Users,DC=example,DC=com'</screen>
</step>
<step>
<para>
Copy the sample connector configuration for the PowerShell connector
from the samples directory to your <literal>conf</literal> directory.
</para>
<screen>$ <userinput>cd /path/to/openidm</userinput>
$ <userinput>cp samples/provisioners/provisioner.openicf-adpowershell.json conf/</userinput></screen>
<para>
The following excerpt of the sample connector configuration shows the
configuration properties:
</para>
<programlisting> "configurationProperties" : {
"AuthenticateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADAuthenticate.ps1",
"CreateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADCreate.ps1",
"DeleteScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADDelete.ps1",
"ResolveUsernameScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADResolveUsername.ps1",
"SchemaScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADSchema.ps1",
"SearchScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADSearch.ps1",
"SyncScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADSync.ps1",
"TestScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADTest.ps1",
"UpdateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADUpdate.ps1",
"VariablesPrefix" : "Connector",
"QueryFilterType" : "AdPsModule",
"ReloadScriptOnExecution" : true,
"UseInterpretersPool" : true,
"SubstituteUidAndNameInQueryFilter" : true,
"UidAttributeName" : "ObjectGUID",
"NameAttributeName" : "DistinguishedName",
"PsModulesToImport" : [ "ActiveDirectory" ],
"Host" : "",
"Port" : null,
"Login" : "",
"Password" : null
},</programlisting>
<para>
The sample connector configuration assumes that the scripts are located in
<filename>C:/openidm/samples/powershell2AD/tools/</filename>. If you
copied your scripts to a different location, adjust your connector
configuration file accordingly.
</para>
<para>
Note that the OpenICF framework requires the path to use forward slash
characters and not the backslash characters that you would expect in a
Windows path.
</para>
<para>
The host, port, login and password of the machine on which Active
Directory runs do not need to be specified here. By default the Active
Directory cmdlets pick up the first available Domain Controller. In
addition, the scripts are executed using the credentials of the .Net
connector server.
</para>
<note>
<para>
The <literal>"ReloadScriptOnExecution"</literal> property is set to
<literal>true</literal> in this sample configuration. This setting causes
script files to be reloaded each time the script is invoked. Having
script files reloaded each time is suitable for debugging purposes.
However, this property should be set to <literal>false</literal> in
production environments, as the script reloading can have a negative
performance impact.
</para>
</note>
<para>
In addition, make sure that the value of the
<literal>"connectorHostRef"</literal> property in the connector
configuration file matches the value that you specified in the remote
connector configuration file, in step 2 of this procedure. For example:
</para>
<screen>"connectorHostRef" : "dotnet",</screen>
</step>
</procedure>
</section>
<section xml:id="powershell-test">
<title>Testing the PowerShell Sample</title>
<para>
Because you have copied all of the required configuration files into the
default OpenIDM project, you can start OpenIDM with the default
configuration (that is, without the <literal>-p</literal> option).
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh</screen>
<para>
When OpenIDM has started, you can test the sample by using the
<literal>curl</literal> command-line utility. The following examples test
the scripts that were provided in the <literal>tools</literal> directory.
</para>
<procedure>
<step>
<para>
Test the connector configuration, and whether OpenIDM is able to connect
to the .NET connector server with the following request.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system?_action=test"</userinput>
<computeroutput>[
{
"ok": true,
"connectorRef": {
"bundleVersion": "1.4.1.0",
"bundleName": "MsPowerShell.Connector",
"connectorName": "Org.ForgeRock.OpenICF.Connectors.MsPowerShell.MsPowerShellConnector"
},
"objectTypes": [
"__ALL__",
"group",
"account"
],
"config": "config/provisioner.openicf/adpowershell",
"enabled": true,
"name": "adpowershell"
}
]</computeroutput> </screen>
</step>
<step>
<para>
Query the users in your Active Directory with the following request:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/adpowershell/account?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 1257,
"result": [
{
"_id": "7c41496a-9898-4074-a537-bed696b6be92",
"distinguishedName": "CN=Administrator,CN=Users,DC=example,DC=com"
},
{
"_id": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9",
"distinguishedName": "CN=Guest,CN=Users,DC=example,DC=com"
},
{
"_id": "99de98a3-c125-48dd-a7c2-e21f1488ab06",
"distinguishedName": "CN=Ben Travis,CN=Users,DC=example,DC=com"
},
{
"_id": "0f7394cc-c66a-404f-ad6d-38dbb4b6526d",
"distinguishedName": "CN=Barbara Jensen,CN=Users,DC=example,DC=com"
},
{
"_id": "3e6fa858-ed3a-4b58-9325-1fca144eb7c7",
"distinguishedName": "CN=John Doe,CN=Users,DC=example,DC=com"
},
{
"_id": "6feef4a0-b121-43dc-be68-a96703a49aba",
"distinguishedName": "CN=Steven Carter,CN=Users,DC=example,DC=com"
},
...</computeroutput></screen>
</step>
<step>
<para>
To return the complete record of a specific user, include the ID of the
user in the URL. The following request returns the record for Steven
Carter.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/adpowershell/account/6feef4a0-b121-43dc-be68-a96703a49aba"</userinput>
<computeroutput>{
"_id": "6feef4a0-b121-43dc-be68-a96703a49aba",
"postalCode": null,
"passwordNotRequired": false,
"cn": "Steven Carter",
"name": "Steven Carter",
"trustedForDelegation": false,
"uSNChanged": "47219",
"manager": null,
"objectGUID": "6feef4a0-b121-43dc-be68-a96703a49aba",
"modifyTimeStamp": "11/27/2014 3:37:16 PM",
"employeeNumber": null,
"sn": "Carter",
"userAccountControl": 512,
"passwordNeverExpires": false,
"displayName": "Steven Carter",
"initials": null,
"pwdLastSet": "130615726366949784",
"scriptPath": null,
"badPasswordTime": "0",
"employeeID": null,
"badPwdCount": "0",
"accountExpirationDate": null,
"userPrincipalName": "steve.carter@ad0.example.com",
"sAMAccountName": "steve.carter",
"mail": "steven.carter@example.com",
"logonCount": "0",
"cannotChangePassword": false,
"division": null,
"streetAddress": null,
"allowReversiblePasswordEncryption": false,
"description": null,
"whenChanged": "11/27/2014 3:37:16 PM",
"title": null,
"lastLogon": "0",
"company": null,
"homeDirectory": null,
"whenCreated": "6/23/2014 2:50:48 PM",
"givenName": "Steven",
"telephoneNumber": "555-2518",
"homeDrive": null,
"uSNCreated": "20912",
"smartcardLogonRequired": false,
"distinguishedName": "CN=Steven Carter,CN=Users,DC=example,DC=com",
"createTimeStamp": "6/23/2014 2:50:48 PM",
"department": null,
"memberOf": [
"CN=employees,DC=example,DC=com"
],
"homePhone": null
}</computeroutput></screen>
</step>
<step>
<para>
Test whether you can authenticate as one of the users in your Active
Directory. The username that you specify here can be either an ObjectGUID,
UPN, sAMAccountname or CN.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system/adpowershell/account?_action=authenticate&amp;username=Steven+Carter&amp;password=Passw0rd"</userinput>
<computeroutput>{
"_id": "6feef4a0-b121-43dc-be68-a96703a49aba"
}</computeroutput></screen>
<para>
The request returns the ObjectGUID if the authentication is successful.
</para>
</step>
<step>
<para>
You can return the complete record for a specific user, using the query
filter syntax described in <link xlink:show="new"
xlink:href="integrators-guide#constructing-queries"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Constructing
Queries</citetitle></link> in the <emphasis>Integrator's Guide</emphasis>.
</para>
<para>
The following query returns the record for the guest user.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/adpowershell/account?_queryFilter=cn+eq+guest"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 1,
"result": [
{
"_id": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9",
"postalCode": null,
"passwordNotRequired": true,
"cn": "Guest",
"name": "Guest",
"trustedForDelegation": false,
"uSNChanged": "8197",
"manager": null,
"objectGUID": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9",
"modifyTimeStamp": "6/9/2014 12:35:16 PM",
"employeeNumber": null,
"userAccountControl": 66082,
"whenChanged": "6/9/2014 12:35:16 PM",
"initials": null,
"pwdLastSet": "0",
"scriptPath": null,
"badPasswordTime": "0",
"employeeID": null,
"badPwdCount": "0",
"accountExpirationDate": null,
"sAMAccountName": "Guest",
"logonCount": "0",
"cannotChangePassword": true,
"division": null,
"streetAddress": null,
"allowReversiblePasswordEncryption": false,
"description": "Built-in account for guest access to the computer/domain",
"userPrincipalName": null,
"title": null,
"lastLogon": "0",
"company": null,
"homeDirectory": null,
"whenCreated": "6/9/2014 12:35:16 PM",
"givenName": null,
"homeDrive": null,
"uSNCreated": "8197",
"smartcardLogonRequired": false,
"distinguishedName": "CN=Guest,CN=Users,DC=example,DC=com",
"createTimeStamp": "6/9/2014 12:35:16 PM",
"department": null,
"memberOf": [
"CN=Guests,CN=Builtin,DC=example,DC=com"
],
"homePhone": null,
"displayName": null,
"passwordNeverExpires": true
}
]
}</computeroutput></screen>
</step>
<step>
<para>
Test whether you are able to create a user on the Active Directory server
by sending a POST request with the <literal>create</literal> action.
</para>
<para>
The following request creates the user <literal>Jane Doe</literal> on the
Active Directory server.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"distinguishedName" : "CN=Jane Doe,CN=Users,DC=example,DC=com",
"sn" : "Doe",
"cn" : "Jane Doe",
"sAMAccountName" : "sample",
"userPrincipalName" : "janedoe@example.com",
"__ENABLE__" : true,
"__PASSWORD__" : "Passw0rd",
"telephoneNumber" : "0052-611-091"
}' \
"https://localhost:8443/openidm/system/adpowershell/account?_action=create"</userinput>
<computeroutput>{
"_id": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
"title": null,
"uSNCreated": "47244",
"pwdLastSet": "130615892934093041",
"cannotChangePassword": false,
"telephoneNumber": "0052-611-091",
"smartcardLogonRequired": false,
"badPwdCount": "0",
"department": null,
"distinguishedName": "CN=Jane Doe,CN=Users,DC=example,DC=com",
"badPasswordTime": "0",
"employeeID": null,
"cn": "Jane Doe",
"division": null,
"description": null,
"userPrincipalName": "janedoe@example.com",
"passwordNeverExpires": false,
"company": null,
"memberOf": [],
"givenName": null,
"streetAddress": null,
"sn": "Doe",
"initials": null,
"logonCount": "0",
"homeDirectory": null,
"employeeNumber": null,
"objectGUID": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
"manager": null,
"lastLogon": "0",
"trustedForDelegation": false,
"scriptPath": null,
"allowReversiblePasswordEncryption": false,
"modifyTimeStamp": "11/27/2014 8:14:53 PM",
"whenCreated": "11/27/2014 8:14:52 PM",
"whenChanged": "11/27/2014 8:14:53 PM",
"accountExpirationDate": null,
"name": "Jane Doe",
"displayName": null,
"homeDrive": null,
"passwordNotRequired": false,
"createTimeStamp": "11/27/2014 8:14:52 PM",
"uSNChanged": "47248",
"sAMAccountName": "sample",
"userAccountControl": 512,
"homePhone": null,
"postalCode": null
}
</computeroutput> </screen>
</step>
<step>
<para>
Test whether you are able to update a user object on the Active Directory
server by sending a PUT request with the complete object, and including
the user ID in the URL.
</para>
<para>
The following request updates user <literal>Jane Doe</literal>'s entry,
including her ID in the request. The update sends the same information
that was sent in the <literal>create</literal> request, but adds an
<literal>employeeNumber</literal>.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"distinguishedName" : "CN=Jane Doe,CN=Users,DC=example,DC=com",
"sn" : "Doe",
"cn" : "Jane Doe",
"sAMAccountName" : "sample",
"userPrincipalName" : "janedoe@example.com",
"__ENABLE__" : true,
"__PASSWORD__" : "Passw0rd",
"telephoneNumber" : "0052-611-091",
"employeeNumber": "567893"
}' \
"https://localhost:8443/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf"</userinput>
<computeroutput>{
"_id": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
"title": null,
"uSNCreated": "47244",
"pwdLastSet": "130615906375709689",
"cannotChangePassword": false,
"telephoneNumber": "0052-611-091",
"smartcardLogonRequired": false,
"badPwdCount": "0",
"department": null,
"distinguishedName": "CN=Jane Doe,CN=Users,DC=example,DC=com",
"badPasswordTime": "0",
"employeeID": null,
"cn": "Jane Doe",
"division": null,
"description": null,
"userPrincipalName": "janedoe@example.com",
"passwordNeverExpires": false,
"company": null,
"memberOf": [],
"givenName": null,
"streetAddress": null,
"sn": "Doe",
"initials": null,
"logonCount": "0",
"homeDirectory": null,
"employeeNumber": "567893",
"objectGUID": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
"manager": null,
"lastLogon": "0",
"trustedForDelegation": false,
"scriptPath": null,
"allowReversiblePasswordEncryption": false,
"modifyTimeStamp": "11/27/2014 8:37:17 PM",
"whenCreated": "11/27/2014 8:14:52 PM",
"whenChanged": "11/27/2014 8:37:17 PM",
"accountExpirationDate": null,
"name": "Jane Doe",
"displayName": null,
"homeDrive": null,
"passwordNotRequired": false,
"createTimeStamp": "11/27/2014 8:14:52 PM",
"uSNChanged": "47253",
"sAMAccountName": "sample",
"userAccountControl": 512,
"homePhone": null,
"postalCode": null
}</computeroutput> </screen>
</step>
<step>
<para>
Test whether you are able to delete a user object on the Active Directory
server by sending a DELETE request with the user ID in the URL.
</para>
<para>
The following request deletes user <literal>Jane Doe</literal>'s entry.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
"https://localhost:8443/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf"</userinput></screen>
<para>
The response includes the complete user object that was deleted.
</para>
<para>
You can you attempt to query the user object to confirm that it has been
deleted.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf"</userinput>
<computeroutput>{
"message": "",
"reason": "Not Found",
"code": 404
}</computeroutput></screen>
</step>
</procedure>
</section>
</section>
<section xml:id="more-sample4">
<title>Sample 4 - CSV to XML File</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 4 - CSV file</secondary>
</indexterm>
<para>Sample 4 works with two databases, a comma-separated value file
and an XML file. There is no need to include any external
resources.
</para>
<para>
A correlation query is used to relate the records in these
two files.
</para>
<section xml:id="install-sample4">
<title>Install the Sample</title>
<para>No external configuration is required for this sample. Prepare
OpenIDM as described in <xref linkend="preparing-openidm"/>, then start up
OpenIDM with the configuration of sample 4.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample4</screen>
</section>
<section xml:id="examine-sample4">
<title>Check and then Run the Sample</title>
<para>
The <filename>sample4/data/hr.csv</filename> file contains two example
users. The first line of the file sets the attribute names.
</para>
<para>
Review the current contents of the database in the
<filename>sample4/data/xmlConnectorData.xml</filename> file. For comparison
purposes, make a copy of the file in a temporary directory with a command
like:
</para>
<screen>$ cp /path/to/openidm/samples/sample4/data/xmlConnectorData.xml /tmp/</screen>
<para>
The reconciliation command run here uses the information
from the <filename>hr.csv</filename> file to update the database in the
<filename>sample4/data/xmlConnectorData.xml</filename> file.
</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=csv_xmlfile&amp;waitForCompletion=true"</screen>
<para>Check the results of reconciliation. Review the updated contents
of the <filename>sample4/data/xmlConnectorData.xml</filename> file.</para>
<para>
If you want to experiment further, try changing the data in the
<filename>hr.csv</filename> file. Run the noted reconciliation command
again.
</para>
<para>
These users will not be visible from the OpenIDM UI, since they are mapped
directly between the XML and CSV files. The internal OpenIDM repository is
not updated in this sample.
</para>
</section>
</section>
<section xml:id="more-sample5">
<title>Sample 5 - Synchronization of Two Resources</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 5 - Synchronization of two resources</secondary>
</indexterm>
<para>
Sample 5 demonstrates the flow of data from one external resource to
another. The resources are named <literal>LDAP</literal> and
<literal>AD</literal> but in the sample, both directory-like resources are
simulated with XML files.
</para>
<para>
You can optionally configure an outbound email service, if you want to
receive emailed reconciliation summaries, as described in the following
section.
</para>
<section xml:id="email-sample5">
<title>Configure Email for the Sample</title>
<para>
If you do not configure the email service, the functionality of the sample
does not change. However, you might see the following message at the
OpenIDM console when you run a reconciliation operation:
</para>
<screen>Email service not configured; report not generated.</screen>
<procedure>
<para>
To configure an email summary, follow these steps:
</para>
<step>
<para>
Copy the template <filename>external.email.json</filename> file from
the <filename>/path/to/openidm/samples/misc</filename> directory:
</para>
<screen>$ cd /path/to/openidm
$ cp samples/misc/external.email.json samples/sample5/conf
</screen>
</step>
<step>
<para>
Edit the <filename>external.email.json</filename> file for outbound email,
as described in the chapter on
<link xlink:show="new" xlink:href="integrators-guide#chap-mail"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Sending
Email</citetitle></link>.
</para>
</step>
<step>
<para>
Edit the <filename>reconStats.js</filename> file from the
<filename>/path/to/openidm/samples/sample5/script</filename> directory.
Near the start of the file, configure the OpenIDM email service to send
statistics to the email addresses of your choice:
</para>
<programlisting language="javascript">
var params = {
//UPDATE THESE VALUES
from : "openidm@example.com",
to : "youremail@example.com",
cc : "idmadmin2@example.com,idmadmin3@example.com",
subject : "Recon stats for " + global.mappingName,
type : "text/html"
},
template,
</programlisting>
</step>
</procedure>
</section>
<section xml:id="install-sample5">
<title>Install the Sample</title>
<para>
No external configuration is required for this sample. Prepare OpenIDM as
described in <xref linkend="preparing-openidm"/>, then start OpenIDM with
the configuration of sample 5.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample5</screen>
<para>
The XML files that simulate the resources are located in the
<filename>openidm/samples/sample5/data/</filename> folder. When you start
OpenIDM with the sample 5 configuration, OpenIDM creates the
<filename>xml_AD_Data.xml</filename> file, which does not contain users
until you run reconciliation.
</para>
</section>
<section xml:id="run-sample5">
<title>Run the Sample</title>
<para>
Run a reconciliation operation, to synchronize the contents of the simulated
LDAP resource to the OpenIDM repository.
</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemLdapAccounts_managedUser&amp;waitForCompletion=true"</screen>
<para>
This command creates a user in the repository. It is not necessary to run a
second reconciliation operation to synchronize the AD resource. Automatic
synchronization propagates any change made to managed users in the OpenIDM
repository to the simulated AD resource.
</para>
<para>
Review the contents of <filename>xml_AD_Data.xml</filename>. It should now
contain information for the same user that was present in the startup
version of the <filename>xml_LDAP_Data.xml</filename> file.
</para>
<para>
Alternatively, you can list users in the AD resource with the following
command:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/ad/account?_queryId=query-all-ids"
</userinput>
<computeroutput>{
"result" : [ {
"name" : "DDOE1",
"__UID__" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98",
"_id" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98"
} ],
"resultCount" : 1,
"pagedResultsCookie" : null,
"remainingPagedResults" : -1
}</computeroutput>
</screen>
<para>
You can use the <literal>_id</literal> of the user to read the user
information from the AD resource, for example:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/ad/account/8dad9df3-820d-41ea-a3ab-a80c241bbc98"
</userinput>
<computeroutput>{
"email" : [ "mail1@example.com" ],
"name" : "DDOE1",
"__UID__" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98",
"firstname" : "Darth",
"lastname" : "Doe",
"_id" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98"
}[</computeroutput></screen>
<para>
To verify that the sample is working, repeat the process. Set up a second
user in the <filename>xml_LDAP_Data.xml</filename> file. An example of how
that file might appear with a second user (<literal>GDOE1</literal>) is
shown here:
</para>
<programlisting language="xml" width="120">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;icf:OpenICFContainer
xmlns:icf="http://openidm.forgerock.com/xml/ns/public/resource/openicf/resource-schema-1.xsd"
xmlns:ri="http://openidm.forgerock.com/xml/ns/public/resource/instances/resource-schema-extension"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://openidm.forgerock.com/xml/ns/public/resource/instances/resource-schema-extension
/path/to/openidm/samples/sample5/data/resource-schema-extension.xsd
http://openidm.forgerock.com/xml/ns/public/resource/openicf/resource-schema-1.xsd
/path/to/openidm/samples/sample5/data/resource-schema-1.xsd"&gt;
&lt;ri:__ACCOUNT__&gt;
&lt;icf:__UID__&gt;1&lt;/icf:__UID__&gt;
&lt;icf:__PASSWORD__&gt;TestPassw0rd2&lt;/icf:__PASSWORD__&gt;
&lt;ri:firstname&gt;Darth&lt;/ri:firstname&gt;
&lt;icf:__DESCRIPTION__&gt;Created By XML1&lt;/icf:__DESCRIPTION__&gt;
&lt;icf:__NAME__&gt;DDOE1&lt;/icf:__NAME__&gt;
&lt;ri:email&gt;mail1@example.com&lt;/ri:email&gt;
&lt;ri:lastname&gt;Doe&lt;/ri:lastname&gt;
&lt;/ri:__ACCOUNT__&gt;
&lt;ri:__ACCOUNT__&gt;
&lt;icf:__UID__&gt;2&lt;/icf:__UID__&gt;
&lt;icf:__PASSWORD__&gt;TestPassw0rd2&lt;/icf:__PASSWORD__&gt;
&lt;ri:firstname&gt;Garth&lt;/ri:firstname&gt;
&lt;icf:__DESCRIPTION__&gt;Created By XML1&lt;/icf:__DESCRIPTION__&gt;
&lt;icf:__NAME__&gt;GDOE1&lt;/icf:__NAME__&gt;
&lt;ri:email&gt;mail2@example.com&lt;/ri:email&gt;
&lt;ri:lastname&gt;Doe&lt;/ri:lastname&gt;
&lt;/ri:__ACCOUNT__&gt;
&lt;/icf:OpenICFContainer&gt;</programlisting>
<para>
Rerun the reconciliation and query REST commands shown previously. The
reconciliation operation creates the new user from the simulated LDAP
resource in the OpenIDM repository. An implicit synchronization operation
then creates that user in the AD resource.
</para>
</section>
</section>
<section xml:id="more-sample5b">
<title>Sample 5b - Failure Compensation With Multiple Resources</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 5b - Failure Compensation With Multiple Resources</secondary>
</indexterm>
<para>
The compensated synchronization mechanism depicted in this sample can help
manage the risks associated with synchronizing data across multiple
resources.
</para>
<para>
Typically, when a managed/user object is changed, implicit synchronization
replays that change to all configured external resources. If synchronization
fails for one target resource (for example, due to a policy validation
failure on the target, or the target being unavailable), the synchronization
operation stops at that point. The effect is that a record might be changed
in the repository, and in the targets on which synchronization was successful,
but not on the failed target, or any targets that would have been
synchronized after the failure. This situation can result in disparate data
sets across resources. While a reconciliation operation would eventually
bring all targets back in sync, reconciliation can be an expensive operation
with large data sets.
</para>
<para>
The compensated synchronization mechanism ensures that either all resources
are synchronized successfully, or that the original change is rolled back.
This mechanism uses an <literal>onSync</literal> script hook configured with
a <filename>compensate.js</filename> script that can be used to "revert" the
partial change to managed/user and to the corresponding external resources.
</para>
<para>
Sample 5b is similar to sample 5 in that it simulates two external resources
with XML files (located in the
<filename>/path/to/samples/sample5b/data</filename> directory). The
<filename>xml_LDAP_Data.xml</filename> file simulates an LDAP data source.
OpenIDM creates the <filename>xml_AD_Data.xml</filename> file when you start
OpenIDM with the sample. Sample 5b adds the <literal>onSync</literal> script
hook to the process, configured in the
<filename>sample5b/conf/managed.json</filename> file.
</para>
<para>
The following excerpt of the <filename>managed.json</filename> file shows
the <literal>onSync</literal> hook, which calls the
<filename>compensate.js</filename> script, provided in the
<filename>/path/to/openidm/bin/defaults/script</filename> directory.
</para>
<programlisting language="javascript">
...
},
"onSync" : {
"type" : "text/javascript",
"file" : "compensate.js"
},
</programlisting>
<para>
You can use the <literal>onSync</literal> script hook to ensure that changes
made in the repository are synchronized to all external resources, or that
no changes are made. For more information about how implicit synchronization
uses the <literal>onSync</literal> script hook, see <link xlink:show="new"
xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#sync-failure-compensation"><citetitle>Configuring
Synchronization Failure Compensation</citetitle></link> in the
<citetitle>Integrator's Guide</citetitle>.
</para>
<para>
You can optionally configure an outbound email service for this sample, if
you want to receive emailed reconciliation summaries. The email service
configuration is identical to that of sample 5
(<xref linkend="email-sample5" />).
</para>
<section xml:id="install-sample5b">
<title>Install the Sample</title>
<para>No external configuration is required for this sample. Prepare
OpenIDM as described in <xref linkend="preparing-openidm"/>, then start
OpenIDM with the configuration of sample 5b.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample5b</screen>
<para>
The XML files that simulate an external LDAP and AD resource are now located
in the <filename>openidm/samples/sample5b/data/</filename> directory. The
simulated AD data store file, <filename>xml_AD_Data.xml</filename>, does not
contain users until you run reconciliation.
</para>
<para>
Run the sample in exactly the same way that you did for Sample 5, following
the steps in <xref linkend="run-sample5" />.
</para>
</section>
<section xml:id="onsync-sample5b">
<title>Demonstrate onSync</title>
<para>
To demonstrate integration of the samples with the OpenIDM UI, this sample
uses the UI to view and make changes to user objects in the repository.
However, you can also use the REST interface to make these changes, as shown
in the previous section.
</para>
<para>
Log into the OpenIDM UI as the administrative user. On a local system,
navigate to <literal>https://localhost:8443/openidmui</literal>. The default
administrative account and password are both <literal>openidm-admin</literal>.
</para>
<para>
Make a change to the data of an existing user (<literal>DDOE1</literal>).
As a result of the implicit synchronization from the managed object
repository, that change is reflected almost immediately on the external
resources. For sample 5b, you should see the changes in both XML files in
the <filename>/path/to/openidm/sample5b/data</filename> directory.
Alternatively, you can query the external resources over REST, as described
previously.
</para>
<para>
The synchronization is successful, across all configured external resources,
so the updated user record can be seen in both the
<filename>xml_LDAP_Data.xml</filename> and
<filename>xml_AD_Data.xml</filename> files.
</para>
<para>
The next step is to simulate a problem connecting to the LDAP resource. One
way to do so on the local system is to rename the LDAP data file so that it
is unreadable. On a Linux system, the following command, as an
administrative user, would serve that purpose:
</para>
<screen>$ cd /path/to/openidm/samples/sample5b/data
$ sudo mv xml_LDAP_Data.xml xml_LDAP_Data.xml.bak</screen>
<para>
In the UI, now try another update to user <literal>DDOE1</literal>. With the
modified filename of the simulated LDAP resource, implicit synchronization
cannot write to this resource. An error similar to the following is
displayed in the log file, <filename>openidm0.log.0</filename>:
</para>
<screen>Data file does not exist:
/path/to/openidm/samples/sample5b/data/xml_LDAP_Data.xml</screen>
<para>
Although the AD resource is available, implicit synchronization will not
reach this resource, because the mapping is specified
<emphasis>after</emphasis> the managed/user to LDAP mapping in the
<filename>sync.json</filename> file.
</para>
<para>
When the implicit synchronization operation fails for the LDAP resource, the
<literal>onSync</literal> hook invokes the
<filename>compensate.js</filename> script. This script attempts to revert
the original change by performing another update to DDOE1 in the repository
(managed/user). This change, in turn, triggers another automatic
synchronization to the AD and LDAP resources.
</para>
<para>
Because the LDAP resource is still unreadable, the synchronization to LDAP
fails again, which triggers the <literal>compensate.js</literal> script
again. This time, however, the script recognizes that the change was
originally called as a result of a compensation and aborts.
</para>
<para>
The original synchronization error from the first update is thrown from the
script and the UI should display that error. If you refresh the UI, and view
that user entry again, you will notice that the change to the entry has been
reverted.
</para>
<para>
Note that if you change the name of the AD resource file (to make it
unavailable), a change to a managed/user entry will be synchronized
successfully with the LDAP resource (because that mapping appears first in
<filename>sync.json</filename>). The synchronization will fail for the AD
resource. In this case, the change will be reverted on both the managed/user
entry, and the LDAP resource.
</para>
</section>
</section>
<section xml:id="more-sample6">
<title>Sample 6 - LiveSync With an AD Server</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 6 - LiveSync between two LDAP servers</secondary>
</indexterm>
<para>
Sample 6 resembles sample 5, but demonstrates LiveSync from an external
resource. Sample 6 includes configuration files for two scenarios, depending
on whether you have a live Active Directory (AD) service, or whether you
need to simulate an AD service with an OpenDJ server. Each scenario is
associated with a file in the
<filename>/path/to/sample6/alternatives</filename> directory. The file you
select should be copied to the <filename>/path/to/sample6/conf</filename>
directory.
</para>
<variablelist>
<varlistentry>
<term>Active AD Deployment</term>
<listitem>
<para>
If you have an actual AD deployment available, copy the
<filename>provisioner.openicf-realad.json</filename> file to the
<filename>conf/</filename> subdirectory. You can then configure
synchronization between an OpenDJ Directory Server and an active AD
deployment.
</para>
<para>
As this sample demonstrates synchronization <emphasis>from</emphasis> the
AD server <emphasis>to</emphasis> OpenDJ, data on the AD server is not
changed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Simulated AD Deployment</term>
<listitem>
<para>
If you need to simulate an AD deployment, copy the
<filename>provisioner.openicf-fakead.json</filename> file to the
<filename>conf/</filename> subdirectory. You can then configure
synchronization between an OpenDJ Directory server and a simulated
AD server.
</para>
<para>
This sample simulates an AD server on the same instance of OpenDJ, using a
different base DN.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
The options shown in the associated configuration files can be easily
modified to work with any standard LDAP server.
</para>
<section xml:id="more-sample6-realad">
<title>Sample 6 with an Active AD Deployment</title>
<para>
If you have an existing, active instance of AD, set up OpenDJ, as described
in the <link xlink:show="new"
xlink:href="http://opendj.forgerock.org/opendj-server/doc/install-guide/">
<citetitle>OpenDJ Installation Guide</citetitle></link>.
</para>
<para>
During installation, populate OpenDJ with the data in the
<filename>Example.ldif</filename> file, available in the
<filename>/path/to/sample6/data</filename> directory.
</para>
<para>
The actions run in this sample should not change any data on the AD server.
</para>
</section>
<section>
<title>Sample 6 with a Simulated AD Deployment</title>
<para>
In this sample, an AD deployment is simulated with a different baseDN
(<literal>dc=fakead,dc=com</literal>) on the same OpenDJ server instance.
You can also simulate the AD server with a separate OpenDJ instance, running
on the same host, as long as the two instances communicate on different
ports. The data for the simulated AD instance is contained in the file
<filename>AD.ldif</filename>. The data for the OpenDJ instance is contained
in the file <filename>Example.ldif</filename>.
</para>
</section>
<section xml:id="external-resource-sample6">
<title>External Configuration</title>
<section xml:id="prepare-livesync-sample6">
<title>Prepare OpenDJ For LiveSync</title>
<para>
This sample assumes a replicated OpenDJ server on the localhost system.
When configured, OpenDJ replication includes an External Change Log (ECL),
required to support LiveSync. LiveSync detects changes in OpenDJ by
reading the ECL.
</para>
<para>
Follow these steps to install and configure an OpenDJ instance.
</para>
<procedure>
<step>
<para>
Download and extract the OpenDJ zip archive.
</para>
<para>
Find a link to the OpenDJ download page from the <link xlink:show="new"
xlink:href="http://forgerock.com/download-stack/" />.
</para>
</step>
<step>
<para>
Install OpenDJ using the command-line setup, as follows:
</para>
<screen><userinput>$ cd /path/to/opendj
$ /setup --cli \
--hostname localhost \
--ldapPort 1389 \
--rootUserDN "cn=Directory Manager" \
--rootUserPassword password \
--adminConnectorPort 4444 \
--addBaseEntry \
--baseDN dc=com \
--acceptLicense \
--no-prompt</userinput>
<computeroutput>...
Configuring Directory Server ..... Done.
Creating Base Entry dc=com ..... Done.
Starting Directory Server ....... Done.
...</computeroutput>
</screen>
<para>
The sample assumes the following configuration:
</para>
<itemizedlist>
<listitem>
<para>
The OpenDJ server is installed on the localhost.
</para>
</listitem>
<listitem>
<para>
The server listens for LDAP connections on port 1389.
</para>
</listitem>
<listitem>
<para>
The administration connector port is 4444.
</para>
</listitem>
<listitem>
<para>
The root user DN is <literal>cn=Directory Manager</literal>.
</para>
</listitem>
<listitem>
<para>
The root user password is <literal>password</literal>.
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Import the LDIF data file required for the sample.
</para>
<screen>$<userinput> cd /path/to/opendj/bin
$ /ldapmodify \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--hostname localhost \
--port 1389 \
--filename /path/to/openidm/samples/sample6/data/Example.ldif</userinput>
</screen>
</step>
<step>
<para>
Configure the OpenDJ server for replication.
</para>
<para>
To enable liveSync, this server must be configured for replication, even
if it does not actually participate in a replication topology. The
following commands configure the server for replication.
</para>
<screen>$<userinput> /dsconfig create-replication-server \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name "Multimaster Synchronization" \
--set replication-port:8989 \
--set replication-server-id:2 \
--type generic \
--trustAll \
--no-prompt
$ /dsconfig create-replication-domain \
--hostname localhost \
--port 4444 \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--provider-name "Multimaster Synchronization" \
--domain-name fakead_com \
--set base-dn:dc=fakead,dc=com \
--set replication-server:localhost:8989 \
--set server-id:3 \
--type generic \
--trustAll \
--no-prompt</userinput>
</screen>
</step>
</procedure>
<para>
Once OpenDJ is configured, you can proceed with either an active or
simulated AD deployment.
</para>
</section>
<section xml:id="external-resource-sample6-active">
<title>External Configuration for an Active AD Deployment</title>
<para>
To configure an active AD deployment for sample 6, open the
<filename>provisioner.openicf-realad.json</filename> file in
a text editor. Update it as needed. At minimum, you should check and
if needed update the following parameters in that file, as shown in the
following table:
</para>
<table xml:id="realad-json-configuration">
<title>Key Parameters for an Active AD Configuration</title>
<tgroup cols="2">
<colspec colnum="1" colwidth="2*"/>
<colspec colnum="2" colwidth="3*"/>
<thead>
<row>
<entry>Option</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>host</entry>
<entry>The hostname/IP address of the AD server</entry>
</row>
<row>
<entry>port</entry>
<entry>The LDAP port; the default is 389.</entry>
</row>
<row>
<entry>ssl</entry>
<entry>By default, SSL is not used.</entry>
</row>
<row>
<entry>principal</entry>
<entry>The full DN of the account to bind with, such as
"CN=Administrator,CN=Users,DC=example,DC=com"</entry>
</row>
<row>
<entry>credentials</entry>
<entry>If a password is used, replace null with that password.
When OpenIDM starts, it encrypts that password in the
<filename>provisioner.openicf-realad.conf</filename> file.</entry>
</row>
<row>
<entry>baseContexts</entry>
<entry>The DNs for account containers, such as
["CN=Users,DC=Example,DC=com"]</entry>
</row>
<row>
<entry>baseContextsToSynchronize</entry>
<entry>Set to the same value as <literal>baseContexts</literal></entry>
</row>
<row>
<entry>accountSearchFilter</entry>
<entry>Default searches for active user (not computer) accounts</entry>
</row>
<row>
<entry>accountSynchronizationFilter</entry>
<entry>Default synchronizes with active user (not computer)
accounts</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
If you do not want to filter out computer and disabled user accounts,
set the <literal>accountSearchFilter</literal> and
<literal>accountSynchronizationFilter</literal> to <literal>null</literal>.
</para>
</section>
<section xml:id="external-resource-sample6-simulated">
<title>External Configuration for a Simulated AD Deployment</title>
<para>
Not everyone has a testable instance of AD readily available. For such
administrators, you can use the <filename>AD.ldif</filename> file from
the <filename>data/</filename> subdirectory to simulate an AD deployment.
</para>
<para>
If you have not already done so, copy the
<filename>provisioner.openicf-fakead.json</filename> file to the
<filename>conf</filename> subdirectory.
</para>
<para>
As previously mentioned, you can use a separate OpenDJ instance to
simulate the AD server. However, the following instructions assume that
the simulated AD server runs on the same OpenDJ instance.
</para>
<para>
Open the <filename>provisioner.openicf-fakead.json</filename> file and
note the following:
</para>
<itemizedlist>
<listitem>
<para>
OpenDJ directory server uses port 1389 by default for users who cannot
use privileged ports, so this is the port that is specified in the
provisioner file. Adjust the port if your OpenDJ server is listening on
a different port.
</para>
</listitem>
<listitem>
<para>
The simulated AD server uses the base DN
<literal>dc=fakead,dc=com</literal>.
</para>
</listitem>
</itemizedlist>
<para>
To load the data for the simulated AD instance, launch the OpenDJ control
panel, add the simulated AD baseDN (<literal>dc=fakead,dc=com</literal>),
and then import the <filename>/path/to/sample6/data/AD.ldif</filename>
file. When you import the <filename>AD.ldif</filename> file, select
"Append to Existing Data", not "Overwrite Existing Data". Otherwise, the
data in the <literal>dc=example,dc=com</literal> baseDN will be
overwritten.
</para>
</section>
</section>
<section xml:id="install-sample6">
<title>Start OpenIDM with Sample 6</title>
<para>
Now that OpenDJ and a real or simulated AD database is configured,
prepare OpenIDM as described in <xref linkend="preparing-openidm"/>.
You can then start OpenIDM with the configuration for sample 6.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample6</screen>
</section>
<section xml:id="run-sample6">
<title>Run the Sample</title>
<para>The following sections show how to run the sample with command-based
reconciliation with a REST call, and to configure scheduled reconciliation
with LiveSync.
</para>
<section xml:id="run-sample6-reconciliation">
<title>Using Reconciliation</title>
<para>
Now that OpenIDM is in operation, review the entries in the OpenDJ
data store. When you run reconciliation, any entries that share the same
<literal>uid</literal> with the AD data store will be updated with the
contents from AD.
</para>
<para>
If you have set up the simulated AD data store as described in
<xref linkend="external-resource-sample6-simulated" />, compare the
entries for <literal>uid=jdoe</literal> as shown in the
<filename>AD.ldif</filename> and <filename>Example.ldif</filename> files.
Note the different values of <literal>givenName</literal> for
<literal>uid=jdoe</literal>.
</para>
<para>
Run reconciliation over the REST interface. If you have followed the
instructions for the simulated AD data store, the following command
takes the information for user <literal>jdoe</literal> imported from the
<filename>AD.ldif</filename> file, with a <literal>givenName</literal> of
Johnny, and synchronizes that information to the LDAP database, overwriting
the <literal>givenName</literal> of John for that same user
<literal>jdoe</literal>.
</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemAdAccounts_managedUser&amp;waitForCompletion=true"</screen>
<para>
The reconciliation operation returns a reconciliation run ID, and the status
of the operation.
</para>
<screen>{
"state": "SUCCESS",
"_id": "985ee939-fbe1-4607-a757-00b404b4ef77"
}</screen>
<para>
The reconciliation operation synchronizes the data in the AD deployment
with OpenIDM repository (managed/user). That information is then
automatically synchronized to the OpenDJ server, as described in
<link xlink:show="new" xlink:href="integrators-guide#handling-sync"
xlink:role="http://docbook.org/xlink/role/olink">
<citetitle>Synchronization Situations and Actions</citetitle></link>.
</para>
<para>After reconciliation, list all users in the OpenDJ server data
store.</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/ldap/account?_queryId=query-all-ids"</userinput></screen>
<para>The result should resemble the following JSON object.</para>
<programlisting language="javascript">{
"result": [ {
"dn" : "uid=jdoe,ou=People,dc=example,dc=com",
"_id" : "uid=jdoe,ou=People,dc=example,dc=com"
}, {
"dn" : "uid=bjensen,ou=People,dc=example,dc=com",
"_id" : "uid=bjensen,ou=People,dc=example,dc=com"
} ],
"resultCount": 2,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</programlisting>
<para>
You see only two entries, as the <literal>uid=jdoe</literal> entry from
<literal>dc=fakead,dc=com</literal> overwrites the original LDAP entry for
<literal>uid=jdoe</literal> in the reconciled LDAP data store.
</para>
<para>
To read the user object in the OpenDJ server, run the
<command>ldapsearch</command> command. The following example returns the
entry for user <literal>uid=jdoe</literal>:
</para>
<screen><userinput>$ /ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
"(uid=jdoe)"</userinput></screen>
</section>
<section xml:id="run-sample6-live-sync">
<title>Using LiveSync</title>
<para>You can start reconciliation by using a scheduled configuration or
by using the REST interface directly. However, to use LiveSync, you must
configure a schedule. When LiveSync is active, the default schedule in the
<filename>schedule-activeSynchroniser_systemAdAccount.json</filename>
configuration file runs LiveSync every 15 seconds.
</para>
<para>
LiveSync pushes changes made in the AD data store to the OpenIDM repository,
automatically.
</para>
<para>
LiveSync is disabled by default. To activate LiveSync, change the
value of the <literal>"enabled"</literal> property
from <literal>false</literal> to <literal>true</literal>.
</para>
<programlisting language="javascript">{
"enabled" : false,
"type" : "cron",
"schedule" : "0/15 * * * * ?",
"invokeService" : "provisioner",
"invokeContext" : {
"action" : "liveSync",
"source" : "system/ad/account"
},
"invokeLogLevel" : "debug"
}</programlisting>
<procedure xml:id="test-live-sync">
<title>Testing LiveSync</title>
<para>
Now you can test LiveSync. This procedure assumes that you have configured
OpenDJ using the parameters and commands described in this section.
</para>
<step>
<para>
Create an LDIF file with a new user entry (<literal>uid=bsmith</literal>)
that will be added to the simulated AD data store.
</para>
</step>
<step>
<para>
The following is the contents of a sample
<filename>bsmith.ldif</filename> file for demonstration purposes:
<screen>dn: uid=bsmith,ou=People,dc=fakead,dc=com
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: top
givenName: Barry
description: Created to see LiveSync work
uid: bsmith
cn: Barry
sn: Smith
mail: bsmith@example.com
telephoneNumber: 1-415-523-0772
userPassword: passw0rd</screen>
</para>
</step>
<step>
<para>
Navigate to the <filename>/path/to/opendj/bin</filename> directory.
</para>
</step>
<step>
<para>
Use the <command>ldapmodify</command> command to add the
<filename>bsmith.ldif</filename> file to the directory.
</para>
<screen><userinput>$ /ldapmodify
--port 1389
--defaultAdd
--bindDN "cn=Directory Manager"
--bindPassword password
--filename /path/to/bsmith.ldif</userinput></screen>
</step>
<step>
<para>
Now you can test synchronization by viewing the new user in the OpenIDM
repository. The easiest way to do this, is through OpenIDM UI. You should
be able to log into the UI with any of the accounts in the AD data store.
For this example, log into the UI as user <literal>bsmith</literal>, with
password <literal>passw0rd</literal>. The fact that you can log into the
UI as this new user indicates that LiveSync has synchronized the user
from the AD data store to the managed/user repository.
</para>
</step>
<step>
<para>
Implicit synchronization pushes this change out to the OpenDJ data store.
To test this synchronization operation, search the OpenDJ baseDN for the
new user entry.
</para>
<screen><userinput>$ /ldapsearch \
--port 1389 \
--baseDN ou=people,dc=example,dc=com \
"(uid=bsmith)"</userinput></screen>
</step>
</procedure>
</section>
</section>
</section>
<section xml:id="more-sample7">
<title>Sample 7 - Scripting a SCIM-like Schema</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 7 - Scripting a SCIM-like Schema</secondary>
</indexterm>
<para>Sample 7 demonstrates how you can use OpenIDM to expose user data with a
SCIM-like schema. The sample uses the XML file connector to read in attributes
from external accounts and construct a JSON object for users stored in the
OpenIDM repository. For more information about SCIM schema, see
<link xlink:show="new"
xlink:href="http://www.simplecloud.info/specs/draft-scim-core-schema-01.html">
<citetitle>System for Cross-Domain Identity Management:
Core Schema 1.1</citetitle></link>.</para>
<section xml:id="install-sample7">
<title>Install the Sample</title>
<para>Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>,
then start OpenIDM with the configuration for sample 7.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample7</screen>
</section>
<section xml:id="run-sample7">
<title>Running the Sample</title>
<para>Run a reconciliation to pull the user from
<filename>samples/sample7/data/xmlConnectorData.xml</filename> into the
OpenIDM internal repository.</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemXmlfileAccounts_managedUser&amp;waitForCompletion=true"</userinput></screen>
<para>Reconciliation creates a user object in the repository. Retrieve the
user from the repository.</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user/DDOE1"</userinput></screen>
<para>The user object has the following JSON representation.</para>
<programlisting language="javascript">{
"_id" : "DDOE1",
"_rev" : "2",
"schemas" : "['urn:scim:schemas:core:1.0']",
"ims" : [ {
"type" : "aim",
"value" : "jonyOnAim"
}, {
"type" : "skype",
"value" : "skyperHiasl"
} ],
"locale" : null,
"phoneNumbers" : [ {
"type" : "work",
"value" : "1234567"
}, {
"type" : "home",
"value" : "1234568"
} ],
"emails" : [ {
"type" : "work",
"value" : "hallo@example.com",
"primary" : true
}, {
"type" : "home",
"value" : "jdoe@forgerock.com"
} ],
"externalId" : "DDOE1",
"preferredLanguage" : "en_US",
"meta" : {
"lastModified" : "Thu May 01 2014 12:57:10 GMT-0800 (GMT-08:00)",
"created" : "Thu May 01 2014 12:56:27 GMT-0800 (GMT-08:00)"
},
"userType" : "permanent",
"photos" : [ {
"type" : "photo",
"value" : "https://photos.example.com/profilephoto/72930000000Ccne/F"
}, {
"type" : "thumbnail",
"value" : "https://photos.example.com/profilephoto/72930000000Ccne/T"
} ],
"title" : "Mr.Universe",
"timezone" : "America/Denver",
"profileUrl" : "https://login.example.com/DDOE1",
"nickName" : "Jonny",
"name" : {
"familyName" : "Doe",
"middleName" : "Hias",
"formatted" : "Dr. John H Doe III",
"givenName" : "John",
"honorificSuffix" : "III",
"honorificPrefix" : "Dr."
},
"userName" : "DDOE1",
"displayName" : "John Doe",
"addresses" : [ {
"streetAddress" : "100 Universal City Plaza",
"region" : "CA",
"formatted" : "100 Universal City Plaza\nHollywood, CA 91608 USA",
"postalCode" : "91608",
"primary" : "true",
"locality" : "Hollywood",
"type" : "work",
"country" : "USA"
}, {
"streetAddress" : "222 Universal City Plaza",
"region" : "CA",
"formatted" : "222 Universal City Plaza\nHollywood, CA 91622 USA",
"postalCode" : "91622",
"primary" : "false",
"locality" : "Hollywood",
"type" : "home",
"country" : "USA"
} ],
"groups" : [ {
"value" : "usemploys",
"display" : "US Employees"
}, {
"value" : "euemploys",
"display" : "EU Employees"
} ]
}</programlisting>
<para>
The sample script file, <filename>scim.js</filename> in the
<filename>/path/to/samples/sample7/script</filename> directory,
transforms the user data from the resource into the JSON object layout
required by SCIM schema.
</para>
</section>
</section>
<section xml:id="more-sample8">
<title>Sample 8 - Logging in Scripts</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 8 - Logging in Scripts</secondary>
</indexterm>
<para>OpenIDM provides a <literal>logger</literal> object with
<literal>debug()</literal>, <literal>error()</literal>,
<literal>info()</literal>, <literal>trace()</literal>, and
<literal>warn()</literal> functions that you can use to log messages to
the OpenIDM console from your scripts.</para>
<section xml:id="install-sample8">
<title>Install the Sample</title>
<para>Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>,
then start OpenIDM with the configuration for sample 8.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample8</screen>
<para>The <filename>sync.json</filename> file in the
<filename>/path/to/samples/sample8/conf</filename> directory includes
brief examples of log messages.
</para>
</section>
<section xml:id="run-sample8">
<title>Running the Sample</title>
<para>Run reconciliation over the REST interface.</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemXmlfileAccounts_managedUser&amp;waitForCompletion=true"</userinput></screen>
<para>
The reconciliation operation returns a reconciliation run ID, and the status
of the operation.
</para>
<para>Notice the log messages displayed on the OpenIDM (Felix) console.
The following example omits timestamps and so forth to show only the message
strings.</para>
<programlisting language="none">-&gt;
...Case no Source: the source object contains: = null [5235432-...
...Case emptySource: the source object contains: = {lastname=Carter, mobile...
...Case sourceDescription: the source object contains: = Created By XML1
...Case onCreate: the source object contains: = {lastname=Carter, mobile...
...Case result: the source object contains: = {SOURCE_IGNORED={count=0, ids=[]},...</programlisting>
</section>
</section>
<section xml:id="more-sample9">
<title>Sample 9 - Asynchronous Reconciliation Using Workflows</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 9 - asynchronous reconciliation</secondary>
</indexterm>
<para>Sample 9 demonstrates asynchronous reconciliation using workflows.
Reconciliation generates an approval request for each ABSENT user. The
configuration for this action is defined in the <filename>conf/sync.json</filename>
file, which specifies that an <literal>ABSENT</literal> condition should
launch the <literal>managedUserApproval</literal> workflow:</para>
<programlisting language="javascript">...
{
"situation" : "ABSENT",
"action" : {
"workflowName" : "managedUserApproval",
"type" : "text/javascript",
"file" : "workflow/triggerWorkflowFromSync.js"
}
},
... </programlisting>
<para>When the request is approved by an administrator, the absent users are
created by an asynchronous reconciliation process.</para>
<para>Prepare a fresh installation of OpenIDM before trying this
sample.</para>
<section xml:id="install-sample9">
<title>Install the Sample</title>
<para>Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>,
then start OpenIDM with the configuration for sample 9.</para>
<screen><userinput>$ cd /path/to/openidm
$ /startup.sh -p samples/sample9</userinput></screen>
</section>
<section xml:id="run-sample9">
<title>Running the Sample</title>
<orderedlist>
<listitem>
<para>Run reconciliation over the REST interface.</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemXmlfileAccounts_managedUser&amp;waitForCompletion=true"</userinput></screen>
<para>
The reconciliation operation returns a reconciliation run ID, and the
status of the operation.
</para>
<para>
Reconciliation starts an approval workflow for each ABSENT user. These
approval workflows (named <literal>managedUserApproval</literal>)
wait for the request to be approved by an administrator.
</para>
</listitem>
<listitem>
<para>Query the invoked workflow task instances over REST.</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/workflow/taskinstance?_queryId=query-all-ids"</userinput></screen>
<para>
In this case, the request returns two workflow results, each with a
process ID (<literal>_id</literal>) as well as a process definition
ID. You will use the value of the <literal>_id</literal> shortly.
</para>
<screen>{
"result" : [ {
"tenantId" : "",
"createTime" : "2014-05-01T13:48:42.980-08:00",
"executionId" : "101",
"delegationStateString" : null,
"processVariables" : { },
"_id" : "123",
"processInstanceId" : "101",
"description" : null,
"priority" : 50,
"name" : "Evaluate request",
"dueDate" : null,
"parentTaskId" : null,
"processDefinitionId" : "managedUserApproval:1:3",
"taskLocalVariables" : { },
"suspensionState" : 1,
"assignee" : "openidm-admin",
"cachedElContext" : null,
"queryVariables" : null,
"activityInstanceVariables" : { },
"deleted" : false,
"suspended" : false,
"_rev" : 1,
"revisionNext" : 2,
"category" : null,
"taskDefinitionKey" : "evaluateRequest",
"owner" : null,
"eventName" : null,
"delegationState" : null
}, {
"tenantId" : "",
"createTime" : "2014-05-01T13:48:42.980-08:00",
"executionId" : "102",
"delegationStateString" : null,
"processVariables" : { },
"_id" : "124",
"processInstanceId" : "102",
"description" : null,
"priority" : 50,
"name" : "Evaluate request",
"dueDate" : null,
"parentTaskId" : null,
"processDefinitionId" : "managedUserApproval:1:3",
"taskLocalVariables" : { },
"suspensionState" : 1,
"assignee" : "openidm-admin",
"cachedElContext" : null,
"queryVariables" : null,
"activityInstanceVariables" : { },
"deleted" : false,
"suspended" : false,
"_rev" : 1,
"revisionNext" : 2,
"category" : null,
"taskDefinitionKey" : "evaluateRequest",
"owner" : null,
"eventName" : null,
"delegationState" : null
} ],
"resultCount" : 2,
"pagedResultsCookie" : null,
"remainingPagedResults" : -1
}</screen>
</listitem>
<listitem>
<para>
Approve the requests over REST, by setting the
<literal>"requestApproved"</literal> parameter for the specified task
instance to <literal>"true"</literal>. Note the use of one of the
values of <literal>_id</literal> in the REST call, in this case,
<literal>124</literal>.
</para>
<para>On UNIX:</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{"requestApproved": "true"}' \
"https://localhost:8443/openidm/workflow/taskinstance/124?_action=complete"</userinput></screen>
<para>On Windows:</para>
<screen><userinput>$ curl ^
--cacert self-signed.crt ^
--header "X-OpenIDM-Username: openidm-admin" ^
--header "X-OpenIDM-Password: openidm-admin" ^
--header "Content-Type: application/json" ^
--request POST ^
--data "{\"requestApproved\": \"true\"}" ^
"https://localhost:8443/openidm/workflow/taskinstance/124?_action=complete"</userinput></screen>
<para>A successful call returns the following:</para>
<screen>{"Task action performed":"complete"}</screen>
</listitem>
<listitem>
<para>
Once the request has been approved, an asynchronous reconciliation
operation runs, which creates the users whose accounts were approved in
the previous step.
</para>
<para>
List the users that were created by the asynchronous reconciliation.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"</userinput></screen>
<para>One user is returned.</para>
<screen>{
"result": [ {
"_rev": "0",
"_id": "1"
} ],
"resultCount": 1,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</screen>
</listitem>
</orderedlist>
</section>
</section>
<section xml:id="more-sample-openam">
<title>Sample - Configuring Authentication Management With OpenAM</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample OpenAM - Authentication Management for OpenIDM</secondary>
</indexterm>
<para>
This sample demonstrates how you can protect a deployment of OpenIDM with
ForgeRock's <link xlink:show="new"
xlink:href="http://docs.forgerock.org/en/index.html?product=openam">
<citetitle>OpenAM Access Management</citetitle></link> product.
</para>
<para>
In this sample, you will use OpenIDM together with OpenAM and ForgeRock's
<link xlink:href="http://docs.forgerock.org/en/index.html?product=opendj">
<citetitle>OpenDJ Directory Services</citetitle></link> product.
</para>
<para>
When configured together, you can demonstrate one use case that maximizes
Identity Relationship Management (IRM) functionality. OpenIDM can maintain
user data in OpenDJ data stores. Once protected by OpenAM, you can configure
rules for authorized access.
</para>
<para>
This sample configures OpenIDM on one system, with a URL of
<literal>openidm.example.com</literal>. It configures OpenAM and OpenDJ on
a separate second system, with a URL of <literal>openam.example.com</literal>.
</para>
<!--TODO: update if/when OpenAM 11 is updated to work with this sample,
ref AME-4386, or when OpenAM 12 is released. -->
<note>
<para>
This sample assumes that you have deployed OpenAM 12.
</para>
</note>
<section xml:id="prepare-sample-openam">
<title>Prepare the Sample</title>
<para>
Before installing OpenIDM, you need to prepare your systems. To set up this
sample, you need to take the following basic steps:
</para>
<itemizedlist>
<listitem>
<para>
Install OpenDJ, using a base DN of <literal>dc=example,dc=com</literal>.
Be sure to import the <filename>Example.ldif</filename> file available in
the <literal>samples/openam/data</literal> directory.
This example assumes that you install OpenDJ on the same system as
OpenIDM, with a FQDN of <literal>openidm.example.com</literal>.
</para>
<para>
You can import the appropriate <filename>Example.ldif</filename> file with
the following command.
</para>
<screen>$ cd /path/to/opendj/bin
$ /ldapmodify \
--bindDN "cn=Directory Manager" \
--bindPassword password \
--hostname localhost \
--port 1389 \
--filename /path/to/openidm/samples/openam/data/Example.ldif
</screen>
<para>
If you set up OpenDJ on a secure port with a self-signed certificate, you
should import that certificate into the OpenIDM datastore. For more
information, see <link xlink:show="new"
xlink:href="integrators-guide#security-management-service"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Accessing the
Security Management Service</citetitle></link> in the <citetitle>Integrator's
Guide</citetitle>.
</para>
</listitem>
<listitem>
<para>
Install an OpenAM 12 server in an environment that OpenIDM can access with
RESTful calls. For installation instructions, see the <link xlink:show="new"
xlink:href="http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/install-guide/index.html">
<citetitle>OpenAM Installation Guide</citetitle></link>. This example
assumes that you have deployed the associated <literal>.war</literal>
file in your selected Java EE container with the following name:
<filename>openam.war</filename>.
</para>
<para>
Configure the OpenAM JVM on a secure port such as 8443.
</para>
<para>
When installing OpenAM, configure it to use the OpenDJ data store
installed on <literal>openam.example.com</literal>.
</para>
<para>
As described in the following procedure, <link xlink:show="new"
xlink:href="http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/install-guide/index.html#configure-openam-custom">
<citetitle>To Custom Configure OpenAM</citetitle></link>, Make sure the
trust store used by the JVM running OpenAM has the necessary certificates
installed.
</para>
<para>
This sample assumes that you will configure OpenAM-based Single Sign-On
(SSO) as described in the OpenAM Administration Guide. For this sample,
configure <literal>.example.com</literal> as the sole cookie domain as
described in the following section of the OpenAM Reference Guide:
<link xlink:show="new"
xlink:href="http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/reference#system-platform-attrs">
<citetitle>System Configuration: Platform</citetitle></link>.
</para>
<note>
<para>
When you configure OpenAM for this sample, you do not need to install a
Java EE agent. This sample uses the <literal>OPENAM_SESSION</literal>
module to protect OpenIDM. That module is included in the
<literal>/path/to/openam/conf/authentication.json</literal> file.
</para>
</note>
</listitem>
<listitem>
<para>
Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>.
</para>
</listitem>
<listitem>
<para>
As OpenAM works with FQDNs (and not IP addresses), you will need to set up
either the noted FQDNs <literal>(openam.example.com and
openidm.example.com)</literal> in an address database such as a DNS server
or the static <filename>hosts</filename> configuration file of both
systems.
</para>
</listitem>
<listitem>
<para>
Do not start OpenIDM yet. You will need to configure it further.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="configure-openam-sample">
<title>Configuring OpenIDM for the OpenAM Sample</title>
<para>
Before starting OpenIDM, you will need to change two configuration files,
related to authentication and provisioning.
</para>
<section xml:id="configure-openam-sample-auth-json">
<title>Configuring the authentication.json File for OpenAM</title>
<para>
To configure OpenIDM authentication, open the
<filename>authentication.json</filename> file. For this sample, you can
find that file in the <literal>samples/openam/conf</literal> directory.
</para>
<para>
Under <literal>"authModules"</literal>, find the
<literal>"OPENAM_SESSION"</literal> authentication module. The default
version of the <filename>authentication.json</filename> file includes two
entries which you must fill in:
</para>
<programlisting language="javascript">"authModules" : [
{
"name" : "OPENAM_SESSION",
"properties" : {
"openamDeploymentUrl" : "",
"groupRoleMapping" : {
"openidm-admin" : [ ]
},</programlisting>
<para>
Based on a standard <literal>openidm-admin</literal> user and the
previously noted URL and <literal>.war</literal> file for your OpenAM
deployment, you would change the code snippet to:
</para>
<programlisting language="javascript">"authModules" : [
{
"name" : "OPENAM_SESSION",
"properties" : {
"openamDeploymentUrl" : "https://openam.example.com:8443/openam",
"groupRoleMapping" : {
"openidm-admin" : [
"cn=idmAdmins,ou=Groups,dc=example,dc=com"
]
},</programlisting>
<para>
Do remember to include <literal>/openam</literal> in the value of
<literal>"openamDeploymentUrl"</literal>. After the Java EE container used
for OpenAM starts, it unpacks a file such as <literal>openam.war</literal>
so that you can access it on the <literal>/openam</literal> endpoint.
</para>
<para>
With the <literal>"groupRoleMapping"</literal>, you can add the
Distinguished Names (DN) of groups to the list. This feature will then
assign the noted role, such as <literal>openidm-admin</literal> to any user
who is a member of one of these groups. For more information on how roles
are assigned, see the following section in the Integrator's Guide on
<link xlink:role="http://docbook.org/roles/xlink/role/olink"
xlink:href="integrators-guide#openidm-roles" xlink:show="new">
<citetitle>Roles and Authentication</citetitle></link>.
</para>
<!-- The README suggests this is needed only for self-signed certs. I think
this may be required for regular certs too, or does our truststore include
the "standard list"? -->
<para>
The <literal>"openamDeploymentUrl"</literal> shown above assumes that you
are using SSL. If you have a self-signed certificate, you will need to
import that into the OpenIDM truststore file. For more information, see
the following section of the Integrator's Guide: <link xlink:show="new"
xlink:role="http://docbook.org/roles/xlink/role/olink"
xlink:href="integrators-guide#security-management-service"><citetitle>
Accessing the Security Management Service</citetitle></link>.
</para>
</section>
<section xml:id="configure-openam-sample-prov">
<title>Configure Provisioning for the OpenAM Sample</title>
<para>
This section describes how you can customize the
<filename>provisioner.openicf-ldap.json</filename> file. The following
edits parallel those for <link xlink:show="new"
xlink:role="http://docbook.org/roles/xlink/role/olink"
xlink:href="install-guide#more-sample2c"><citetitle>Sample
2c - Synchronizing LDAP Group Membership</citetitle></link>.
</para>
<para>
Open the noted provisioner file, which you can find in the
<literal>samples/openam/conf</literal> directory. In the
initial version of this file, you should see something similar to the
following code:
</para>
<programlisting language="javascript">{
"name" : "ldap",
"configurationProperties" : {
"host" : "localhost",
"port" : 1389,
"ssl" : false,
"principal" : "cn=Directory Manager",
"credentials" : "password",
"baseContexts" : [
"dc=example,dc=com"
],
"baseContextsToSynchronize" : [
"dc=example,dc=com"
],</programlisting>
<para>
This snippet already matches the noted base context of
<literal>"dc=example,dc=com"</literal> with a principal of
<literal>"cn=Directory Manager"</literal>.
</para>
<para>
Make sure that the following settings are consistent with the
way you have configured OpenDJ and OpenAM.
</para>
<para>
Change the <literal>"localhost"</literal> entry to the
URL where OpenDJ is installed. In this case, that URL is
<literal>openidm.example.com</literal>. Depending on whether you want to
set up communications over a regular or secure LDAP port, you might
change the <literal>"port"</literal> number to 389 or 636. The following
excerpt illustrates the change to a secure connector configuration:
</para>
<programlisting language="javascript">{
"name" : "ldap",
"configurationProperties" : {
"host" : "openidm.example.com",
"port" : 636,
"ssl" : true,
"principal" : "cn=Directory Manager",
"credentials" : "password",
"baseContexts" : [
"dc=example,dc=com"
],
"baseContextsToSynchronize" : [
"dc=example,dc=com"
],</programlisting>
<para>
Just remember, if you want to set up secure communications with OpenDJ,
you should configure OpenDJ with that in mind. If you have a self-signed
certificate for OpenDJ, you will also want to import that into the
OpenIDM truststore, as discussed in the following section of the
Integrator's Guide: <link xlink:show="new"
xlink:role="http://docbook.org/roles/xlink/role/olink"
xlink:href="integrators-guide#security-management-service"><citetitle>
Accessing the Security Management Service</citetitle></link>.
</para>
</section>
</section>
<section xml:id="install-sample-openam">
<title>Install the Sample</title>
<para>
If you have not already done so, make sure OpenDJ is running on its
assigned system, in this case, <literal>openidm.example.com</literal>.
</para>
<para>
Finish preparing OpenIDM, with the procedure described in
<xref linkend="preparing-openidm"/>, then start OpenIDM with the
configuration for sample openam.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/openam</screen>
<para>
If you have not already done so, make sure OpenAM is also running on its
assigned system, in this case, <literal>openam.example.com</literal>.
</para>
</section>
<section xml:id="run-sample-openam">
<title>Running the Sample</title>
<para>
With everything configured, now run reconciliation on the
<literal>openidm.example.com</literal> system. The following command
imports users from the OpenDJ database into the OpenIDM managed/user
database:
</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemLdapAccounts_managedUser&amp;waitForCompletion=true"</screen>
<para>
The reconciliation operation returns a reconciliation run ID, and the status
of the operation.
</para>
</section>
<section xml:id="review-sample-openam">
<title>Make Sure the Sample Works as Intended</title>
<para>
You should still be able to access the OpenIDM UI
in the "normal" way. In this case, you would point a browser to
<literal>https://openidm.example.com:8443/openidmui</literal>.
</para>
<para>
The difference is how OpenIDM uses a proxy service to authenticate with
OpenAM's REST-based authentication service. This sample implements the
proxy service as a custom endpoint in the <filename>openamProxy.js</filename>
file, in the <literal>samples/openam/script</literal>
directory.
</para>
<para>
The first time you log into the OpenIDM UI, you should see a login prompt
configured with OpenAM. The default version of this prompt appears quite
similar to the OpenIDM prompt. When you login through OpenAM, successfully,
you will receive a valid SSO token.
</para>
<para>
The next time you access OpenIDM, with the SSO token stored in your browser,
you will be logged in automatically, with all the roles configured for your
user account. This works only if your session is still active in OpenAM.
For more information, see the OpenAM Administration Guide section on
<link xlink:show="new"
xlink:href="http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/admin-guide/#session-mgmt">
<citetitle>Session Management</citetitle></link>.
</para>
<note>
<para>You can review the contents of the SSO token by pointing a browser to
the FQDN of the OpenIDM instance, with the <literal>info/login</literal>
endpoint. In this case, that URL would be
<literal>https://openidm.example.com/openidm/info/login</literal>.</para>
</note>
</section>
<section xml:id="sample-openam-going-further">
<title>Additional Options for the OpenAM Sample</title>
<para>
You can extend the OpenAM sample in one of three ways:
</para>
<itemizedlist>
<listitem>
<para><literal>Disable INTERNAL_USER when enabling LiveSync</literal></para>
<para>
To keep OpenDJ in sync with the OpenIDM managed/user database, you can
enable scheduled reconciliation and LiveSync. To do so, edit the following
files: <filename>schedule-recon.json</filename> and
<filename>schedule-livesync.json</filename>, in the
<literal>samples/openam/conf</literal> directory. Set
<literal>"enabled" : true</literal> in each of these files.
</para>
<para>
This ensures that any change to the OpenDJ user store is automatically
reflected in the OpenIDM managed/user data store. For a similar example,
see <xref linkend="more-sample6"/>.
</para>
<para>
You can take these steps while OpenIDM is running.
</para>
</listitem>
<listitem>
<para>
Execute the reconciliation command described earlier in the description
for this sample.
</para>
</listitem>
<listitem>
<para>
Once scheduled reconciliation and LiveSync are configured, you can then
disable the <literal>INTERNAL_USER</literal> under
<literal>authModules</literal> in the relevant
<filename>authentication.json</filename> file. Just navigate to that
section of the file and set <literal>"enabled" : false</literal>. Once
complete, the only users with access to OpenIDM are users with a valid
SSO authentication token in their browsers.
</para>
</listitem>
<!-- possible additional options; can incorporate with more detail
<listitem>
<para>
<literal>Set up workflows to request access to applications</literal>
</para>
<para>
You can configure a workflow to allow users to request access to
additional applications. Upon approval, OpenIDM can update user accounts
with the permissions used by OpenAM to control access to that application.
For more information on workflows, see the Integrator's Guide section to
<link xlink:role="http://docbook.org/roles/xlink/role/olink"
xlink:href="integrators-guide#chap-workflow" xlink:show="new">
<citetitle>Integrating Business Processes and Workflows</citetitle>.
</link>
</para>
</listitem>
<listitem>
<para><literal>Single Sign-On Dashboard</literal></para>
<para>
You could extend workflow concepts to give users a choice of
applications, based on their SSO token. Once configured, you could set up
a "Single Sign-On" application dashboard for your users.
</para>
</listitem> also:
Integrated Services For Custom Applications
Based on the presence of a shared SSO token, any application secured by OpenAM
could reliably invoke OpenIDM REST services in an integrated and transparent
way. For example, an intranet portal could make AJAX requests to OpenIDM to
start a workflow process or execute a query. The fact that the request was
being made to OpenIDM would be hidden from the user - the SSO token that they
used to access the intranet would also be used by OpenIDM, and everything
would just work seamlessly.
-->
</itemizedlist>
</section>
</section>
<section xml:id='more-sample-audit'>
<title>Sample - Demonstrate Extended Audit Capabilities</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample Audit - Extended Audit Capabilities</secondary>
</indexterm>
<para>
This sample demonstrates how you can use the audit features associated with
OpenIDM, for access, activity, reconciliation, and synchronization.
</para>
<para>
This sample uses a MySQL database as a target repository for audit
information. This audit sample is closely related to the
<link xlink:show="new" xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="install-guide#chap-sample"><citetitle>First OpenIDM
Sample</citetitle></link>.
</para>
<para>
One difference: the audit sample includes a ScriptedSQL implementation of
the Groovy Connector Toolkit.
</para>
<para>
If you want to use any of the audit features demonstrated in this sample,
copy information from files in the /path/to/samples/audit-sample directory.
This can help you set up auditing for any other sample.
</para>
<section xml:id="audit-config-files">
<title>Audit Sample Configuration Files</title>
<para>
Review the configuration files used in this sample. They can help you
understand the functionality of the data sets being audited.
</para>
<itemizedlist>
<para>
The key configuration files, in the
<literal>/path/to/samples/audit-sample</literal> directory, are as follows:
</para>
<listitem>
<para>
<filename>conf/provisioner.openicf-scriptedsql.json</filename> shows the
configuration of the <link xlink:role="http://docbook.org/xlink/role/olink"
xlink:show="new" xlink:href="integrators-guide#scripted-sql-connector">
<citetitle>Scripted SQL Connector</citetitle></link>.
</para>
</listitem>
<listitem>
<para>
<filename>conf/provisioner.openicf-xml.json</filename> shows the
configuration of the <link xlink:role="http://docbook.org/xlink/role/olink"
xlink:show="new" xlink:href="integrators-guide#xml-file-connector">
<citetitle>XML File Connector</citetitle></link>.
</para>
</listitem>
<listitem>
<para>
<filename>conf/audit.json</filename> configures audit logging on the
router, to a remote system, as discussed in <link xlink:show="new"
xlink:href="integrators-guide#audit-configuration"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Audit
Configuration</citetitle></link>.
</para>
</listitem>
<listitem>
<para>
<filename>conf/sync.json</filename> shows mappings between managed users
and the data set attached through the XML File Connector.
</para>
</listitem>
<listitem>
<para>
<filename>data/sample_audit_db.mysql</filename> includes a schema that
supports tables in the external MySQL database.
</para>
</listitem>
<listitem>
<para>
Groovy scripts in the <filename>tools/</filename> subdirectory supports
communications between the Scripted SQL connector and the MySQL
database.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="external-audit-mysql-sample">
<title>Configuration with MySQL</title>
<para>
You need to set up communications between OpenIDM and an external MySQL
database server.
</para>
<para>
Make sure MySQL is running.
</para>
<itemizedlist>
<para>
The sample expects the following configuration for MySQL:
</para>
<listitem>
<para>
The database is available on the local system.
</para>
</listitem>
<listitem>
<para>
The database listens on the standard MySQL port, 3306.
</para>
</listitem>
<listitem>
<para>
You can connect to the MySQL database over the network.
</para>
</listitem>
<listitem>
<para>
MySQL is configured with an administrative account with user
<literal>root</literal> and password <literal>password</literal>.
</para>
</listitem>
<listitem>
<para>
MySQL serves a database called <literal>audit</literal> with three tables:
<literal>access</literal>, <literal>activity</literal>, and
<literal>recon</literal>.
</para>
</listitem>
<listitem>
<para>
For more information on the database schema, examine the following data
definition language file:
<literal>openidm/samples/audit-sample/data/sample_audit_db.mysql</literal>.
Import the file into MySQL before running the sample.
</para>
<screen>$ <userinput>mysql -u root -p &lt; /path/to/openidm/samples/audit-sample/data/sample_audit_db.mysql
</userinput>
Enter password:
$ </screen>
</listitem>
</itemizedlist>
<para>
Now you can review the format of the audit database sample, created from
the <filename>sample_audit_db.mysql</filename> file, at the MySQL prompt."
access that prompt, run the following command:
</para>
<screen>$ <userinput>mysql -u root -p</userinput>
mysql &gt; <userinput>use audit;</userinput>
<computeroutput>Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed</computeroutput></screen>
<para>
You can now review the current state of the access, activity, and
reconciliation logs with the following commands:
</para>
<screen>select * from auditaccess;
select * from auditactivity;
select * from auditrecon;
select * from auditsync;</screen>
<para>
Unless you enable scheduled reconciliation, you will not see audit
data until you run reconciliation manually.
</para>
<para>
Download the <link xlink:show="new"
xlink:href="http://dev.mysql.com/downloads/connector/j/5.1.html">MySQL
Driver</link>, (MySQL Connector/J, version 5.1 or later) from the MySQL
website. Unpack the download and copy the .jar into the
<filename>openidm/bundle</filename> directory.
</para>
<screen>$ cp mysql-connector-java-<replaceable>version</replaceable>-bin.jar \
/path/to/openidm/bundle/</screen>
</section>
<section xml:id="install-sample-audit">
<title>Install the Sample</title>
<para>Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>,
then start OpenIDM with the configuration for the audit sample.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/audit-sample</screen>
</section>
<section xml:id="run-sample-audit">
<title>Running the Sample</title>
<para>Run reconciliation over the REST interface.</para>
<screen>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=systemXmlfileAccounts_managedUser&amp;waitForCompletion=true"
</screen>
<para>
Alternatively, open the
<filename>schedule-reconcile_systemXmlAccounts_managedUser.json</filename>
file in the <literal>/path/to/openidm/samples/audit-sample/conf</literal>
directory and change the value of the <literal>"enabled"</literal> directive
to turn on scheduled reconciliation:
</para>
<screen>"enabled" : true,</screen>
<para>
You can now review the results in the MySQL database, from the MySQL prompt,
using the commands described earlier.
</para>
<para>
If you have retained the default <literal>"useForQueries" : true</literal>
option in the <filename>conf/audit.json</filename> file, you can also
<literal>GET</literal> the same data with a REST call. For examples on how
you can query reconciliation, activity, and synchronization audit logs,
see relevant sections of the
<link xlink:show="new" xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#chap-synchronization">
<citetitle>Configuring Synchronization</citetitle></link>. For more
information on <literal>"useForQueries"</literal>, see
<link xlink:show="new" xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="integrators-guide#audit-remote-logging"><citetitle>Logging
to a Remote System</citetitle></link>.
</para>
</section>
</section>
<section xml:id="sample-google-connector">
<title>Sample - Connecting to Google With the Google Apps Connector</title>
<indexterm>
<primary>Samples</primary>
<secondary>Google Apps Connector</secondary>
</indexterm>
<para>
OpenICF ${openicfBundleVersion} provides a new Google Apps Connector that
enables you to interact with Google's web applications.
</para>
<note>
<para>
The Google Apps Connector, and this corresponding sample, are provided only
with <link xlink:show="new"
xlink:href="http://www.forgerock.com/download-stack/">OpenIDM
Enterprise</link>.
</para>
</note>
<para>
This sample demonstrates the creation of users and groups on an external
Google system, using OpenIDM's REST interface.
</para>
<section xml:id="google-apps-">
<title>Before You Start</title>
<para>
This sample requires that you have a Google Apps account. Obtaining a Google
Apps account is described in the <link xlink:show="new"
xlink:href="https://support.google.com/a/answer/53926?hl=en">Google
documentation</link>. The sample uses OAuth2 to authorize the connection to
the Google service. This authorization mechanism requires that you obtain a
refresh token from Google. The following steps indicate how to obtain the
refresh token. <!--TODO these steps will be replaced by a process in our
Admin UI - update this procedure at that point-->
</para>
<procedure>
<step>
<para>
Log in to your Google Apps Admin Console (at
<literal>https://www.google.com/a/<replaceable>domain-name</replaceable></literal>)
and verify that the following APIs are enabled:
</para>
<itemizedlist>
<listitem>
<para>
Admin SDK API
</para>
</listitem>
<listitem>
<para>
Enterprise License Manager API
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Use the OAuth 2.0 Installed Application flow to obtain a
<filename>client_secrets.json</filename> file. For more information
about the OAuth 2.0 Installed Application flow, see the corresponding
<link xlink:show="new"
xlink:href="https://developers.google.com/maps-engine/documentation/oauth/installedapplication">Google
documentation</link>.
</para>
</step>
<step>
<para>
Download the Google Admin Directory API client library
(<link xlink:show="new"
xlink:href="https://developers.google.com/resources/api-libraries/download/admin/directory_v1/java">google-api-services-admin-directory_v1-rev42-java-1.19.0.zip</link>)
and extract it to a writeable directory.
</para>
<screen><userinput>$ unzip ~/Downloads/google-api-services-admin-directory_v1-rev42-java-1.19.0.zip -d /path/to</userinput>
<computeroutput>Archive: ~/Downloads/google-api-services-admin-directory_v1-rev42-java-1.19.0.zip
creating: /path/to/admin/
extracting: /path/to/admin/google-api-services-admin-directory_v1-rev42-1.19.0.jar
extracting: /path/to/admin/google-api-services-admin-directory_v1-rev42-1.19.0-javadoc.jar
...</computeroutput></screen>
</step>
<step>
<para>
Download the Google Apps connector jar and copy it to the
<literal>admin</literal> directory, created in the previous step.
</para>
<screen><userinput>$ cp ~/Downloads/googleapps-connector-${openicfBundleVersion}.jar /path/to/admin</userinput></screen>
</step>
<step>
<para>
Change to the <literal>admin</literal> directory and run the following
command on the <filename>client_secrets.json</filename> file that you
obtained earlier in this procedure.
</para>
<screen><userinput>$ cd admin/
$ java -jar googleapps-connector-1.4.0.0.jar /path/to/client_secrets.json</userinput>
<computeroutput>Please open the following address in your browser:
https://accounts.google.com/o/oauth2/auth?
access_type=offline
...</computeroutput></screen>
<para>
This command opens the default browser, and loads a screen on which you
authorize consent to access the Google Apps account.
</para>
<tip>
<para>
If you have recently created your Google Apps account, it might take some
time (often two hours or more) to synchronize the data required for this
access request to work.
</para>
</tip>
</step>
<step>
<para>
When you have authorized consent, the browser returns a code. Copy and
paste the code into the terminal from which you ran the original command.
</para>
<screen><computeroutput>Attempting to open that address in the default browser now...
Please enter code:</computeroutput><userinput>XXXXXXXX</userinput></screen>
<para>
A response similar to the following is returned:
</para>
<screen><computeroutput>{
"clientId" : "5x4x3x4x0x8x-cxlx3xsxcx8xixlxmx3x0xrxgx7x6x3x.apps.googleusercontent.com",
"clientSecret" : "0xhx9xrx8xdxqx9xDxjxUx3x",
"refreshToken" : "1x7xmxfx_yxuxNxUxFxjxVxVxkxXx3XxHxMxYxzx5xcxI"
}</computeroutput></screen>
</step>
<step>
<para>
Use the information in this response to edit the
<literal>"configurationProperties"</literal> in the Google Apps
provisioner configuration file
(<filename>samples/google-connector/conf/provisioner.openicf-google.json</filename>).
For example:
</para>
<programlisting language="javascript">"configurationProperties": {
"domain": "example.com",
"clientId": "5x4x3x4x0x8x-cxlx3xsxcx8xixlxmx3x0xrxgx7x6x3x.apps.googleusercontent.com",
"clientSecret": "0xhx9xrx8xdxqx9xDxjxUx3x",
"refreshToken": "1x7xmxfx_yxuxNxUxFxjxVxVxkxXx3XxHxMxYxzx5xcxI"
}, </programlisting>
</step>
</procedure>
</section>
<section xml:id="running-the-google-apps-sample">
<title>Running the Google Apps Sample</title>
<procedure>
<step>
<para>
To run the sample, start OpenIDM with the configuration for the Google
Apps Connector.
</para>
<screen><userinput>$ cd /path/to/openidm
$ /startup.sh -p samples/google-connector</userinput>
<computeroutput>Executing /startup.sh...
Using OPENIDM_HOME: /path/to/openidm
Using OPENIDM_OPTS: -Xmx1024m -Xms1024m
Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties
Using boot properties at /path/to/openidm/samples/google-connector/conf/boot/boot.properties
OpenIDM version "3.1.0" (revision: 0) null null
-> OpenIDM ready</computeroutput>
</screen>
</step>
<step>
<para>
To verify the connector configuration, check the status of the Google Apps
connector as follows:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system/google?_action=test"</userinput>
<computeroutput>{
"name":"google",
"enabled":true,
"config":"config/provisioner.openicf/google",
"connectorRef": {
"connectorName":"org.forgerock.openicf.connectors.googleapps.GoogleAppsConnector",
"bundleName":"org.forgerock.openicf.connectors.googleapps-connector",
"bundleVersion":"[1.4.0.0,2.0.0.0)"
},
"ok":true
}</computeroutput></screen>
</step>
<step>
<para>
To test that the connector is working, create, update, and read a user and
group entry on the Google resource, over REST.
</para>
<note>
<para>
When creating resources for Google, note that the equals
(<literal>=</literal>) character cannot be used in any attribute value.
</para>
</note>
<para>
The following command creates an entry for user <literal>Sam Carter</literal>:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"__NAME__": "samcarter@example.com",
"__PASSWORD__" : "password",
"givenName" : "Sam",
"familyName": "Carter",
"agreedToTerms": true,
"changePasswordAtNextLogin" : false
}' \
"https://localhost:8443/openidm/system/google/__ACCOUNT__?_action=create" \
</userinput>
<computeroutput>{
"phones":null,
"givenName":"Sam",
"familyName":"Carter",
"thumbnailPhotoUrl":null,
"addresses":null,
"__NAME__":"samcarter@example.com",
"changePasswordAtNextLogin":false,
"suspensionReason":null,
"aliases":null,
"customerId":"Cwev45or",
"isDelegatedAdmin":false,
"lastLoginTime":["1970-01-01T00:00:00.000Z"],
"agreedToTerms":true,
"deletionTime":null,
"isAdmin":false,
"creationTime":["2014-09-23T18:54:19.000Z"],
"ims":null,
"organizations":null,
"ipWhitelisted":false,
"fullName":"Sam Carter",
"includeInGlobalAddressList":true,
"isMailboxSetup":false,
"externalIds":null,
"suspended":false,
"nonEditableAliases":null,
"orgUnitPath":"/",
"relations":null,
"emails": [
{
"address":"samcarter@example.com",
"primary":true
}
],
"_id":"101643010677712061243",
"_rev":"\"SHoRIPAZisvsSqsLJr3m3XS9Uw4/RfZOwrjqrpRsO6PCGNfEp2H00Ds\""
}</computeroutput></screen>
</step>
<step>
<para>
Update the user's phone number by sending a PUT request with the updated
data, and specifying the user ID in the request, for example:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"__NAME__": "samcarter@example.com",
"__PASSWORD__" : "password",
"givenName" : "Sam",
"familyName": "Carter",
"agreedToTerms": true,
"changePasswordAtNextLogin" : false
"phones" :
[
{
"value": "1234567890",
"type": "home"
},
{
"value": "0987654321",
"type":"work"
}
]
}' \
"https://localhost:8443/openidm/system/google/__ACCOUNT__/101643010677712061243"</userinput></screen>
<para>
The response returns the complete user object, with the updated phone
numbers.
</para>
</step>
<step>
<para>
Read the user entry from the Google resource:
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "If-None-Match" : "*" \
--request GET \
"https://localhost:8443/openidm/system/google/__ACCOUNT__/101643010677712061243"</userinput>
<computeroutput>{
"phones":
[
{"value":"1234567890","type":"home"},
{"value":"0987654321","type":"work"}
],
"givenName":"Sam",
"familyName":"Carter",
...
"_id":"101643010677712061243",
"_rev":"\"SHoRIPAZisvsSqsLJr3m3XS9Uw4/RfZOwrjqrpRsO6PCGNfEp2H00Ds\""
}</computeroutput> </screen>
</step>
<step>
<para>
Create a group entry on the Google resource.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
--data '{
"__NAME__": "testGroup@example.com",
"__DESCRIPTION__": "Group used for google-connector sample.",
"name": "TestGroup"
}' \
"https://localhost:8443/openidm/system/google/__GROUP__?_action=create"
</userinput>
<computeroutput>{
"__DESCRIPTION__": "Group used for google-connector sample.",
"directMembersCount": 0,
"nonEditableAliases": null,
"aliases": null,
"adminCreated": true,
"name": "TestGroup",
"__NAME__": "testGroup@example.com",
"_id": "03oy7u291s3qw1i",
"_rev": "\"SHoRIPAZisvsSqsLJr3m3XS9Uw4/8JZsrHGxtdaH4sF8vgzLiPmPqFs\""
}</computeroutput></screen>
</step>
<step>
<para>
Add the user Sam Carter, that you created previously, to the test group.
Include the <literal>_id</literal> that was generated in the user creation
step in the request URL.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
"groupKey" : "03oy7u291s3qw1i",
"role": "MEMBER",
"__NAME__": "samcarter@example.com",
"email": "samcarter@example.com",
"type": "MEMBER"
}' \
"https://localhost:8443/openidm/system/google/Member/101643010677712061243"
</userinput>
<computeroutput>{
"role": "MEMBER",
"type": "USER",
"email": "samcarter@example.com",
"__NAME__": "03oy7u291s3qw1i/samcarter@example.com",
"groupKey": "109384837631312225274",
"_id": "014ykbeg40r6x06/samcarter@example.com",
"_rev": "\"SHoRIPAZisvsSqsLJr3m3XS9Uw4/GiZLiMKb3U62M58IabSzPKAVIOE\""
}</computeroutput></screen>
</step>
<step>
<para>
Read the group entry by specifying the group <literal>_id</literal> in the
request URL. Notice that the group has one member
(<literal>"directMembersCount": 1</literal>).
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/system/google/__GROUP__/03oy7u291s3qw1i"
</userinput>
<computeroutput>{
"__DESCRIPTION__": "Group used for google-connector sample.",
"directMembersCount": 1,
"nonEditableAliases": [
"googleAppsTestGroup@example.com.test-google-a.com"
],
"aliases": null,
"adminCreated": true,
"name": "TestGroup",
"__NAME__": "testGroup@example.com",
"_id": "03oy7u291s3qw1i",
"_rev": "\"SHoRIPAZisvsSqsLJr3m3XS9Uw4/WcZC54etzuPtruE7uByf9NQ82Ow\""
}</computeroutput></screen>
</step>
<!--TODO Can we add a step here to check the user's group membership? -->
<step>
<para>
Delete the group entry.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json"
--request DELETE \
"https://localhost:8443/openidm/system/google/__GROUP__/03oy7u291s3qw1i"
</userinput>
<computeroutput>{
"_id": "03oy7u291s3qw1i"
}</computeroutput></screen>
<para>
The delete request returns the group ID.
</para>
</step>
<!--TODO Can we add a step here to check the user's group membership again
now that the group has been deleted? -->
<step>
<para>
Delete the user Sam Carter, to return the Google system to its original
state.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request DELETE \
"https://localhost:8443/openidm/system/google/__ACCOUNT__/101643010677712061243" </userinput>
<computeroutput>{
"_id":"101643010677712061243"
}</computeroutput></screen>
</step>
<!--TODO Do we not want to show some kind of recon process here? -->
</procedure>
</section>
</section>
<section xml:id="sample-salesforce-connector">
<title>Sample - Connecting to Salesforce With the Salesforce Connector</title>
<indexterm>
<primary>Samples</primary>
<secondary>Salesforce Connector</secondary>
</indexterm>
<para>
OpenIDM ${docTargetVersion} provides a Salesforce Connector that enables
provisioning, reconciliation, and synchronization with a Salesforce
organization. The Salesforce Connector is not an OpenICF connector, but a
separate OpenIDM module, based on the ForgeRock Common Resource API.
</para>
<note>
<para>
The Salesforce Connector, and this corresponding sample, are provided only
with <link xlink:show="new"
xlink:href="http://www.forgerock.com/download-stack/">OpenIDM
Enterprise</link>.
</para>
</note>
<para>
This sample demonstrates the creation and update of users from OpenIDM to
Salesforce, and from Salesforce to OpenIDM. You can use either the Admin UI,
or the command line to run this sample. Both methods are outlined in the
sections that follow.
</para>
<section xml:id="salesforce-setup">
<title>Before you Start</title>
<para>
This sample requires that you have a Salesforce account, and a Connected
App with OAuth enabled. For more information about Connected Apps, see the
<link
xlink:href="http://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_overview.htm">Connected
Apps Overview</link> in the Salesforce documentation.
</para>
<procedure>
<para>
To set up a Connected App for OpenIDM, follow these steps:
</para>
<step>
<para>
Log in to <link xlink:href="http://salesforce.com">salesforce.com</link>
with your Salesforce credentials.
</para>
</step>
<step>
<para>
Click <emphasis>Setup</emphasis> in the top right corner.
</para>
</step>
<step>
<para>
In the left hand menu, under <emphasis>Build</emphasis>, expand the
<emphasis>Create</emphasis> item and click <emphasis>Apps</emphasis>.
</para>
</step>
<step>
<para>
On the right hand panel, scroll down to <emphasis>Connected Apps</emphasis>
and click <emphasis>New</emphasis>.
</para>
</step>
<step>
<para>
In the <emphasis>New Connected App</emphasis> panel, enter the following
Basic Information:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Connected App Name</emphasis>. Enter a name that you will
recognize as the OpenIDM App, for example, <literal>OpenIDM</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>API Name</emphasis>. This field defaults to the Connected
App Name, but you can change it. Note that the Application API Name can
only contain underscores and alphanumeric characters. The name must be
unique, begin with a letter, not include spaces, not end with an
underscore, and not contain two consecutive underscores.
</para>
</listitem>
<listitem>
<para>
<emphasis>Contact Email</emphasis>. Enter the email address of the
person responsible for this Connected App within your organization.
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Select <emphasis>Enable OAuth Settings</emphasis> and enter the following
information:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Callback URL</emphasis>. Enter the OpenIDM URL, to which the
requested OAuth token will be sent, for example
<literal>https://localhost:8443/admin/oauth.html</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Selected OAuth Scopes</emphasis>. Click the
<emphasis>Add</emphasis> button to add the following
<emphasis>Available Auth Scopes</emphasis> to the
<emphasis>Selected OAuth Scopes</emphasis> column:
</para>
<itemizedlist>
<listitem>
<para>
Access and manage your data
</para>
</listitem>
<listitem>
<para>
Access your basic information
</para>
</listitem>
<listitem>
<para>
Perform requests on your behalf at any time
</para>
</listitem>
</itemizedlist>
<mediaobject xml:id="new-app-data">
<alt>New connected app data</alt>
<imageobject>
<imagedata fileref="images/new-app-data.png" format="PNG" />
</imageobject>
</mediaobject>
<para>
You can leave the remaining fields blank.
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Click <emphasis>Save</emphasis> to create the new Connected App.
</para>
</step>
<step>
<para>
The next window displays your new Connected App.
</para>
<para>
Under the <emphasis>API (Enable OAuth Settings)</emphasis> item, the
Consumer Key and a link to the Consumer Secret are displayed.
</para>
<para>
Click the link to reveal the Consumer Secret.
</para>
<mediaobject xml:id="consumer-key">
<alt>Connected app consumer key and secret</alt>
<imageobject>
<imagedata fileref="images/consumer-key.png" format="PNG" />
</imageobject>
</mediaobject>
<para>
OpenIDM requires the Consumer Key and Secret to obtain an access token
and a refresh token for access to salesforce.com.
</para>
<para>
Copy and paste both the key and the secret into a file for use when you
set up the sample.
</para>
</step>
<step>
<para>
To demonstrate the reconciliation of users from Salesforce to OpenIDM,
your Salesforce organization should contain at least a few users. Add
these users now if your Salesforce organization is empty.
</para>
</step>
</procedure>
</section>
<section xml:id="install-sample-salesforce">
<title>Install the Sample</title>
<para>
Prepare OpenIDM as described in <xref linkend="preparing-openidm"/>, then
start OpenIDM with the configuration for the Salesforce sample.
</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/salesforce-connector</screen>
</section>
<section xml:id="salesforce-sample-ui">
<title>Running the Sample by Using the Admin UI</title>
<para>
The Admin UI is the recommended way to test this sample.
</para>
<procedure>
<step>
<para>
Log in to the Admin UI at the URL
<literal>https://localhost:8443/admin</literal> as the default
administrative user (<literal>openidm-admin</literal>) with password
<literal>openidm-admin</literal>.
</para>
<para>
The first time you log into the Admin UI, you are prompted to change your
password. If you do not want to change your password at this time, simply
click X to close this window, and continue with the sample.
</para>
<para>
The Resources tab shows the Salesforce connector, which is currently
disabled.
</para>
<mediaobject>
<alt>Resources tab showing disabled Salesforce connector</alt>
<imageobject>
<imagedata fileref="images/salesforce-connector.png" format="PNG"/>
</imageobject>
</mediaobject>
</step>
<step>
<para>
Enable the Salesforce connector by completing the authentication details
as follows. You will need the Consumer Key and Consumer Secret that you
obtained in the previous section.
</para>
<itemizedlist>
<listitem>
<para>
Click on the Salesforce connector to open the Edit Connector dialog.
</para>
</listitem>
<listitem>
<para>
Select True from the Enabled list.
</para>
</listitem>
<listitem>
<para>
Select Salesforce Connector from the Connector Type list.
</para>
</listitem>
<listitem>
<para>
Under Basic Connector Details select the Sandbox URL (for the purposes
of testing the sample) and enter the Connector Key and Secret that you
obtained in the previous section.
</para>
</listitem>
<listitem>
<para>
You can leave the default LiveSync details for now. Click Update to
update the connector configuration.
</para>
<mediaobject>
<alt>Editing the Salesforce connector</alt>
<imageobject>
<imagedata fileref="images/edit-sf-connector.png" format="PNG"/>
</imageobject>
</mediaobject>
</listitem>
<listitem>
<para>
On the permission request screen click Allow, to enable OpenIDM to
access your Salesforce Connected App.
</para>
<note>
<para>
In the current OpenIDM release, an issue is occasionally seen where the
system appears to time out while retrieving the refresh token from
Salesforce, at this stage. If this happens, refresh your browser and
attempt the connector setup again.
</para>
</note>
<para>
On the Resources tab, your Salesforce Connector should now be Active.
</para>
<mediaobject>
<alt>Salesforce connector now active</alt>
<imageobject>
<imagedata fileref="images/active-sf-connector.png" format="PNG"/>
</imageobject>
</mediaobject>
</listitem>
</itemizedlist>
</step>
<step>
<para>
To test the reconciliation process, select the Mappings tab.
</para>
<para>
This tab shows two configured mappings, one from Salesforce to the OpenIDM
repository (<literal>managed/user</literal>) and one from the OpenIDM
repository to Salesforce.
</para>
<mediaobject>
<alt>Mappings tab showing mappings for salesforce connector sample</alt>
<imageobject>
<imagedata fileref="images/salesforce-mappings.png" format="PNG"/>
</imageobject>
</mediaobject>
</step>
<step>
<para>
Click anywhere on the first mapping and click Reconcile Now.
</para>
<mediaobject>
<alt>Mappings tab showing reconciliation for salesforce sample</alt>
<imageobject>
<imagedata fileref="images/salesforce-reconcile.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The reconciliation operation creates the users that were present in your
Salesforce organization in the OpenIDM repository.
</para>
</step>
<step>
<para>
Retrieve the users in the repository by clicking the Data Management View
link at the top right of the Admin UI.
</para>
<para>
This link opens the Data Management UI. If you did not change your
password in the first step, you are prompted to change your password
again. You can bypass this by clicking X to close the password prompt
window.
</para>
</step>
<step>
<para>
Select the Users tab.
</para>
<mediaobject>
<alt>Users tab showing the users in the repo for salesforce sample</alt>
<imageobject>
<imagedata fileref="images/salesforce-users.png" format="PNG"/>
</imageobject>
</mediaobject>
<para>
The users from the Salesforce organization have been reconciled to the
OpenIDM repository. If the reconciliation was successful, the list of
users displayed here should reflect what was in your Salesforce
organization.
</para>
</step>
<step>
<para>
To retrieve the details of a specific user, click that username on the
Users tab.
</para>
<para>
The following image shows the details of user <literal>bjensen</literal>.
Note the Linked Systems panel at the bottom of the image. This panel shows
the corresponding user record in Salesforce.
</para>
<mediaobject>
<alt>User details for a specific user</alt>
<imageobject>
<imagedata fileref="images/salesforce-bjensen.png" format="PNG" />
</imageobject>
</mediaobject>
</step>
<step>
<para>
To test the second mapping (from OpenIDM to Salesforce), update any user
in the OpenIDM repository. For example, update Babs Jensen's username.
</para>
</step>
<step>
<para>
By default, <emphasis>implicit synchronization</emphasis> is enabled for
mappings <emphasis>from</emphasis> the <literal>managed/user</literal>
repository <emphasis>to</emphasis> any external resource. This means that
when you update a managed object, any mappings defined in the
<filename>sync.json</filename> file that have the managed object as the
source are automatically executed to update the target system. For more
information, see <link xlink:href="integrators-guide#synchronization-mappings-file"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Synchronization
Mappings File</citetitle></link> in the <emphasis>Integrator's
Guide</emphasis>.
</para>
<para>
To test that the implicit synchronization has been successful, look at
Babs Jensen's record in the Data Management UI. At the bottom of the
user profile, the Linked Systems panel indicates Babs Jensen's record in
the Salesforce data store. Note the changed Username.
</para>
<para>
Alternatively, check the updated user record in Salesforce.
</para>
</step>
</procedure>
</section>
<section xml:id="salesforce-sample-cli">
<title>Running the Sample by Using the Command Line</title>
<para>
Running the sample by using the command line is a little more complex. This
section breaks the sample into two tasks - configuring the connector, and
then testing the configuration by running reconciliation operations between
the two systems.
</para>
<procedure>
<title>To Set Up the Salesforce Connector</title>
<para>
Before you start, you will need the Consumer Key and Consumer Secret that
you obtained in the previous section.
</para>
<step>
<para>
Obtain the refresh token from salesforce.com by pointing your browser to
the following URL. Substitute your Consumer Key for
<literal>CLIENT_ID</literal>. If OpenIDM is not running on the localhost,
substitute the appropriate hostname and port number in the value of the
<literal>redirect_uri</literal> parameter.
</para>
<para>
<link xlink:show="new"
xlink:href="https://login.salesforce.com/services/oauth2/authorize?response_type=code&amp;client_id=CLIENT_ID&amp;redirect_uri=https://localhost:8443/admin/oauth.html&amp;scope=id%20api%20refresh_token" />
</para>
</step>
<step>
<para>
You are redirected to Salesforce, and prompted to give this application
access to your Salesforce account. When you have given consent, you should
receive a response URL that looks similar to the following:
</para>
<screen width="pagewide">https://localhost:8443/admin/index.html#connectors/edit//&amp;code=aPrxJZTK7Rs03PU634VK8Jn9o_U3ZY1ERxM7IiklFzbmVpZ1TTJlU1TiETXKhNHqpr4SlyCkpg%3D%3D</screen>
<para>
The <literal>&amp;code</literal> part of this URL is an authorization
code, that you need for the following step.
</para>
<caution>
<para>
Note that this authorization code expires after 10 minutes. If you do not
complete the OAuth flow within that time, you will need to start this
process again.
</para>
</caution>
</step>
<step>
<para>
Copy the authorization code from the response URL and use it as the value
of the <literal>"code"</literal> parameter in the following REST call. You
will also need to supply your Consumer Key and Consumer Secret in this
call.
</para>
<screen width="pagewide">$ <userinput> curl \
--verbose \
--data "grant_type=authorization_code" \
--data "client_id=<replaceable>consumer-key</replaceable>" \
--data "client_secret=<replaceable>consumer-secret</replaceable>" \
--data "redirect_uri=https://localhost:8443/admin/oauth.html" \
--data "code=<replaceable>access-token-code</replaceable>" \
"https://login.salesforce.com/services/oauth2/token"</userinput>
<computeroutput>{
"access_token": "00DS0000003K4fU!AQMAQOzEU.8tCjg8Wk79yKPKCtrtaszX5jrHtoT4NBpJ8x2NFZGjg3PNuc0TWq0EgiGS_mVkfg5f4pVN5uhXbmAx6hRyk80Z",
"signature": "2uREX1lseXdg3Vng/2+Hrlo/KHOWYoim+poj74wKFtw=",
"refresh_token": "5Aep861KIwKdekr90I4iHdtDgWwRoG7O_6uHrgJ.yVtMS0UaGxRqE6WFM77W7wCV4muVMgdqKjuWI2i5S6sjN2X",
"token_type": "Bearer",
"instance_url": "https://example-com.cs1.my.salesforce.com",
"scope": "id api refresh_token",
"issued_at": "1417182949781",
"id": "https://login.salesforce.com/id/00DS0000003K4fUMAS/00530000009hWLcAAM"
}</computeroutput></screen>
<para>
The output includes an <literal>access_token</literal> and a
<literal>refresh_token</literal>. You will need the
<literal>refresh_token</literal> in the following step.
</para>
</step>
<step>
<para>
Edit the <literal>configurationProperties</literal> in your Salesforce
connector configuration file
(<filename>openidm/samples/salesforce-connector/conf/provisioner.salesforce-salesforce.json</filename>)
to include your Consumer Key (<literal>clientID</literal>), Consumer
Secret (<literal>clientSecret</literal>), and refresh token.
</para>
<para>
In addition, set the <literal>"instanceUrl"</literal> to the value
returned in the previous step, and set the <literal>"enabled"</literal>
property to <literal>true</literal>.
</para>
<para>
The relevant excerpts of the <filename>provisioner.salesforce-salesforce.json</filename>
file are as follows:
</para>
<programlisting width="pagewide">{
"name" : "salesforce",
"enabled" : true,
"connectorRef" : {
...
"configurationProperties" : {
"connectTimeout" : 120000,
"loginUrl" : null,
"idleCheckInterval" : 10000,
"refreshToken" : "5Aep861KIwKdekr90I4iHdtDgWwRoG7O_6uHrgJ.yVtMS0UaGxRqE6WFM77W7wCV4muVMgdqKjuWI2i5S6sjN2X",
"clientSecret" : "4850xxxxxxxxxxxxx425",
"clientId" : "3MVG98dostKihXN7Is8Q0g5q1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPdB5f5ATwmaMuWxl",
"instanceUrl" : "https://example-com.cs1.my.salesforce.com",
"version" : 29
}
... </programlisting>
</step>
<step>
<para>
Check that your connector configuration is correct by testing the status
of the connector, over REST.
</para>
<screen><userinput>$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/system?_action=test"</userinput>
<computeroutput>[
{
"ok": true,
"connectorRef": {
"bundleVersion": "${provisioner.salesforcemodule.version}",
"systemType": "provisioner.salesforce",
"displayName": "Salesforce Connector",
"bundleName": "org.forgerock.openidm.salesforce",
"connectorName": "org.forgerock.openidm.salesforce.Salesforce"
},
"objectTypes": [
"User",
"PermissionSet",
"PermissionSetAssignment",
"Profile",
"PermissionSetLicenseAssign",
"Organization",
"PermissionSetLicense",
"Group",
"GroupMember"
],
"config": "config/provisioner.salesforce/salesforce",
"enabled": true,
"name": "salesforce"
}
]</computeroutput>
</screen>
</step>
</procedure>
<procedure>
<title>To Run Reconciliation by Using the Command Line</title>
<para>
The mapping configuration file (<filename>sync.json</filename>) for this
sample includes two mappings, <literal>sourceSalesforceUser_managedUser</literal>,
which synchronizes users from the Salesforce with the OpenIDM repository,
and <literal>managedUser_sourceSalesforceUser</literal>,
which synchronizes changes from the OpenIDM repository to Salesforce.
</para>
<step>
<para>
Reconcile the repository over the REST interface by running the following
command:
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--request POST \
"https://localhost:8443/openidm/recon?_action=recon&amp;mapping=sourceSalesforceUser_managedUser&amp;waitForCompletion=true"</userinput>
<computeroutput>{
"state": "SUCCESS",
"_id": "8a6281ef-6faf-43dd-af5c-3a842b38c468"
}</computeroutput></screen>
<para>
The reconciliation operation returns a reconciliation run ID and the
status of the operation. Reconciliation creates user objects from LDAP in
the OpenIDM repository, assigning the new objects random unique IDs.
</para>
</step>
<step>
<para>
View the recon entry over REST for an indication of the actions that were
taken on the OpenIDM repository.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/recon/8a6281ef-6faf-43dd-af5c-3a842b38c468"</userinput>
<computeroutput>{
"duration": 6447,
"ended": "2014-11-28T15:01:38.399Z",
"started": "2014-11-28T15:01:31.952Z",
"parameters": {
"null": false,
"boolean": false,
"number": false,
"list": false,
"object": {
"targetQuery": {
"_queryId": "query-all-ids",
"resourceName": "managed/user"
},
"sourceQuery": {
"_queryId": "query-all-ids",
"resourceName": "system/salesforce/User"
}
},
"pointer": {
"empty": true
},
"transformers": [],
"set": false,
"map": true,
"string": false,
"collection": false,
"wrappedObject": {
"targetQuery": {
"resourceName": "managed/user",
"_queryId": "query-all-ids"
},
"sourceQuery": {
"_queryId": "query-all-ids",
"resourceName": "system/salesforce/User"
}
}
},
"_id": "8a6281ef-6faf-43dd-af5c-3a842b38c468",
"mapping": "sourceSalesforceUser_managedUser",
"state": "SUCCESS",
"stage": "COMPLETED_SUCCESS",
"stageDescription": "reconciliation completed.",
"progress": {
"links": {
"created": 8,
"existing": {
"total": "0",
"processed": 0
}
},
"target": {
"created": 8,
"existing": {
"total": "0",
"processed": 0
}
},
"source": {
"existing": {
"total": "9",
"processed": 9
}
}
},
"situationSummary": {
"FOUND_ALREADY_LINKED": 0,
"UNASSIGNED": 0,
"TARGET_IGNORED": 0,
"SOURCE_IGNORED": 0,
"MISSING": 0,
"FOUND": 0,
"AMBIGUOUS": 0,
"UNQUALIFIED": 0,
"CONFIRMED": 0,
"SOURCE_MISSING": 0,
"ABSENT": 9
},
"statusSummary": {
"SUCCESS": 8,
"FAILURE": 1
}
}</computeroutput></screen>
<para>
The output shows that eight entries were created on the target
(<literal>managed/user</literal>).
</para>
</step>
<step>
<para>
You can display those users by querying the IDs in the managed/user
repository.
</para>
<screen>$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--request GET \
"https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"</userinput>
<computeroutput>{
"remainingPagedResults": -1,
"pagedResultsCookie": null,
"resultCount": 8,
"result": [
{
"_rev": "0",
"_id": "f15322f2-5873-4e5f-a4e5-2d4bc03dd190"
},
{
"_rev": "0",
"_id": "85879c60-afa1-4425-8c7a-5cccbbaff587"
},
{
"_rev": "0",
"_id": "ed3fe655-29a6-4016-b6bc-4b2356911fd1"
},
{
"_rev": "0",
"_id": "34678464-c080-41b1-8da6-d5fde9d35aeb"
},
{
"_rev": "0",
"_id": "02d5da29-8349-4f35-affc-5f6c331307ef"
},
{
"_rev": "0",
"_id": "f91d6fce-bf27-4379-9411-fd626f8a9528"
},
{
"_rev": "0",
"_id": "6ace9220-59e7-4d97-8683-e03362a9150c"
},
{
"_rev": "0",
"_id": "56863eea-35d7-4aeb-a017-74ef28fd3116"
}
]
</computeroutput></screen>
</step>
</procedure>
</section>
</section>
</chapter>