chap-samples.xml revision 3f86d4e2ad2128cae27b60d8584d6befb05505d8
<?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>
</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 it 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>Prepare the LDAP server by creating a base suffix of
<literal>dc=example,dc=com</literal>, and importing these objects from
<filename>samples/sample2/data/Example.ldif</filename>.</para>
<programlisting language="ldif">
dn: dc=com
objectClass: domain
objectClass: top
dc: com
dn: dc=example,dc=com
objectClass: domain
objectClass: top
dc: example
dn: ou=People,dc=example,dc=com
ou: people
description: people
objectclass: organizationalunit
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 for OpenIDM
userPassword: password
</programlisting>
<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>.</para>
</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>Running the Sample</title>
<para>Run reconciliation over the REST interface.</para>
<screen width="91"><?dbfo pgwide="1"?>$ 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"</screen>
<para>Successful reconciliation returns an "_id" object.</para>
<note>
<para>
If you want to run a <command>curl</command> command over regular HTTP,
remove the <literal>--cacert self-signed.crt</literal> option and change
the URL to use protocol HTTP over port 8080. For instructions on how
to set up a <filename>self-signed.crt</filename>, see the Integrator's
Guide section entitled <link xlink:show="new"
xlink:href="integrators-guide#rest-over-https"
xlink:role="http://docbook.org/xlink/role/olink">
<citetitle>Restrict REST Access to the HTTPS Port</citetitle></link>.
</para>
</note>
<para>With the configuration of sample 2, OpenIDM creates user objects from
LDAP in OpenIDM, assigning the new objects random unique IDs. To list user
objects by ID, run a query over the REST interface.</para>
<screen>
$ 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"</screen>
<para>
If you want the output to be formatted, include the
<literal>_prettyPrint=true</literal> request parameter or pipe the output to
a JSON parser such as <command>jq</command>, otherwise the resulting JSON
object appears all on one line.
</para>
<programlisting language="javascript">
{
"result": [
{
"_id": "53fbf0c5-aa37-4845-a4b3-92a21810a36d"
"_rev": "0",
},
{
"_id": "ae0aed26-2a9c-43e3-a6f0-ca707c9c6455"
"_rev": "0",
}
],
"resultCount": 2,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</programlisting>
<para>
You should now be able to review reconciled users in the UI. Connect to
an appropriate URI such as <literal>https://localhost:8443/openidmui</literal>,
log in as the <literal>openidm-admin</literal> user, and navigate to the
Users tab. Select the desired user for current profile information as shown.
</para>
<mediaobject>
<alt>Example of adding a new user</alt>
<imageobject>
<imagedata fileref="images/sample2-bjensen.png" format="PNG" />
</imageobject>
</mediaobject>
<para>Alternatively, you can still retrieve individual user objects by ID, as
shown here.</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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/53fbf0c5-aa37-4845-a4b3-92a21810a36d"</screen>
<para>Read <filename>openidm/samples/sample2/conf/sync.json</filename> and
<filename>openidm/samples/sample2/conf/provisioner.openicf-ldap.json</filename>
to understand the layout of the user object in the repository.</para>
</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 also connects to an external LDAP
server.</para>
<para>Unlike sample 2, 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.</para>
<para>
When configuring the LDAP server, be sure to import the appropriate
LDIF file, in this case,
<filename>/path/to/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>Running the Sample</title>
<para>Run reconciliation over the REST interface.</para>
<screen width="96"><?dbfo pgwide="1"?>$ 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"</screen>
<para>Successful reconciliation returns an "_id" object.</para>
<para>With the configuration of sample 2b, OpenIDM creates user objects from
LDAP in OpenIDM, assigning the new objects random unique IDs. To list user
objects by ID, run a query over the REST interface.</para>
<screen>
$ 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"
</screen>
<para>The resulting JSON object should include content similar to the
following.</para>
<programlisting language="javascript">
{
"result": [
{
"_id": "53fbf0c5-aa37-4845-a4b3-92a21810a36d"
"_rev": "0",
},
{
"_id": "6490489f-bbff-4855-808a-5c1862b09339"
"_rev": "0",
}
"resultCount": 2,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
],
}</programlisting>
<para>To retrieve the user, log into the UI, on an URI such as
<literal>https://localhost:8443/openidmui</literal> as the administrative
user
</para>
<para>
Alternatively, you can make a commons REST call to get a specific user
object by ID.</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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/53fbf0c5-aa37-4845-a4b3-92a21810a36d"</screen>
<para>Test the second mapping by creating a user in the OpenIDM
repository. On UNIX:</para>
<screen width="100">$ curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Content-Type: application/json" \
--data '{"mail":"fdoe@example.com","sn":"Doe","telephoneNumber","555-1234",
"userName":"fdoe","givenName":"Felicitas","description":"Felicitas Doe",
"displayName":"fdoe"}' \
--request POST \
"http://localhost:8443/openidm/managed/user?_action=create"</screen>
<para>On Windows:</para>
<screen width="100">$ 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\"}" ^
"http://localhost:8443/openidm/managed/user?_action=create"</screen>
<para>By default, automatic synchronization is enabled. This means that
when you update a managed object, any mappings defined in the
<filename>sync.json</filename> file 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 automatic reconciliation has been successful by
locating the new user in the LDAP directory.</para>
<screen>$ <userinput>/path/to/OpenDJ/bin/ldapsearch \
-bindDN "cn=Directory Manager" \
-bindPassword password \
-hostname localhost \
-port 1389 \
-baseDN "dc=example,dc=com" \
"uid=fdoe" </userinput>
<computeroutput>dn: uid=fdoe,ou=People,dc=example,dc=com
mail: fdoe@example.com
givenName: Felicitas
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
uid: fdoe
cn: Felicitas Doe
sn: Doe</computeroutput>
</screen>
</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 also connects to an external LDAP
server. The only difference is that in sample 2c, LDAP Group Memberships
are synchronized.</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.</para>
<para>In addition, two LDAP Groups should be created, which can be found in
the LDIF file: <filename>openidm/samples/sample2c/data/Example.ldif</filename>:</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. (The user
<literal>bjensen</literal> 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>. User <literal>bjensen</literal> is not
actually used in this sample.)</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>Running the Sample</title>
<para>Run reconciliation over the REST interface.</para>
<screen width="91"><?dbfo pgwide="1"?>$ <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"
</userinput></screen>
<para>Successful reconciliation returns an "_id" object.</para>
<para>With the configuration of sample 2c, OpenIDM creates user objects from
LDAP in OpenIDM, assigning the new objects random unique IDs. To list user
objects by ID, run a query over the REST interface.</para>
<screen>
$ 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"
</screen>
<para>The resulting JSON object should appear similar to the following:</para>
<programlisting language="javascript">
{
"result": [
{
"_id": "53fbf0c5-aa37-4845-a4b3-92a21810a36d"
"_rev": "0",
}
{
"_id": "56f0fb7e-3837-464d-b9ec-9d3b6af665c3",
"_rev": "0"
}
]
"resultCount": 2,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</programlisting>
<para>You should now be able to retrieve John Doe's user object in the UI,
navigating to the domain for OpenIDM such as
<literal>https://localhost:8443/openidmui</literal>. Alternatively, with a
bit of trial and error, you can retrieve John Doe's user object at the
command line by his ID.</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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/53fbf0c5-aa37-4845-a4b3-92a21810a36d"</screen>
<para>The user object should contain a property like: </para>
<screen>"ldapGroups":["cn=openidm,ou=Groups,dc=example,dc=com"]</screen>
<para>Now change the user on the OpenIDM side with the following REST
call (on UNIX):</para>
<screen width="96"><?dbfo pgwide="1"?>$ 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"
</screen>
<para>
On Windows, you might need to escape certain characters, so your REST call
would look like this:
</para>
<screen width="96"><?dbfo pgwide="1"?>$ 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"
</screen>
<para>This will change the user's <literal>ldapGroups</literal> property
in OpenIDM from <filename>"cn=openidm,ou=Groups,dc=example,dc=com"</filename>
to <filename>"cn=openidm2,ou=Groups,dc=example,dc=com"</filename> and, as a
result, the user will be removed from the one LDAP group and added to
the other LDAP group on OpenDJ.</para>
<para>Retrieve John Doe's user object again and notice the changed
<literal>"ldapGroups"</literal> property.</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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/56f0fb7e-3837-464d-b9ec-9d3b6af665c3"</screen>
<para>By default, automatic synchronization is enabled. This means that
when you update a managed object, any mappings defined in the
<filename>sync.json</filename> file 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>
</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>Run reconciliation <emphasis>for the groups mapping</emphasis> over
the REST interface.</para>
<screen width="91"><?dbfo pgwide="1"?>$ 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"</screen>
<para>Successful reconciliation returns an "_id" object.</para>
<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>
$ 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?_queryId=query-all-ids"
</screen>
<para>The resulting JSON object should include content similar to the
following.</para>
<programlisting language="javascript">
{
"result":[
{
"_id":"3c704429-aacd-4995-816a-fac33451c642"
"_rev": "0",
},
{
"_id":"b0982152-5099-4358-bdd1-45a39ebe0d77"
"_rev": "0",
}
]
"resultCount": 2,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</programlisting>
<para>To retrieve a group, get the object by ID.</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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/b0982152-5099-4358-bdd1-45a39ebe0d77"</screen>
<para>Your group's object should be similar to the following:</para>
<programlisting language="javascript">
{
"name": [
"openidm"
],
"uniqueMember": [
"uid=jdoe,ou=People,dc=example,dc=com"
],
"description": [],
"dn": "cn=openidm,ou=Groups,dc=example,dc=com",
"_rev": "0",
"_id": "b0982152-5099-4358-bdd1-45a39ebe0d77"
}
</programlisting>
</section>
</section>
<section xml:id="more-sample3">
<title>Sample 3 - Scripted SQL</title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 3 - Scripted SQL</secondary>
</indexterm>
<para>
Sample 3 shows an example configuration for the Scripted SQL connector. The
Scripted SQL connector communicates with the database through configurable
SQL scripts. Each operation, like create or delete, is represented by its own
script.
</para>
<para>
Prepare a fresh installation of OpenIDM before trying this sample.
</para>
<section xml:id="external-mysql-config-sample3">
<title>External Configuration</title>
<para>
In this example OpenIDM communicates with 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 host.</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>
<listitem>
<para>MySQL serves a database called <literal>HRDB</literal> with a table
called <literal>Users</literal>.</para>
</listitem>
<listitem>
<para>
The database schema is as described in the data definition language file,
<filename>openidm/samples/sample3/data/sample_HR_DB.mysql</filename>.
Import the file into MySQL before running the sample.
</para>
<screen width="87"><?dbfo pgwide="1"?>
$ <userinput>mysql -u root -p &lt; /path/to/openidm/samples/sample3/data/sample_HR_DB.mysql
</userinput>
Enter password:
$ </screen>
</listitem>
</itemizedlist>
<para>
Once imported, you can review the contents of the sample in MySQL. The
following command accesses the MySQL prompt:
</para>
<screen>
$ mysql -u root -p
</screen>
<para>
From the <literal>mysql &gt;</literal> prompt, use the following commands
to review the users configured in the <filename>sample_HR_DB.mysql</filename>
file:
</para>
<screen>
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. ...
| 2 | rowley | 2aa60a8ff... | Rowley | Birkin | Rowley Birkin | Rowl ...
| 3 | louis | 1119cfd37... | Louis | Balfour | Louis Balfour | Loui ...
| 4 | john | a1d7584da... | John | Smith | John Smith | John ...
| 5 | jdoe | edba955d0... | John | Doe | John Doe | John ...
+----+--------+------------------------------------------+-----------+---- ...
5 rows in set (0.01 sec)
</computeroutput>
</screen>
<para>
You can exit from the MySQL prompt with the <command>quit</command>
command.
</para>
<note>
<para>
The passwords in the output shown above are hashed to the SHA-1 standard,
as they cannot be read into OpenIDM as cleartext.
</para>
<para>
The SHA-1 Hash function is used for compatibility reasons. When configuring
a database for production, more secure algorithms should be used.
</para>
</note>
</section>
<section xml:id="install-sample3">
<title>Install the Sample</title>
<itemizedlist>
<listitem><para>Prepare OpenIDM as described in <xref
linkend="preparing-openidm"/>.</para></listitem>
<listitem>
<para>OpenIDM requires a MySQL driver, the MySQL Connector/J. Download
MySQL Connector/J, version 5.1 or later. Unpack the delivery 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>
</listitem>
<listitem>
<para>Start OpenIDM with the configuration for sample 3.</para>
<screen>$ cd /path/to/openidm
$ /startup.sh -p samples/sample3</screen>
</listitem>
</itemizedlist>
</section>
<section xml:id="run-sample3">
<title>Run the Sample</title>
<para>
The sample 3 <filename>sync.json</filename> configuration file contains a
mapping to reconcile OpenIDM and the external database. Run the
reconciliation with the following command.
</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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"</screen>
<para>
Reconciliation creates the five users from the database in the OpenIDM
repository. Check the result with the following command.
</para>
<screen>$ 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"</screen>
<para>
The result should resemble the following JSON object.
</para>
<programlisting language="javascript">
{
"result": [ {
"_id": "8366a23d-f6cf-46df-9746-469bf45aafcd"
"_rev": "0",
},
{
"_id": "3f90933b-9397-4897-84d0-03ed8d99f61e"
"_rev": "0",
},
{
"_id": "8fbf759d-bebc-42ed-b321-b69487b4470f"
"_rev": "0",
},
{
"_id": "9592de42-a8ef-4db3-9c6c-7d191e39b084"
"_rev": "0",
},
{
"_id": "d933441b-684b-446c-a89e-01eca6d21ef3"
"_rev": "0",
},
{
"_id": "fd962b71-752a-444b-8492-35bff57bec69"
"_rev": "0",
} ],
"resultCount": 6,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</programlisting>
<para>
Naturally, you can review the users from the UI, navigating to the domain
where OpenIDM is installed, with a URI similar to
<literal>https://localhost:8443/openidmui</literal>
</para>
<para>Alternatively, to view the JSON for one of the users, get the user by
the value of the <literal>_id</literal>.</para>
<screen width="83"><?dbfo pgwide="1"?>$ 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/8366a23d-f6cf-46df-9746-469bf45aafcd"</screen>
</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 width="89"><?dbfo pgwide="1"?>$ 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"</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-email"
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.reconName,
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"</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
samples/sample5/data/resource-schema-extension.xsd
http://openidm.forgerock.com/xml/ns/public/resource/openicf/resource-schema-1.xsd
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. Automatic synchronization then creates
that user in the AD resource.
</para>
</section>
</section>
<section xml:id="more-sample5b">
<title>Sample 5b - Compensated Synchronization with <literal>onSync</literal></title>
<indexterm>
<primary>Samples</primary>
<secondary>Sample 5b - Synchronization with the <literal>onSync</literal>
script hook</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, automatic 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 automatic 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-types-automatic"><citetitle>How Automatic
Sync works with onSync</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>).
With automatic synchronization, 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, automatic 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, automatic 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 automatic synchronization 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. 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>
To activate the OpenDJ ECL, enable replication as described in the
<link xlink:show="new"
xlink:href="http://opendj.forgerock.org/opendj-server/doc/admin-guide/index.html#chap-replication">
<citetitle>OpenDJ Administration Guide chapter on Managing Data
Replication</citetitle></link>
</para>
<para>
Once 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"</screen>
<para>The result of a successful reconciliation is an
<literal>_id</literal> object, similar to:</para>
<screen>{"_id":"9ece3807-08c3-4ec6-87fb-a6a2d0c71cee"}</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>$ 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"</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>$ /ldapsearch \
--port 1389 \
--baseDN dc=example,dc=com \
"(uid=jdoe)"</screen>
<!--
<para>To read the user object, navigate to the OpenIDM UI as the
administrative user, or use the <literal>_id</literal> value.</para>
<screen width="83"><?dbfo pgwide="1"?>$ curl
\-\-cacert self-signed.crt
\-\-header "X-OpenIDM-Username: openidm-admin"
\-\-header "X-OpenIDM-Password: openidm-admin"
\-\-header "Content-Type: application/json"
\-\-request GET
"https://localhost:8443/openidm/managed/user/b6b76e9c-d534-4d0a-ac81-87153169a223"</screen>
<para>The result should resemble the following JSON object, though all
on one line.</para>
<programlisting language="javascript">
{
"displayName": "John Doe",
"givenName": "John",
"userName": "jdoe",
"familyName": "Doe",
"description": "Created for OpenIDM",
"email": "jdoe@example.com",
"_rev": "0",
"_id": "b6b76e9c-d534-4d0a-ac81-87153169a223"
}</programlisting>
-->
</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>$ /ldapmodify
--port 1389
--defaultAdd
--bindDN "cn=Directory Manager"
--bindPassword password
--filename /path/to/bsmith.ldif</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>
Automatic 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>$ /ldapsearch \
--port 1389 \
--baseDN ou=people,dc=example,dc=com \
"(uid=bsmith)"</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 width="94"><?dbfo pgwide="1"?>$ 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"</screen>
<para>Reconciliation creates a user object in the repository. Retrieve the
user from the repository.</para>
<screen>$ 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"</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 width="99"><?dbfo pgwide="1"?>$ 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"</screen>
<para>Successful reconciliation returns an "_id" object.</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/workflow.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>$ cd /path/to/openidm
$ /startup.sh -p samples/sample9</screen>
</section>
<section xml:id="run-sample9">
<title>Running the Sample</title>
<orderedlist>
<listitem>
<para>Run reconciliation over the REST interface.</para>
<screen width="99"><?dbfo pgwide="1"?>$ 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"
</screen>
<para>Successful reconciliation returns an "_id" object.</para>
<para>The 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>$ 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"
</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>$ 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"
</screen>
<para>On Windows:</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 ^
--data "{\"requestApproved\": \"true\"}" ^
"https://localhost:8443/openidm/workflow/taskinstance/124?_action=complete"
</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>$ 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"
</screen>
<para>One user is returned.</para>
<screen>{
"result": [ {
"_rev": "0",
"_id": "1"
} ],
"resultCount": 1,
"pagedResultsCookie": null,
"remainingPagedResults": -1,
}</screen>
</listitem>
</orderedlist>
</section>
</section>
</chapter>