chap-configuration.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-configuration"
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>Configuring OpenIDM</title>
<para>OpenIDM configuration is split between <filename>.properties</filename> and container
configuration files, and also dynamic configuration objects. The majority
of OpenIDM configuration files are stored under
<filename>openidm/conf/</filename>, as described in the appendix listing the
<link xlink:href="integrators-guide#appendix-file-layout"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>File
Layout</citetitle></link>.</para>
<para>OpenIDM stores configuration objects in its internal repository.
You can manage the configuration by using either the REST access to the
configuration objects, or by using the JSON file based views.</para>
<section xml:id="configuration-objects">
<title>OpenIDM Configuration Objects</title>
<indexterm>
<primary>Objects</primary>
<secondary>Configuration objects</secondary>
</indexterm>
<indexterm>
<primary>Configuration</primary>
<secondary>Objects</secondary>
</indexterm>
<para>OpenIDM exposes internal configuration objects in JSON format.
Configuration elements can be either single instance or multiple
instance for an OpenIDM installation.</para>
<itemizedlist xml:id="single-instance-configuration-objects">
<title>Single Instance Configuration Objects</title>
<para>Single instance configuration objects correspond to services that have
at most one instance per installation.</para>
<para>JSON file views of these configuration objects are named
<filename><replaceable>object-name</replaceable>.json</filename>.</para>
<listitem>
<para>The <literal>audit</literal> configuration specifies how audit
events are logged.</para>
</listitem>
<listitem>
<para>The <literal>authentication</literal> configuration controls
REST access.</para>
</listitem>
<listitem>
<para>The <literal>cluster</literal> configuration defines how one
OpenIDM instance can be configured in a cluster.</para>
</listitem>
<listitem>
<para>The <literal>endpoint</literal> configuration controls any custom
REST endpoints.</para>
</listitem>
<listitem>
<para>The <literal>info</literal> configuration points to script files for
the customizable information service.</para>
</listitem>
<listitem>
<para>The <literal>managed</literal> configuration defines managed
objects and their schemas.</para>
</listitem>
<listitem>
<para>The <literal>policy</literal> configuration defines the policy
validation service.</para>
</listitem>
<listitem>
<para>The <literal>process access</literal> configuration defines access
to any configured workflows.</para>
</listitem>
<listitem>
<para>The <literal>repo.<replaceable>repo-type</replaceable></literal>
configuration such as <literal>repo.orientdb</literal> or
<literal>repo.jdbc</literal> configures the internal repository.</para>
</listitem>
<listitem>
<para>The <literal>router</literal> configuration specifies filters to
apply for specific operations.</para>
</listitem>
<listitem>
<para>The <literal>script</literal> configuration defines default
and custom configuration directories.</para>
</listitem>
<listitem>
<para>The <literal>sync</literal> configuration defines the mappings that
OpenIDM uses when synchronizing and reconciling managed objects.</para>
</listitem>
<listitem>
<para>The <literal>ui</literal> configuration defines the configurable
aspects of the default user interface.</para>
</listitem>
<listitem>
<para>The <literal>workflow</literal> configuration defines the
configuration of the workflow engine.</para>
</listitem>
</itemizedlist>
<itemizedlist xml:id="multiple-instance-configuration-objects">
<title>Multiple Instance Configuration Objects</title>
<para>Multiple instance configuration objects correspond to services that
can have many instances per installation. Configuration objects are named
<literal><replaceable>objectname</replaceable>/<replaceable>instancename</replaceable></literal>,
for example, <filename>provisioner.openicf/xml</filename>.</para>
<para><emphasis>JSON file</emphasis> views of these configuration objects
are named <filename><replaceable>objectname</replaceable>-<replaceable
>instancename</replaceable>.json</filename>, for example,
<filename>provisioner.openicf-xml.json.</filename></para>
<listitem>
<para>Multiple <literal>schedule</literal> configurations can run
reconciliations and other tasks on different schedules.</para>
</listitem>
<listitem>
<para>Multiple <literal>provisioner.openicf</literal> configurations
correspond to the resources connected to OpenIDM.</para>
</listitem>
<listitem>
<para>Multiple <literal>servletfilter</literal> configurations can
be used for different servlet filters such as the Cross Origin and GZip
filters.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="changing-configuration">
<title>Changing the Default Configuration</title>
<itemizedlist>
<para>When you change OpenIDM's configuration objects, take the following
points into account.</para>
<listitem>
<para>OpenIDM's authoritative configuration source is the internal
repository. JSON files provide a view of the configuration objects,
but do not represent the authoritative source.</para>
<para>OpenIDM updates JSON files after making configuration changes,
whether those changes are made through REST access to configuration
objects, or through edits to the JSON files.</para>
</listitem>
<listitem>
<para>OpenIDM recognizes changes to JSON files when it is running. OpenIDM
<emphasis>must</emphasis> be running when you delete configuration
objects, even if you do so by editing the JSON files.</para>
</listitem>
<listitem>
<para>Avoid editing configuration objects directly in the internal
repository. Rather edit the configuration over the REST API, or
in the configuration JSON files to ensure consistent behavior
and that operations are logged.</para>
</listitem>
<listitem>
<para>OpenIDM stores its configuration in the internal database by
default. If you remove an OpenIDM instance and do not specifically
drop the repository, the configuration remains in effect for a
new OpenIDM instance that uses that repository. For testing or
evaluation purposes, you can disable this <emphasis>persistent
configuration</emphasis> in the <filename>conf/system.properties</filename>
file by uncommenting the following line:</para>
<programlisting>
# openidm.config.repo.enabled=false
</programlisting>
<para>Disabling persistent configuration means that OpenIDM will
store its configuration in memory only. You should not disable
persistent configuration in a production environment.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="configuring-for-production">
<title>Configuring an OpenIDM System for Production</title>
<para>
Out of the box, OpenIDM is configured to make it easy to install and
evaluate. Specific configuration changes are required before you deploy
OpenIDM in a production environment.
</para>
<section xml:id="configuring-production-repo">
<title>Configuring a Production Repository</title>
<para>
By default, OpenIDM uses OrientDB for its internal repository so that you do
not have to install a database in order to evaluate OpenIDM. Before you use
OpenIDM in production, you must replace OrientDB with a supported
repository.
</para>
<para>
For more information, see <link xlink:href="install-guide#chap-repository"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Installing a
Repository for Production</citetitle></link> in the <citetitle>Installation
Guide</citetitle>.
</para>
</section>
<section xml:id="disabling-auto-config-updates">
<title>Disabling Automatic Configuration Updates</title>
<para>
By default, OpenIDM polls the JSON files in the <literal>conf</literal>
directory periodically for any changes to the configuration. In a production
system, it is recommended that you disable automatic polling for updates to
prevent untested configuration changes from disrupting your identity service.
</para>
<para>
To disable automatic polling for configuration changes, edit the
<filename>conf/system.properties</filename> file by uncommenting the
following line:
</para>
<programlisting># openidm.fileinstall.enabled=false</programlisting>
<para>
Before you disable automatic polling, you must have started the OpenIDM
instance at least once to ensure that the configuration has been loaded into
the repository.
</para>
<para>
Note if automatic polling is enabled, changes to scripts that are called
from a JSON configuration file are taken into account immediately.
</para>
</section>
<section xml:id="disabling-file-based-config">
<title>Disabling the File-Based Configuration View</title>
<para>
To control configuration changes to the OpenIDM system, you disable the
file-based configuration view and have OpenIDM read its configuration only
from the repository. To disable the file-based configuration view, edit the
<filename>conf/system.properties</filename> file to uncomment the following
line:
</para>
<programlisting># openidm.fileinstall.enabled=false</programlisting>
</section>
<!-- Section still in progress
<section xml:id="bootstrapping-config">
<title>Bootstrapping the OpenIDM Configuration</title>
<para>OpenIDM stores its configuration in the repository
(database), and provides a file-based view of this configuration.
For the system to start up with the configuration in the repository,
(or to store a new configuration in the repository), some basic
connectivity information is required to bootstrap the repository.</para>
<para>Bootstrapping information is provided in a combination of
the following three files, depending on how the configuration is
exposed:</para>
<itemizedlist>
<listitem>
<para><filename>openidm/conf/boot/boot.properties</filename></para>
<para><filename>openidm/conf/repo.*.json</filename></para>
<para><filename>openidm/conf/system.properties</filename></para>
</listitem>
</itemizedlist>
<section>
<title>Bootstrapping Information in <filename>boot.properties</filename></title>
<para>The <filename>boot.properties</filename> file specifies
security information...</para>
</section>
<section>
<title>Bootstrapping Information in <filename>repo.*.json</filename></title>
<para>Specifically if you are using the file-based configuration view,
<filename>openidm/conf/repo.*.json</filename> contains basic
connectivity information that is loaded to bootstrap.</para>
<para>Examples of repo configuration files are provided in
<filename>conf/repo.orientdb.json</filename> and
<filename>samples/repo.jdbc.json</filename>. This file determines the
repository type that is configured for the system - do not add more
than one <filename>repo.*.json</filename> file to the
<literal>conf</literal> directory.</para>
<para>For bootstrapping, only the basic connectivity properties
are required. These properties correspond to the properties that
are documented in the system properties section.</para>
</section>
<section>
<title>Bootstrapping Information in <filename>system.properties</filename></title>
<para>If you have disabled the file-based configuration view,
bootstrapping information must be included in the the
<filename>system.properties</filename> file.</para>
</section>
</section>
-->
</section>
<section xml:id="configuring-over-rest">
<title>Configuring OpenIDM Over REST</title>
<indexterm>
<primary>REST API</primary>
</indexterm>
<indexterm>
<primary>Configuration</primary>
<secondary>REST API</secondary>
</indexterm>
<para>OpenIDM exposes configuration objects under the
<literal>/openidm/config</literal> context path.</para>
<indexterm>
<primary>REST API</primary>
<secondary>Listing configuration objects</secondary>
</indexterm>
<para>You can list the configuration on the local host by performing a GET
<literal>https://localhost:8443/openidm/config</literal>. The following
example shows excerpts of the default configuration for an OpenIDM instance
started with Sample 1.</para>
<screen>
$ <userinput>curl \
--request GET \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--cacert self-signed.crt \
https://localhost:8443/openidm/config</userinput>
<computeroutput>
{
"configurations": [
{
"factoryPid": "servletfilter",
"pid": "servletfilter.ec099f08-bfd4-4ab4-8537-78e5b956c7cc",
"_id": "servletfilter/gzip"
},
{
"factoryPid": null,
"pid": "router",
"_id": "router"
},
...
{
"factoryPid": "endpoint",
"pid": "endpoint.7e9ec068-bb4a-4fa0-ae15-1706bb4a3a07",
"_id": "endpoint/jqgrid"
},
{
"factoryPid": "endpoint",
"pid": "endpoint.47978983-0411-425d-8f53-4022175e146a",
"_id": "endpoint/gettasksview"
},
...
{
"factoryPid": "ui",
"pid": "ui.b10eb4cb-83e3-4a4b-9d29-d91d90eb3053",
"_id": "ui/countries"
},
{
"factoryPid": "process",
"pid": "process.9863529c-60e0-42e3-b5d5-c5c704016e95",
"_id": "process/access"
}
] </computeroutput>
}</screen>
<para>Single instance configuration objects are located under
<literal>openidm/config/<replaceable>object-name</replaceable></literal>.
The following example shows the default <literal>audit</literal>
configuration.</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
"https://localhost:8443/openidm/config/audit"</userinput>
<computeroutput>
{
"eventTypes": {
"recon": {},
"activity": {
"filter": {
"actions": [
"create",
"update",
"delete",
"patch",
"action"
]
},
"passwordFields": [
"password"
],
"watchedFields": []
}
},
"exceptionFormatter": {
"file": "bin/defaults/script/audit/stacktraceFormatter.js",
"type": "text/javascript"
},
"logTo": [
{
"recordDelimiter": ";",
"logType": "csv",
"location": "audit"
},
{
"useForQueries": true,
"logType": "repository"
}
]
} </computeroutput>
</screen>
<para>Multiple instance configuration objects are found under
<literal>openidm/config/<replaceable>object-name</replaceable>/<replaceable
>instance-name</replaceable></literal>.
</para>
<para>The following example shows the configuration for the XML connector
provisioner, based on the first IDM sample described in the
<link xlink:show="new" xlink:href="install-guide#chap-sample"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>
First IDM Sample</citetitle></link> of the OpenIDM Installation Guide.</para>
<screen>
$ <userinput>curl \
--cacert self-signed.crt \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
"https://localhost:8443/openidm/config/provisioner.openicf/xml"</userinput>
<computeroutput>
{
"operationTimeout": {
"SCRIPT_ON_CONNECTOR": -1,
"VALIDATE": -1,
"SYNC": -1,
"DELETE": -1,
"TEST": -1,
"UPDATE": -1,
"CREATE": -1,
"AUTHENTICATE": -1,
"SEARCH": -1,
"GET": -1,
"SCRIPT_ON_RESOURCE": -1,
"SCHEMA": -1
},
"connectorRef": {
"connectorName": "org.forgerock.openicf.connectors.xml.XMLConnector",
"bundleVersion": "1.1.0.1",
"bundleName": "org.forgerock.openicf.connectors.xml-connector"
},
"connectorPoolingSupported": true,
"syncFailureHandler": {
"maxRetries": 5,
"postRetryAction": "logged-ignore"
},
"configurationProperties": {
"xsdFilePath": "samples/sample1/data/resource-schema-extension.xsd",
"xsdIcfFilePath": "samples/sample1/data/resource-schema-1.xsd",
"xmlFilePath": "samples/sample1/data/xmlConnectorData.xml"
},
"objectTypes": {
"account": {
"nativeType": "__ACCOUNT__",
"$schema": "http://json-schema.org/draft-03/schema",
"type": "object",
"properties": {
"securityAnswer": {
"nativeType": "string",
"nativeName": "securityAnswer",
"required": true,
"type": "string"
},
"securityQuestion": {
"nativeType": "string",
"nativeName": "securityQuestion",
"required": true,
"type": "string"
},
"password": {
"nativeType": "string",
"nativeName": "password",
"type": "string"
},
"mobileTelephoneNumber": {
"nativeType": "string",
"nativeName": "mobileTelephoneNumber",
"required": true,
"type": "string"
},
"_id": {
"nativeName": "__UID__",
"type": "string"
},
"email": {
"nativeType": "string",
"nativeName": "email",
"type": "string"
},
"description": {
"nativeType": "string",
"nativeName": "__DESCRIPTION__",
"type": "string"
},
"name": {
"nativeType": "string",
"nativeName": "__NAME__",
"required": true,
"type": "string"
},
"roles": {
"nativeType": "string",
"nativeName": "roles",
"required": false,
"type": "string"
},
"lastname": {
"nativeType": "string",
"nativeName": "lastname",
"required": true,
"type": "string"
},
"firstname": {
"nativeType": "string",
"nativeName": "firstname",
"type": "string"
}
},
"id": "__ACCOUNT__"
}
},
"operationOptions": {},
"name": "xmlfile",
"producerBufferSize": 100,
"poolConfigOption": {
"maxObjects": 10,
"minEvictableIdleTimeMillis": 120000,
"maxIdle": 10,
"minIdle": 1,
"maxWait": 150000
}
}
</computeroutput>
</screen>
<para>You can change the configuration over REST by using an HTTP PUT request
to modify the required configuration object. The following example modifies
the <filename>router.json</filename> file to remove all filters, effectively
bypassing any policy validation.</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 '{
"filters" : [
{
"onRequest" : {
"type" : "text/javascript",
"file" : "bin/defaults/script/router-authz.js"
}
}
]
}' \
"https://localhost:8443/openidm/config/router"</userinput>
<computeroutput>
{
"filters": [
{
"onRequest": {
"file": "bin/defaults/script/router-authz.js",
"type": "text/javascript"
}
}
]
} </computeroutput>
</screen>
<para>See the <link xlink:href="integrators-guide#appendix-rest"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>REST API
Reference</citetitle></link> appendix for additional details and
examples using REST access to update and patch objects.</para>
</section>
<section xml:id="using-property-substitution">
<title>Using Property Value Substitution in the Configuration</title>
<para>In an environment where you have more than one OpenIDM instance, you
might require a configuration that is similar, but not identical, across the
different OpenIDM hosts. OpenIDM supports variable replacement in its
configuration which means that you can modify the effective configuration
according to the requirements of a specific environment or OpenIDM instance.</para>
<itemizedlist><para>Property substitution enables you to achieve the following:</para>
<listitem><para>Define a configuration that is specific to a single OpenIDM
instance, for example, setting the location of the keystore on a particular
host.</para></listitem>
<listitem><para>Define a configuration whose parameters vary between different
environments, for example, the URLs and passwords for test, development,
and production environments.</para></listitem>
<listitem><para>Disable certain capabilities on specific nodes. For example,
you might want to disable the workflow engine on specific instances.</para>
</listitem>
</itemizedlist>
<para>When OpenIDM starts up, it combines the system configuration, which
might contain specific environment variables, with the defined OpenIDM
configuration properties. This combination makes up the effective configuration
for that OpenIDM instance. By varying the environment properties, you can
change specific configuration items that vary between OpenIDM instances or
environments.</para>
<para>Property references are contained within the construct
<literal>&amp;{ }</literal>. When such references are found, OpenIDM replaces
them with the appropriate property value, defined in the
<filename>boot.properties</filename> file.</para>
<example>
<para>The following example defines two separate OpenIDM environments -
a development environment and a production environment. You can specify the
environment at startup time and, depending on the environment, the database
URL is set accordingly.</para>
<para>The environments are defined by adding the following lines to the
<filename>conf/boot.properties</filename> file:</para>
<programlisting language="javascript">
PROD.location=production
DEV.location=development
</programlisting>
<para>The database URL is then specified as follows in the
<filename>repo.orientdb.json</filename> file:</para>
<programlisting language="javascript">
{
"dbUrl" : "plocal:/db/&amp;{&amp;{environment}.location}-openidm",
...
}
</programlisting>
<para>The effective database URL is determined by setting the
<literal>OPENIDM_OPTS</literal> environment variable when you start OpenIDM.
To use the production environment, start OpenIDM as follows:</para>
<screen>
$ export OPENIDM_OPTS="-Xmx1024m -Xms1024m -Denvironment=PROD"
$ /startup.sh
</screen>
<para>To use the development environment, start OpenIDM as follows:</para>
<screen>
$ export OPENIDM_OPTS="-Xmx1024m -Xms1024m -Denvironment=DEV"
$ /startup.sh
</screen>
</example>
<section xml:id="property-substitution-system">
<title>Using Property Value Substitution With System Properties</title>
<para>You can use property value substitution in conjunction with the system
properties, to modify the configuration according to the system on which
the OpenIDM instance runs.</para>
<example xml:id="custom-audit-log-location">
<title>Custom Audit Log Location</title>
<para>The following example modifies the <literal>audit.json</literal> file so
that the log file is written to the user's directory. The
<literal>user.home</literal> property is a default Java System property.</para>
<programlisting language="javascript">{
"logTo" : [
{
"logType" : "csv",
"location" : "&amp;{user.home}/audit",
"recordDelimiter" : ";"
}
]
}
</programlisting>
</example>
<para>You can define <emphasis>nested</emphasis> properties (that is a property
definition within another property definition) and you can combine system
properties and boot properties.</para>
<example>
<para>The following example uses the <literal>user.country</literal> property,
a default Java System property. The example defines specific LDAP ports,
depending on the country (identified by the country code) in the
<literal>boot.properties</literal> file. The value of the LDAP port (set in
the <literal>provisioner.openicf-ldap.json</literal> file) depends on the
value of the <literal>user.country</literal> System property.</para>
<para>The port numbers are defined in the <literal>boot.properties</literal>
file as follows:</para>
<programlisting language="javascript">
openidm.NO.ldap.port=2389
openidm.EN.ldap.port=3389
openidm.US.ldap.port=1389</programlisting>
<para>The following extract from the
<literal>provisioner.openicf-ldap.json</literal> file shows how the value of
the LDAP port is eventually determined, based on the System property:</para>
<programlisting language="javascript">
"configurationProperties" :
{
"credentials" : "Passw0rd",
"port" : "&amp;{openidm.&amp;{user.country}.ldap.port}",
"principal" : "cn=Directory Manager",
"baseContexts" :
[
"dc=example,dc=com"
],
"host" : "localhost"
}
</programlisting>
</example>
</section>
<section xml:id="property-substitution-limitations">
<title>Limitations of Property Value Substitution</title>
<itemizedlist>
<para>Note the following limitations when you use property value
substitution:</para>
<listitem><para>You cannot reference complex objects or properties with syntaxes
other than String. Property values are resolved from the
<literal>boot.properties</literal> file or from the System properties and the
value of these properties is always in String format.</para>
<para>Property substitution of boolean values is currently only supported in
stringified format, that is, resulting in <literal>"true"</literal> or
<literal>"false"</literal>.
</para></listitem>
<listitem><para>Substitution of encrypted property values is currently not
supported.</para></listitem>
<!-- TO DO
For now, encypted property substitution is not supported. Check and
replace with this chunk when it is
<listitem><para>Encryption is performed before the property value substitution.
This can be problematic for properties whose values should be encrypted after
substitution, for example, passwords.</para>
<para>To use encrypted values with property substitution break the encrypted
object down into separate string properties, as shown in the following
example.</para>
<para>The following extract of the <literal>repo.jdbc.json</literal> file shows
the expected encrypted object:</para>
<programlisting language="javascript">"credentials" : {
"$crypto" : {
"value" : {
"iv" : "6Lk0/4WL8VsobGNCSh7bNQ==",
"data" : "R0L0E0h8opPFANzb2iYrlg==",
"cipher" : "AES/CBC/PKCS5Padding",
"key" : "openidm-sym-default"
},
"type" : "x-simple-encryption"
}
}
</programlisting>
<para>To use property substitution for the <literal>iv</literal> and
<literal>data</literal> properties, define two string properties in the
<literal>boot.properties</literal> file:</para>
<programlisting language="javascript">
ldap.credentials.iv=6Lk0/4WL8VsobGNCSh7bNQ==
ldap.credentials.data=R0L0E0h8opPFANzb2iYrlg==
</programlisting>
<para>You can then change the configuration in <literal>repo.jdbc.json</literal>
as follows:</para>
<programlisting language="javascript">"credentials" : {
"$crypto" : {
"value" : {
"iv" : "&amp;{ldap.credentials.iv}",
"data" : "&amp;{ldap.credentials.data}",
"cipher" : "AES/CBC/PKCS5Padding",
"key" : "openidm-sym-default"
},
"type" : "x-simple-encryption"
}
}
</programlisting></listitem>
-->
</itemizedlist>
</section>
</section>
<!-- Add this section when you have been able to test the example
</section>
<section xml:id="optimizing-the-openidm-object-model">
<title>Optimizing the OpenIDM Object Model</title>
<para>You can improve performance in specific deployment scenarios by
customizing the way in which OpenIDM objects are mapped to the relational
database. This section assumes that you are using MySQL as an internal
repository (for more information, see
<link xlink:href="install-guide#chap-repository"
xlink:role="http://docbook.org/xlink/role/olink">
<citetitle>Installing a Repository For Production</citetitle></link>).</para>
<itemizedlist>
<para>There are two ways in which OpenIDM objects can be mapped to the
relational database tables:</para>
<listitem><para><emphasis>Using a generic mapping</emphasis>, which allows
arbitrary objects to be stored without specific setup or administration.</para>
<para>The generic mapping facilitates rapid development, and makes system
evolution and maintenance simpler by providing a more stable database structure.
However, the generic mapping incurs a performance cost because it does not
take full advantage of the benefits of a relational database. The object model
is not normalized in the traditional sense and there is less flexibility in
indexing.</para></listitem>
<listitem><para><emphasis>Using an explicit mapping</emphasis>, which allows you
to optimize storage and queries by explicitly mapping a specific object type to
the database.</para>
<para>The explicit mapping provides a traditional object-relational mapping
and can therefore take greater advantage of relational database capabilities.
However, an explicit mapping implies that, as an administrator, you must
ensure that the mapping and objects remain synchronized at all times, and
must manage any migration or upgrade procedures carefully when objects are
added or changed. There are currently some limitations to an explicit table
mapping, and it has not been extensively tested for managed objects.</para>
</listitem>
</itemizedlist>
<para>Out of the box, OpenIDM uses an explicit mapping for a few specific tables
whose structure will remain stable, tables for which easy external queries are
required, or tables for which performance is particularly important. Tables for
managed objects (such as "managed/user") use a generic mapping by default.</para>
<para>The generic mapping provided out of the box indexes every property to
make it searchable, as shown in the following <literal>repo.jdbc.json</literal>
extract:</para>
<programlisting language="javascript">"credentials" : {
"genericMapping" : {
"managed/*" : {
"mainTable" : "managedobjects",
"propertiesTable" : "managedobjectproperties",
"searchableDefault" : true
}
},
</programlisting>
<para>In certain deployments, such a configuration might not
meet your performance requirements. You can optimize the performance of the
generic mapping by restricting the properties that are indexed (searchable).
To do this, change the <literal>searchableDefault</literal> property to
<literal>false</literal> and explicitly specify the properties that can be
searched by using the <literal>searchable</literal> setting.</para>
<para>Alternatively, leave the <literal>searchableDefault</literal> property
set to <literal>true</literal> and specify which properties should
<emphasis>not</emphasis> be indexed by setting the
<literal>searchableDefault</literal> property to <literal>false</literal> for
those properties.</para>
<para>The following example creates a separate generic table for managed/user
objects and indexes only two properties, <literal>_id</literal> and
<literal>username</literal>:</para>
<programlisting language="javascript">
"genericMapping" : {
"managed/user" : {
"mainTable" : "manageduserobjects",
"propertiesTable" : "manageduserobjectproperties",
"searchableDefault" : false,
"properties" : {
"/_id" : {
"searchable" : true
},
"/username" : {
"searchable" : true
}
}
}
},
</programlisting>
<para>Another way to optimize performance is to change the mapping
configuration to use a different mapping table type. For example, you can
change the configuration for a managed object such as "managed/user" to an
explicit table mapping, in which you specifically define the columns and
indexes. For example:</para>
<programlisting language="javascript">
"explicitMapping" : {
"managed/user" : {
...
</programlisting>
<orderedlist>
<para>In general, you should assess the following performance improvement
strategies, in order:</para>
<listitem><para>Optimize the generic mapping by explicitly defining which
properties should be searchable.</para></listitem>
<listitem><para>Place different managed object types in different generic
tables (by default, all managed object types are mapped to the
<literal>managedobjects</literal> and <literal>managedobjectproperties</literal>
tables).</para></listitem>
<listitem><para>Finally, consider explicit mappings where appropriate.</para></listitem>
</orderedlist>
</section>
-->
<section xml:id="adding-custom-endpoints">
<title>Adding Custom Endpoints</title>
<para>
You can customize OpenIDM to meet the specific requirements of your
deployment by adding your own RESTful endpoints. Endpoints are configured
in files named
<filename>conf/endpoint-<replaceable>name</replaceable>.json</filename>,
where <replaceable>name</replaceable> generally describes the purpose of the
endpoint.
</para>
<para>
A sample custom endpoint configuration is provided in the
<filename>openidm/samples/customendpoint</filename> directory. The use
of this sample is described in
<xref linkend="adding-custom-endpoints-example" /> Custom
endpoints in OpenIDM can be written either in JavaScript or Groovy.
The sample includes three files:
</para>
<variablelist>
<varlistentry>
<term>conf/endpoint-echo.json</term>
<listitem>
<para>
Provides the configuration for the endpoint.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>script/echo.js</term>
<listitem>
<para>
Supports an endpoint written in JavaScript.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>script/echo.groovy</term>
<listitem>
<para>
Supports a script written in Groovy.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Endpoint configuration files have a certain structure. They may cite
scripts written in JavaScript or Groovy.
</para>
<para>
The cited scripts include defined <literal>request</literal> and
<literal>context</literal> global variables.
</para>
<section xml:id="adding-custom-endpoints-structure">
<title>The Components of an Endpoint Configuration File</title>
<para>
You can find sample components in the
<filename>/path/to/openidm/sample/customendpoint</filename> directory.
In that the directory, the <filename>conf/endpoint-echo.json</filename>
configuration file depicts one typical endpoint, configured to use a
Groovy script specified in the <filename>script/echo.groovy</filename>
file.
</para>
<programlisting language="javascript">
{
"file" : "echo.groovy",
"type" : "groovy",
"_file" : "echo.js",
"_type" : "text/javascript"
}
</programlisting>
<para>
The <literal>"_file"</literal> and <literal>"_type"</literal> properties
are comments, which you can change to accommodate an endpoint written in
JavaScript.
</para>
<para>
If appropriate, you can also include a <literal>context</literal> property
in this file. The following example shows how the
<literal>context</literal> is used to display routing to an endpoint.
</para>
<programlisting language="javascript">
"context" : "endpoint/echo",
</programlisting>
<para>
The endpoint configuration can specify the route on which the endpoint is
available. For an example, look at the
<filename>conf/endpoint-linkedView.json</filename> file. The code shown
declares the route on which the endpoint is available.
</para>
<programlisting language="javascript">
{
"context": "endpoint/linkedView/*",
"type" : "text/javascript",
"source" : "require('linkedView').fetch(request.resourceName);"
}
</programlisting>
<para>
The following list describes each property in the custom endpoint
configuration file:
</para>
<variablelist>
<varlistentry>
<term><literal>"type"</literal></term>
<listitem>
<para>
string, required
</para>
<para>
Specifies the type of script to be executed. Supported types include
<literal>"text/javascript"</literal> and <literal>"groovy"</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>"file"</literal> or <literal>"source"</literal></term>
<listitem>
<para>
The actual script, inline, or a path to the file that
contains the script. The script files associated with this sample,
<filename>echo.js</filename> and <filename>echo.groovy</filename>,
support requests using all ForgeRock RESTful CRUD operations
(including PATCH, ACTION, and QUERY).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>context</literal></term>
<listitem>
<para>
Requests are dispatched, routed, handled, processed, and more, in a
<literal>context</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="adding-custom-endpoints-context-components">
<title>Context Component Access Methods</title>
<para>
For both JavaScript and Groovy, the context consists of a chain of
structures that provide different levels of detail. The detail varies
depending on the context type:
</para>
<variablelist>
<varlistentry>
<term><literal>security</literal></term>
<listitem>
<para>
Provides authentication / authorization data.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>http</literal></term>
<listitem>
<para>
Provides data from the HTTP request.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>router</literal>
</term>
<listitem>
<para>
Provides data on where the information is sent.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
JavaScript and Groovy access these context structures in different ways.
The term shown is the JavaScript access method; the definition includes
the Groovy access method.
</para>
<variablelist>
<varlistentry>
<term><literal>context.current</literal></term>
<listitem>
<para>
In Groovy, known simply as <literal>context</literal>
</para>
<para>
The current context in which the request is handled by a script or a
script-hook.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>context.http</literal>
</term>
<listitem>
<para>
In Groovy, known as one of the following:
<simplelist>
<member>
<literal>
context.asContext(org.forgerock.json.resource.servlet.HttpContext.class)
</literal>
</member>
<member>
<literal>
context.getContext("http")
</literal>
</member>
</simplelist>
</para>
<para>
The HTTP context.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>context.security</literal></term>
<listitem>
<para>
In Groovy, known as one of the following:
<simplelist>
<member>
<literal>
context.asContext(org.forgerock.json.resource.SecurityContext.class)
</literal>
</member>
<member>
<literal>
context.getContext("security")
</literal>
</member>
</simplelist>
</para>
<para>
The security context.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="adding-custom-endpoints-request">
<title>Custom Endpoints and <literal>request</literal> Objects</title>
<para>The endpoint configuration file does not just point to a script. It
should specify a script <literal>source</literal> or a script
<literal>file</literal> as described in
<xref linkend="adding-custom-endpoints-structure" /> The script will be
invoked with a global <literal>request</literal> variable in its scope.
</para>
<para>
Depending on the type of <literal>request</literal>,
the associated content may include the following
properties:
</para>
<variablelist>
<varlistentry>
<term><literal>method</literal></term>
<listitem>
<para>The requested operation, which may be <literal>create</literal>,
<literal>read</literal>, <literal>update</literal>,
<literal>delete</literal>, <literal>patch</literal>,
<literal>query</literal> or <literal>action</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>resourceName</literal></term>
<listitem>
<para>The local identifier, without the <literal>endpoint/</literal>
prefix, such as <literal>echo</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>newResourceId</literal></term>
<listitem>
<para>
An identifier associated with a new resource, associated with
the <literal>create</literal> method.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>additionalParameters</literal></term>
<listitem>
<para>The sample code returns request parameters from an HTTP GET
with <literal>?param=x</literal>, as
<literal>"params":{"param":"x"}</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>revision</literal></term>
<listitem>
<para>
The revision level associated with the method used, relative to
a <literal>newResourceId</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>content</literal></term>
<listitem>
<para>
Content based on the latest version of the object, using
<literal>getObject</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>context</literal></term>
<listitem>
<para>
Based on a JSON object that contains nested attributes. The object
with the attributes, defines the request, based on ForgeRock REST
<link xlink:show="new"
xlink:href="integrators-guide#rest-supported-operations"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>
Supported Operations</citetitle></link>.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="adding-custom-endpoints-chains">
<title>Custom Endpoints, Contexts, and Chains</title>
<para>
Custom endpoints include contexts that may be wrapped in various layers,
analogous to the way network packets can be wrapped at ascending network
levels.
</para>
<para>
As an example, start with a request such as the following:
</para>
<screen>
GET http://localhost:8080/openidm/endpoint/echo?queryId=query-all-ids&amp;_para=foo
</screen>
<para>
A request at an endpoint starts with a root context, associated with a
specific context ID, and the
<literal>org.forgerock.json.resource.RootContext</literal> context.
</para>
<para>
The root context is wrapped in the security context that holds the
authentication and authorization detail for the request. The associated
class is <literal>org.forgerock.json.resource.SecurityContext</literal>,
with an <literal>authenticationId</literal> user name such as
<literal>openidm-admin</literal>, and associated roles such as
<literal>openidm-authorized</literal>.
</para>
<para>
That security context is further wrapped by the HTTP context, with the
target URI. The class is
<literal>org.forgerock.json.resource.HttpContext</literal>, and it is
associated with the normal parameters of a REST call, including a user
agent, authorization token, and method.
</para>
<para>
The HTTP context is then further wrapped by one or more server / router
context(s). That class is
<literal>org.forgerock.json.resource.RouterContext</literal>,
with an endpoint URI. You may see several layers of server and router
contexts.
</para>
</section>
<section xml:id="adding-custom-endpoints-more">
<title>Additional Custom Endpoint Parameters</title>
<para>
A couple of additional parameters are shown with the
<literal>query</literal> request method. You can review how this works
in the following section of the OpenIDM Installation Guide:
<link xlink:show="new"
xlink:role="http://docbook.org/xlink/role/olink"
xlink:href="install-guide#more-sample2c"><citetitle>Sample 2c -
Synchronizing LDAP Group Membership</citetitle></link>.
</para>
<para>The final statement in the script is the return value. In the
following example, there is no <literal>return</literal> keyword, and
the value of the last statement
(<literal>x</literal>) is returned.</para>
<programlisting language="javascript">var x = "Sample return";
functioncall();
x </programlisting>
</section>
<section xml:id="adding-custom-endpoints-example">
<title>Custom Endpoint Example</title>
<para>The following example uses the sample provided in the
<filename>openidm/samples/customendpoint</filename> directory, copied to
the <filename>openidm/conf</filename> and
<filename>openidm/script</filename> directories. The output from the query
shows the complete request structure.
</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/endpoint/echo?_queryId=query-all-ids"
</userinput>
<computeroutput>{
"result" : [ {
"method" : "query",
"resourceName" : "",
"pagedResultsCookie" : null,
"pagedResultsOffset" : 0,
"pageSize" : 0,
"queryExpression" : null,
"queryId" : "query-all-ids",
"queryFilter" : "null",
"parameters" : { },
"context" : {
"parent" : {
"parent" : {
"parent" : {
"parent" : {
"parent" : {
"parent" : null,
"contextName" : "root",
"rootContext" : true,
"id" : "43576021-fe54-4468-8d10-09b14af2a36d"
},
"contextName" : "security",
"authenticationId" : "openidm-admin",
"authorizationId" : {
"id" : "openidm-admin",
"component" : "repo/internal/user",
"roles" : [ "openidm-admin", "openidm-authorized" ]
},
"rootContext" : false,
"id" : "43576021-fe54-4468-8d10-09b14af2a36d"
},
"headers" : {
"X-OpenIDM-Username" : [ "openidm-admin" ],
"Host" : [ "localhost:8443" ],
"Accept" : [ "*/*" ],
"X-OpenIDM-Password" : [ "openidm-admin" ],
"User-Agent" : [ "curl/7.19.7 (x86_64-redhat-linux-gnu)
libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18
libssh2/1.4.2" ]
},
"parameters" : {
"_queryId" : [ "query-all-ids" ],
"_prettyPrint" : [ "true" ]
},
"external" : true,
"contextName" : "http",
"method" : "GET",
"path" : "https://localhost:8443/openidm/endpoint/echo",
"rootContext" : false,
"id" : "43576021-fe54-4468-8d10-09b14af2a36d"
},
"contextName" : "apiInfo",
"apiVersion" : "2.3.1-SNAPSHOT",
"apiName" : "org.forgerock.commons.json-resource-servlet",
"rootContext" : false,
"id" : "43576021-fe54-4468-8d10-09b14af2a36d"
},
"contextName" : "server",
"rootContext" : false,
"id" : "43576021-fe54-4468-8d10-09b14af2a36d"
},
"uriTemplateVariables" : { },
"contextName" : "router",
"matchedUri" : "endpoint/echo",
"baseUri" : "endpoint/echo",
"rootContext" : false,
"id" : "43576021-fe54-4468-8d10-09b14af2a36d"
}
} ],
"resultCount" : 1,
"pagedResultsCookie" : null,
"remainingPagedResults" : -1
} </computeroutput> </screen>
<para>You must protect access to any custom endpoints by configuring the
appropriate authorization for those contexts. For more information, see
the <link xlink:href="integrators-guide#openidm-authorization"
xlink:role="http://docbook.org/xlink/role/olink">
<citetitle>Authorization</citetitle></link> section.</para>
</section>
</section>
<section xml:id="config-default-directories">
<title>Default and Custom Configuration Directories</title>
<para>
You can set up custom configuration files in directories as
defined in the <filename>openidm/conf/script.json</filename> file.
</para>
<para>
The following portion of the <filename>script.json</filename> file
points to sources in installation and project directories. As implied in the
section on
<link xlink:href="integrators-guide#startup-configuration" xlink:show="new"
xlink:role="http://docbook.org/xlink/role/olink">
<citetitle>Specifying the OpenIDM Startup Configuration</citetitle>
</link>, the <literal>launcher.project.location</literal> is the directory
cited if you start OpenIDM with a specific project directory.</para>
<programlisting>...
"sources" : {
"default" : {
"directory" : "&amp;{launcher.install.location}/bin/defaults/script",
"subdirectories" : "true",
"visibility" : "public",
"type" : "auto-detect"
},
"install" : {
"directory" : "&amp;{launcher.install.location}",
"subdirectories" : "true",
"visibility" : "public",
"type" : "auto-detect"
},
"project" : {
"directory" : "&amp;{launcher.project.location}",
"subdirectories" : "true",
"visibility" : "public",
"type" : "auto-detect"
},
"project-script" : {
"directory" : "&amp;{launcher.project.location}/script",
"subdirectories" : "true",
"visibility" : "public",
"type" : "auto-detect"
}
...</programlisting>
<para>
For example, if you start OpenIDM from the <filename>/path/to/openidm</filename>
directory with the following command:
</para>
<screen>$ /startup.sh -p /path/to/openidm/customconfig
</screen>
<para>
The <literal>launcher.project.location</literal> directory would be
<filename>/path/to/openidm/customconfig</filename>.
</para>
<para>
The <filename>script.json</filename> file also refers to a
<literal>launcher.install.location</literal> directory, which is
<filename>/path/to/openidm</filename>.
</para>
<para>Thus, based on the way the <filename>script.json</filename> file
is configured for <literal>project</literal>
and <literal>project-script</literal>, you can add custom
configuration and script files to the
<filename>/path/to/openidm/customconfig</filename> and the
<filename>/path/to/openidm/customconfig/script</filename> directories.
</para>
</section>
<!-- Add this when OrientDB is supported in production
<section xml:id="orientdb-perf">
<title>Tuning the Performance of the OrientDB Repository</title>
<para>By default, the embedded OrientDB repository assumes an
environment with unreliable (non-RAID) hardware. These settings might
not be appropriate in other environments.</para>
<para>To improve performance in a deployment that runs on reliable
(RAID) hardware, change the following settings in the OrientDB
configuration file
(<filename>/path/to/openidm/conf/repo.orientdb.json</filename>):</para>
<screen>
"transactionCommitSynch" : false,
"transactionLogSynch" : false,
"nonTransactionRecordUpdateSync" : false
</screen>
<para>By default, these parameters are all set to <literal>true</literal>,
which implies the following:</para>
<simplelist>
<member><literal>transactionCommitSynch</literal> - The storage
is synchronized after each transaction commit.</member>
<member><literal>transactionLogSynch</literal> - A disk synch is
executed for each log entry, which slows down transactions but
guarantees transaction reliability on non-reliable drives.</member>
<member><literal>nonTransactionRecordUpdateSync</literal> - A disk
synch is executed at every record operation. This slows down
record updates but guarantee reliability on unreliable drives.</member>
</simplelist>
</section>
-->
</chapter>