diff --git a/openidm-doc/pom.xml b/openidm-doc/pom.xml index b04c82000..c522b6d51 100644 --- a/openidm-doc/pom.xml +++ b/openidm-doc/pom.xml @@ -51,6 +51,24 @@ release + + build-man-pages-asciidoc + package + + asciidoc-pre-process + antora + asciidoc-to-pdf + + + + getting-started + install-guide + samples-guide + integrators-guide + connectors-guide + + + OpenIDM diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/appendix-interfaces.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/appendix-interfaces.adoc new file mode 100644 index 000000000..bf7b0897d --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/appendix-interfaces.adoc @@ -0,0 +1,138 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-interfaces] +== OpenICF Interfaces + +This chapter describes all of the interfaces supported by the OpenICF framework, along with notes about their implementation. Specific connectors may support only a subset of these interfaces. + +[#interface-attributeNormalizer] +=== AttributeNormalizer + +Normalize attributes to ensure consistent filtering. + + +[#interface-AuthenticationApiOp] +=== Authenticate + +Provides simple authentication with two parameters, presumed to be a user name and password. If the connector does not implement the AuthenticateOp interface it can not be used in OpenIDM to provide pass-through authentication. + + +[#interface-BatchApiOp] +=== Batch + +Execute a series of operations in a single request. If a resource does not support batch operations, the connector will not implement the batch operation interface. The OpenICF framework will still support batched requests but the operations will be executed iteratively through the connector. + + +[#interface-ConnectorEventSubscriptionApiOp] +=== Connector Event + +Subscribe for notification of any specified event on the target resource. This operation can be used in the context of IoT device reports, to receive notification of events such as low battery signals, inactive devices, and so on. + + +[#interface-CreateApiOp] +=== Create + +Create an object and return its uid. + + +[#interface-DeleteApiOp] +=== Delete + +Delete an object by its uid. + + +[#interface-GetApiOp] +=== Get + +Get an object by its uid. + + +[#interface-PoolableConnector] +=== PoolableConnector + +Use pools of target resources. + + +[#interface-ResolveUsernameApiOp] +=== Resolve Username + +Resolve an object to its uid based on its username. + + +[#interface-SchemaApiOp] +=== Schema + +Describe supported object types, operations, and options. + + +[#interface-ScriptOnConnectorApiOp] +=== Script on Connector + +Allow script execution on connector. + + +[#interface-ScriptOnResourceApiOp] +=== Script On Resource + +Allow script execution on the resource. + + +[#interface-SearchApiOp] +=== Search + +Allow searches for resource objects. + +Connectors that implement __only__ this interface can only be used for reconciliation operations. + + +[#interface-SyncApiOp] +=== Sync + +Poll for synchronization events, which are native changes to target objects. + + +[#interface-SyncEventSubscriptionApiOp] +=== Sync Event + +Subscribe for notification of synchronization events, which are native changes to target objects. + + +[#interface-TestApiOp] +=== Test + +Test the connection configuration, including connecting to the resource. + + +[#interface-UpdateApiOp] +=== Update + +Allows an authorized caller to update (modify or replace) objects on the target resource. + + +[#interface-UpdateAttributeValuesOp] +=== Update Attribute Values + +Allows an authorized caller to update (modify or replace) attribute values on the target resource. This operation is more advanced than the `UpdateOp` operation, and provides better performance and atomicity semantics. + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/appendix-options.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/appendix-options.adoc new file mode 100644 index 000000000..de3fbdcb2 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/appendix-options.adoc @@ -0,0 +1,101 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-options] +== OpenICF Operation Options + +This chapter describes all of the predefined operation options by the OpenICF framework, along with notes about their use. Specific connectors may support only a subset of these options. + +[#operation-option-scope] +=== Scope + +An option to use with Search (in conjunction with link:#operation-option-container[Container]) that specifies how far beneath the specified container to search. Must be one of the following values: + +* SCOPE_OBJECT + +* SCOPE_ONE_LEVEL + +* SCOPE_SUBTREE + + + +[#operation-option-container] +=== Container + +An option to use with Search that specifies the container under which to perform the search. Must be of type QualifiedUid. Should be implemented for those object classes whose ObjectClassInfo.isContainer() returns true. + + +[#operation-option-run-as-user] +=== Run as User + +An option to use with Script on Resource and possibly others that specifies an account under which to execute the script/operation. The specified account will appear to have performed any action that the script/operation performs. + + +[#operation-option-run-with-password] +=== Run with Password + +An option to use with Script on Resource and possibly others that specifies a password under which to execute the script/operation. + + +[#operation-option-attributes-to-get] +=== Attributes to Get + +Determines which attributes to retrieve during Search and Sync. This option overrides the default behavior, which is for the connector to return exactly the set of attributes that are identified as returned by default in the schema for that connector. This option allows a client application to request additional attributes that would not otherwise not be returned (generally because such attributes are more expensive for a connector to fetch and to format) and/or to request only a subset of the attributes that would normally be returned. + + +[#operation-option-paged-results-cookie] +=== Paged Results Cookie + +An option to use with Search that specifies an opaque cookie which is used by the connector to track its position in the set of query results. + + +[#operation-option-paged-results-offset] +=== Paged Results Offset + +An option to use with Search that specifies the index within the result set of the first result which should be returned. + + +[#operation-option-page-size] +=== Page Size + +An option to use with Search that specifies the requested page results page size. + + +[#operation-option-sort-keys] +=== Sort Keys + +An option to use with Search that specifies the sort keys which should be used for ordering the ConnectorObject returned by search request. + + +[#operation-option-fail-on-error] +=== Fail on Error + +This option is used with the Batch operation, to specify whether the batch process should be aborted when the first error is encountered. The default behavior is to continue processing regardless of errors. + + +[#operation-option-require-serial] +=== Require Serial + +This option instructs the connector to execute batched requests in a serial manner if possible. The default behavior of the Batch operation is to execute requests in parallel, for speed and efficiency. In either case the task ID must be reflected in the response for each task, so that tasks can be correctly reordered. + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/appendix-pooling.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/appendix-pooling.adoc new file mode 100644 index 000000000..b79b53dbf --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/appendix-pooling.adoc @@ -0,0 +1,45 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-pooling] +== Connection Pooling Configuration + +Certain connectors support the ability to be pooled. For a pooled connector, OpenICF maintains a pool of connector instances and reuses these instances for multiple provisioning and reconciliation operations. When an operation must be executed, an existing connector instance is taken from the connector pool. If no connector instance exists, a new instance is initialized. When the operation has been executed, the connector instance is released back into the connector pool, ready to be used for a subsequent operation. + +For an unpooled connector, a new connector instance is initialized for every operation. When the operation has been executed, OpenICF disposes of the connector instance. + +Because the initialization of a connector is an expensive operation, reducing the number of connector initializations can substantially improve performance. + +To configure connection pooling, set the following values in the connector configuration file `poolConfigOptions` property: + +* `"maxObjects"` - the maximum number of connector instances in the pool (both idle and active). The default value is `10` instances. + +* `"maxIdle"` - the maximum number of idle connector instances in the pool. The default value is `10` idle instances. + +* `"maxWait"` - the maximum period to wait for a free connector instance to become available before failing. The default period is `150000` milliseconds, or 15 seconds. + +* `"minEvictableIdleTimeMillis"` - the minimum period to wait before evicting an idle connector instance from the pool. The default period is `120000` milliseconds, or 12 seconds. + +* `"minIdle"` - the minimum number of idle connector instances in the pool. The default value is `1` instance. + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-ad.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-ad.adoc new file mode 100644 index 000000000..7faf8fa2e --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-ad.adoc @@ -0,0 +1,305 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-ad] +== Active Directory Connector + +The Active Directory connector is a legacy connector, written in C# for the .NET platform. OpenICF connects to Active Directory over ADSI, the native connection protocol for Active Directory. The connector therefore requires a .NET connector server that has access to the ADSI .dll files. + +The Active Directory connector will be deprecated in a future OpenICF release, and, ultimately, support for its use with OpenIDM will be discontinued. For simple Active Directory (and Active Directory LDS) deployments, the generic LDAP Connector works better than the Active Directory connector, in most circumstances. Using the generic LDAP connector avoids the need to install a remote connector server in the overall deployment. In addition, the generic LDAP connector has significant performance advantages over the Active Directory connector. For more complex Active Directory deployments, use the PowerShell Connector Toolkit, as described in xref:chap-powershell.adoc#chap-powershell["PowerShell Connector Toolkit"]. + +[#ad-connector-config] +=== Configuring the Active Directory Connector + +Before you configure the Active Directory Connector, make sure that the .NET Connector Server is installed, configured and started, and that OpenIDM has been configured to use the Connector Server. For more information, see xref:../integrators-guide/index.adoc["Installing and Configuring a .NET Connector Server"] in the __Integrator's Guide__. + +[#d8013e16273] +.Setting Up the Active Directory Connector +==== + +. Download the Active Directory Connector from link:https://forgerock.org/downloads/[ForgeRock's download page, window=\_blank]. + +. Extract the contents of the AD Connector zip file into the directory in which you installed the Connector Server (by default `c:\Program Files (x86)\Identity Connectors\Connector Server>`). ++ +Note that the files, specifically the connector itself (`ActiveDirectory.Connector.dll`) must be directly under the `path\to\Identity Connectors\Connector Server` directory, and __not__ in a subdirectory. ++ + +[NOTE] +====== +If the account that is used to install the Active Directory connector is different from the account under which the Connector Server runs, you must give the Connector Server runtime account the rights to access the Active Directory connector log files. +====== + +. A sample Active Directory Connector configuration file is provided in `openidm/samples/provisioners/provisioner.openicf-ad.json`. On the OpenIDM host, copy the sample Active Directory connector configuration file to your project's `conf/` directory: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ cp samples/provisioners/provisioner.openicf-ad.json project-dir/conf/ +---- + +. Edit the Active Directory connector configuration to match your Active Directory deployment. ++ +Specifically, check and edit the `configurationProperties` that define the connection details to the Active Directory server. ++ +Also, check that the `bundleVersion` of the connector matches the version of the `ActiveDirectory.Connector.dll` in the Connector Server directory. The bundle version can be a range that includes the version of the connector bundle. To check the .dll version: ++ + +* Right click on the `ActiveDirectory.Connector.dll` file and select Properties. + +* Select the Details tab and note the Product Version. ++ + +image::images/bundle-version.png[] + ++ +The following configuration extract shows sample values for the `connectorRef` and `configurationProperties`: ++ + +[source] +---- +... +"connectorRef" : + { + "connectorHostRef" : "dotnet", + "connectorName" : "Org.IdentityConnectors.ActiveDirectory.ActiveDirectoryConnector", + "bundleName" : "ActiveDirectory.Connector", + "bundleVersion" : "[1.4.0.0,2.0.0.0)" + }, ... +"configurationProperties" : + { + "DirectoryAdminName" : "EXAMPLE\\Administrator", + "DirectoryAdminPassword" : "Passw0rd", + "ObjectClass" : "User", + "Container" : "dc=example,dc=com", + "CreateHomeDirectory" : true, + "LDAPHostName" : "192.0.2.0", + "SearchChildDomains" : false, + "DomainName" : "example", + "SyncGlobalCatalogServer" : null, + "SyncDomainController" : null, + "SearchContext" : "" + }, +---- ++ +The main configurable properties are as follows: ++ +-- + +`connectorHostRef`:: +Must point to an existing connector info provider configuration in `project-dir/conf/provisioner.openicf.connectorinfoprovider.json`. The `connectorHostRef` property is required because the Active Directory connector must be installed on a .NET connector server, which is always __remote__, relative to OpenIDM. + +`DirectoryAdminName` and `DirectoryAdminPassword`:: +Specify the credentials of an administrator account in Active Directory, that the connector will use to bind to the server. ++ +The `DirectoryAdminName` can be specified as a bind DN, or in the format `DomainName\\samaccountname`. + +`SearchChildDomains`:: +Specifies if a Global Catalog (GC) should be used. This parameter is used in search and query operations. A Global Catalog is a read-only, partial copy of the entire forest, and is never used for create, update or delete operations. ++ +Boolean, false by default. + +`LDAPHostName`:: +Specifies a particular Domain Controller (DC) or Global Catalog (GC), using its hostname. This parameter is used for query, create, update, and delete operations. ++ +If `SearchChildDomains` is set to `true`, this specific GC will be used for search and query operations. If the `LDAPHostName` is null (as it is by default), the connector will allow the ADSI libraries to pick up a valid DC or GC each time it needs to perform a query, create, update, or delete operation. + +`SyncGlobalCatalogServer`:: +Specifies a Global Catalog server name for sync operations. This property is used in combination with the `SearchChildDomains` property. ++ +If a value for `SyncGlobalCatalogServer` is set (that is, the value is not `null`) and `SearchChildDomains` is set to `true`, this GC server is used for sync operations. If no value for `SyncGlobalCatalogServer` is set and `SearchChildDomains` is set to `true`, the connector allows the ADSI libraries to pick up a valid GC. + +`SyncDomainController`:: +Specifies a particular DC server for sync operations. If no DC is specified, the connector picks up the first available DC and retains this DC in future sync operations. + +-- ++ +The updated configuration is applied immediately. + +. Check that the connector has been configured correctly by running the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=test" +---- ++ +The command must return `"ok" : true` for the Active Directory connector. + +. The connector is now configured. To verify the configuration, perform a RESTful GET request on the remote system URL, for example: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ActiveDirectory/account?_queryId=query-all-ids" +---- ++ +This request should return the user accounts in the Active Directory server. + +. (Optional) To configure reconciliation or LiveSync between OpenIDM and Active Directory, create a synchronization configuration file (`sync.json`) in your project's `conf/` directory. ++ +The synchronization configuration file defines the attribute mappings and policies that are used during reconciliation. ++ +The following is a simple example of a `sync.json` file for Active Directory: ++ + +[source, console] +---- +{ + "mappings" : [ + { + "name" : "systemADAccounts_managedUser", + "source" : "system/ActiveDirectory/account", + "target" : "managed/user", + "properties" : [ + { "source" : "cn", "target" : "displayName" }, + { "source" : "description", "target" : "description" }, + { "source" : "givenName", "target" : "givenName" }, + { "source" : "mail", "target" : "email" }, + { "source" : "sn", "target" : "familyName" }, + { "source" : "sAMAccountName", "target" : "userName" } + ], + "policies" : [ + { "situation" : "CONFIRMED", "action" : "UPDATE" }, + { "situation" : "FOUND", "action" : "UPDATE" }, + { "situation" : "ABSENT", "action" : "CREATE" }, + { "situation" : "AMBIGUOUS", "action" : "EXCEPTION" }, + { "situation" : "MISSING", "action" : "UNLINK" }, + { "situation" : "SOURCE_MISSING", "action" : "DELETE" }, + { "situation" : "UNQUALIFIED", "action" : "DELETE" }, + { "situation" : "UNASSIGNED", "action" : "DELETE" } + ] + } + ] +} +---- + +. To test the synchronization, run a reconciliation operation as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemADAccounts_managedUser" +---- ++ +If reconciliation is successful, the command returns a reconciliation run ID, similar to the following: ++ + +[source, console] +---- +{"_id":"0629d920-e29f-4650-889f-4423632481ad","state":"ACTIVE"} +---- + +. Query the internal repository, using either a `curl` command, or the OpenIDM Admin UI, to make sure that the users in your Active Directory server were provisioned into the repository. + +==== + + +[#ad-powershell] +=== Using PowerShell Scripts With the Active Directory Connector + +The Active Directory connector supports PowerShell scripting. The following example shows a simple PowerShell script that is referenced in the connector configuration and can be called over the REST interface. + +[NOTE] +==== +External script execution is disabled on system endpoints by default. For testing purposes, you can enable script execution over REST, on system endpoints by adding the `script` action to the system object, in the `access.js` file. For example: + +[source, console] +---- +$ more /path/to/openidm/script/access.js +... +{ + "pattern" : "system/ActiveDirectory", + "roles" : "openidm-admin", + "methods" : "action", + "actions" : "script" +}, +---- +Be aware that scripts passed to clients imply a security risk in production environments. If you need to expose a script for direct external invocation, it might be better to write a custom authorization function to constrain the script ID that is permitted. Alternatively, do not expose the script action for external invocation, and instead, expose a custom endpoint that can make only the desired script calls. For more information about using custom endpoints, see xref:../integrators-guide/chap-scripting.adoc#custom-endpoints["Creating Custom Endpoints to Launch Scripts"] in the __Integrator's Guide__. +==== +The following PowerShell script creates a new MS SQL user with a username that is specified when the script is called. The script sets the user's password to `Passw0rd` and, optionally, gives the user a role. Save this script as `project-dir/script/createUser.ps1`: + +[source, powershell] +---- +if ($loginName -ne $NULL) { + [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null + $sqlSrv = New-Object ('Microsoft.SqlServer.Management.Smo.Server') ('WIN-C2MSQ8G1TCA') + + $login = New-Object -TypeName ('Microsoft.SqlServer.Management.Smo.Login') ($sqlSrv, $loginName) + $login.LoginType = 'SqlLogin' + $login.PasswordExpirationEnabled = $false + $login.Create('Passw0rd') + # The next two lines are optional, and to give the new login a server role, optional + $login.AddToRole('sysadmin') + $login.Alter() + } else { + $Error_Message = [string]"Required variables 'loginName' is missing!" + Write-Error $Error_Message + throw $Error_Message + } +---- +Now edit the Active Directory connector configuration to reference the script. Add the following section to the connector configuration file (`project-dir/conf/provisioner.openicf-ad.json`): + +[source, javascript] +---- +"systemActions" : [ + { + "scriptId" : "ConnectorScriptName", + "actions" : [ + { + "systemType" : ".*ActiveDirectoryConnector", + "actionType" : "Shell", + "actionSource" : "@echo off \r\n echo %loginName%\r\n" + }, + { + "systemType" : ".*ActiveDirectoryConnector", + "actionType" : "PowerShell", + "actionFile" : "script/createUser.ps1" + } + ] + } + ] +---- +To call the PowerShell script over the REST interface, use the following request, specifying the userName as input: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ActiveDirectory/?_action=script&scriptId=ConnectorScriptName&scriptExecuteMode=resource&loginName=myUser" +---- + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-csv.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-csv.adoc new file mode 100644 index 000000000..cff2985d0 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-csv.adoc @@ -0,0 +1,174 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-csv] +== CSV File Connector + +The CSV file connector is useful when importing users, either for initial provisioning or for ongoing updates. When used continuously in production, a CSV file serves as a change log, often containing only user records that have changed. + +[#csv-connector-config] +=== Configuring the CSV File Connector + +A sample CSV file connector configuration is provided in `openidm/samples/provisioners/provisioner.openicf-csv.json`. + +The following example shows an excerpt of the provisioner configuration. The `connectorHostRef` property is optional and must be provided only if the connector runs remotely. + +[source, javascript] +---- +{ + "connectorRef": { + "connectorHostRef": "#LOCAL", + "connectorName": "org.forgerock.openicf.csvfile.CSVFileConnector", + "bundleName": "org.forgerock.openicf.connectors.csvfile-connector", + "bundleVersion": "1.5.1.4" + } +} +---- +The following excerpt shows the __required__ configuration properties: + +[source, javascript] +---- +"configurationProperties" : { + "csvFile" : "&{launcher.project.location}/data/hr.csv", + "headerUid" : "uid" +}, +---- +-- + +`csvFile`:: +The path to the CSV file that is the data source for this connector. + +`headerUid`:: +The CSV header that maps to the `uid` (or name) for each row. + ++ +Default: `uid` + +-- +-- +The CSV file connector also supports following optional configuration properties: + +`encoding`:: +Default: `utf-8` + +`headerPassword`:: +The CSV header that maps to the password for each row. Use this property when password-based authentication is required. + +`fieldDelimiter`:: +The character in the CSV file that is used to separate field values. + ++ +Default: `,` + +`quoteCharacter`:: +The character in the CSV file that is used to encapsulate strings. + ++ +Default: `"` + +`newlineString`:: +The character string in the CSV file that is used to terminate each line. + ++ +Default: `\n` + +`syncFileRetentionCount`:: +The number of historical copies of the CSV file to retain when performing synchronization operations. + ++ +Default: `3` + +-- + + +[#sec-implemented-interfaces-org-forgerock-openicf-csvfile-CSVFileConnector-1_5_1_4] +=== OpenICF Interfaces Implemented by the CSV File Connector + +The CSV File Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-BatchApiOp[Batch]:: +Execute a series of operations in a single request. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-forgerock-openicf-csvfile-CSVFileConnector-1_5_1_4] +=== CSV File Connector Configuration + +The CSV File Connector has the following configurable properties. + +[#configuration-properties-org-forgerock-openicf-csvfile-CSVFileConnector-1_5_1_4] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-database.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-database.adoc new file mode 100644 index 000000000..f8913b506 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-database.adoc @@ -0,0 +1,143 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-database] +== Database Table Connector + +The Database Table connector enables provisioning to a single table in a JDBC database. + +[#database-connector-config] +=== Configuring the Database Table Connector + +A sample connector configuration for the Database Table connector is provided in `samples/provisioners/provisioner.openicf-contractordb.json`. The corresponding data definition language file is provided in `samples/provisioners/provisioner.openicf-contractordb.sql`. + +The following excerpt shows the settings for the connector configuration properties in the sample Database Table connector: + +[source, javascript] +---- +"configurationProperties" : + { + "quoting" : "", + "host" : "localhost", + "port" : "3306", + "user" : "root", + "password" : "", + "database" : "contractordb", + "table" : "people", + "keyColumn" : "UNIQUE_ID", + "passwordColumn" : "", + "jdbcDriver" : "com.mysql.jdbc.Driver", + "jdbcUrlTemplate" : "jdbc:mysql://%h:%p/%d", + "enableEmptyString" : false, + "rethrowAllSQLExceptions" : true, + "nativeTimestamps" : true, + "allNative" : false, + "validConnectionQuery" : null, + "changeLogColumn" : "CHANGE_TIMESTEMP", + "datasource" : "", + "jndiProperties" : null + }, +---- +The mandatory configurable properties are as follows: +-- + +`database`:: +The JDBC database that contains the table to which you are provisioning. + +`table`:: +The name of the table in the JDBC database that contains the user accounts. + +`keyColumn`:: +The column value that is used as the unique identifier for rows in the table. + +-- + + +[#sec-implemented-interfaces-org-identityconnectors-databasetable-DatabaseTableConnector-1_1_0_2] +=== OpenICF Interfaces Implemented by the Database Table Connector + +The Database Table Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-identityconnectors-databasetable-DatabaseTableConnector-1_1_0_2] +=== Database Table Connector Configuration + +The Database Table Connector has the following configurable properties. + +[#configuration-properties-org-identityconnectors-databasetable-DatabaseTableConnector-1_1_0_2] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-google.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-google.adoc new file mode 100644 index 000000000..0754c2d50 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-google.adoc @@ -0,0 +1,75 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-google] +== Google Apps Connector + +The link:https://forgerock.org/downloads/[Enterprise build, window=\_blank] of OpenIDM includes a Google Apps connector, along with a sample connector configuration. The Google Apps Connector enables you to interact with Google's web applications. + +[#google-connector-config] +=== Configuring the Google Apps Connector + +To use this connector, you need a Google Apps account. + +If you have OpenIDM Enterprise, you can view a sample Google Apps connector configuration file in `samples/provisioners/provisioner.openicf-google.json` + +The following is an excerpt of the provisioner configuration file. This example shows an excerpt of the provisioner configuration. The default location of the connector .jar is `openidm/connectors`. Therefore the value of the `connectorHostRef` property must be `"#LOCAL"`: + +[source, javascript] +---- +{ + "connectorHostRef": "#LOCAL", + "connectorName": "org.forgerock.openicf.connectors.googleapps.GoogleAppsConnector", + "bundleName": "org.forgerock.openicf.connectors.googleapps-connector", + "bundleVersion": "[1.4.0.0,2.0.0.0)" +}, +---- +The following excerpt shows the required configuration properties: + +[source, javascript] +---- +"configurationProperties": { + "domain": "", + "clientId": "", + "clientSecret": null, + "refreshToken": null +}, +---- +These configuration properties are fairly straightforward: +-- + +`domain`:: +Set to the domain name for OAuth 2-based authorization. + +`clientId`:: +A client identifier, as issued by the OAuth 2 authorization server. For more information, see the following section of RFC 6749: link:http://tools.ietf.org/html/rfc6749#section-2.2[Client Identifier, window=\_blank]. + +`clientSecret`:: +Sometimes also known as the client password. OAuth 2 authorization servers can support the use of `clientId` and `clientSecret` credentials, as noted in the following section of RFC 6749: link:http://tools.ietf.org/html/rfc6749#section-2.3.1[Client Password, window=\_blank]. + +refreshToken:: +A client can use an OAuth 2 refresh token to continue accessing resources. For more information, see the following section of RFC 6749: link:http://tools.ietf.org/html/rfc6749#section-10.4[Refresh Tokens, window=\_blank]. + +-- +For a sample Google Apps configuration that includes OAuth 2-based entries for `configurationProperties`, see xref:../samples-guide/chap-google-sample.adoc#chap-google-sample["Google Sample - Connecting to Google With the Google Apps Connector"] in the __Samples Guide__. + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-groovy.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-groovy.adoc new file mode 100644 index 000000000..0571baef7 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-groovy.adoc @@ -0,0 +1,129 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-groovy] +== Groovy Connector Toolkit + +OpenICF provides a generic Groovy Connector Toolkit that enables you to run a Groovy script for any OpenICF operation, such as search, update, create, and others, on any external resource. + +The Groovy Connector Toolkit is not a complete connector in the traditional sense. Rather, it is a framework within which you must write your own Groovy scripts to address the requirements of your implementation. Specific scripts are provided within these samples, which demonstrate how the Groovy Connector Toolkit can be used. These scripts cannot be used as is in your deployment, but are a good starting point on which to base your customization. + +[#groovy-connector] +=== Groovy Connector Toolkit + +The Groovy Connector Toolkit is bundled with OpenIDM 4.5, in the JAR `openidm/connectors/groovy-connector-1.4.2.1.jar`. + +Sample implementations are provided in xref:../samples-guide/chap-groovy-samples.adoc#chap-groovy-samples["Samples That Use the Groovy Connector Toolkit to Create Scripted Connectors"] in the __Samples Guide__. + + +[#sec-implemented-interfaces-org-forgerock-openicf-connectors-groovy-ScriptedConnector-1_4_2_1] +=== OpenICF Interfaces Implemented by the Scripted Groovy Connector + +The Scripted Groovy Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-ScriptOnResourceApiOp[Script on Resource]:: +Runs a script on the target resource that is managed by this connector. + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-forgerock-openicf-connectors-groovy-ScriptedConnector-1_4_2_1] +=== Scripted Groovy Connector Configuration + +The Scripted Groovy Connector has the following configurable properties. + +[#operation-script-files-properties-org-forgerock-openicf-connectors-groovy-ScriptedConnector-1_4_2_1] +==== Operation Script Files Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#groovy-engine-configuration-properties-org-forgerock-openicf-connectors-groovy-ScriptedConnector-1_4_2_1] +==== Groovy Engine configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#configuration-properties-org-forgerock-openicf-connectors-groovy-ScriptedConnector-1_4_2_1] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-kerberos.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-kerberos.adoc new file mode 100644 index 000000000..880f8e251 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-kerberos.adoc @@ -0,0 +1,336 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-kerberos] +== Scripted Kerberos Connector + +New in OpenIDM 4.5.0, the scripted Kerberos connector is an implementation of the scripted SSH connector, and is based on Java Secure Channel (JSch) and the Java implementation of the Expect library (Expect4j). The connector depends on the following files, provided with OpenIDM: + +* `/path/to/openidm/lib/ssh-connector-1.4.0.0.jar` + +* `/path/to/openidm/lib/expect4j-.jar` + +* `/path/to/openidm/lib/jsch-.jar` + +The Kerberos connector enables you to manage Kerberos user principals from OpenIDM. The connector is provided in `/path/to/openidm/connectors/kerberos-connector-1.4.0.0.jar` and bundles a number of Groovy scripts to interact with a Kerberos admin server. Users of the Kerberos connector are not expected to edit the bundled Groovy scripts. The bundled scripts use the `kadmin` utility to communicate with the Kerberos server. + +The Kerberos connector enables you to perform the following operations on Kerberos user principals. + +* List the existing principals + +* Display the details of a principal + +* Add a user principal + +* Change the password of a user principal and unlock the principal + +* Delete a user principal + + +[#ssh-kerberos-schema] +=== Kerberos Connector Schema + +The Kerberos connector can only be used to manage the Kerberos `principal` object type (which maps to the OpenICF `__ACCOUNT__` object). The following attributes are supported in the schema: + +* `principal` - (maps to `__NAME__` and `__UID__`) + +* `__PASSWORD__` - updatable, required when an object is created + +* `__LOCK_OUT__` - updatable only; unlock an account by setting this attribute to `false` + +* `policy` - the password policy used by the principal + +* `expirationDate` - the date that the user principal expires + +* `passwordExpiration` - the date that the password expires + +* `maximumTicketLife` - the maximum ticket life for the principal. At the end of the ticket lifetime, the ticket can no longer be used. However, if the renewable lifetime (`maximumRenewableLife`) is longer than the ticket lifetime, the ticket holder can present the ticket to the KDC and request a new ticket. + +* `maximumRenewableLife` - the period during which the ticket can be renewed. A renewed ticket usually has a new ticket lifetime, dating from the time that it was renewed, that is constrained by the renewable ticket lifetime. + +In addition, the following read-only attributes are supported: + +* `lastPasswordChange` + +* `lastModified` + +* `lastSuccessfulAuthentication` + +* `lastFailedAuthentication` + +* `failedPasswordAttempts` + + + +[#ssh-kerberos-config] +=== Configuring the Kerberos Connector + +OpenIDM provides a sample connector configuration (`provisioner.openicf-kerberos.json`) in the `/path/to/openidm/samples/kerberos/conf/` directory. You can copy the sample connector configuration to your project's `conf/` directory, and adjust it to match your Kerberos environment. + +Set the authentication properties, as described in xref:chap-ssh.adoc#ssh-authentication["Configuring Authentication to the SSH Server"]. In addition, set at least the following properties: +-- + +[#customConfiguration] +`customConfiguration`:: +Specify the details of the user principal and the default realm here. The sample provisioner file has the following custom configuration: ++ + +[source, javascript] +---- +"customConfiguration" : "kadmin{ + cmd = '/usr/sbin/kadmin.local'; + user = ''; + default_realm = '' +}", +---- ++ +A complete custom configuration will look something like this: ++ + +[source, javascript] +---- +"customConfiguration" : "kadmin { + cmd = '/usr/sbin/kadmin.local'; + user = 'openidm/admin'; + default_realm = 'EXAMPLE.COM' }", +---- + +[#customSensitiveConfiguration] +`customSensitiveConfiguration`:: +Set the password for the user principal here. The sample provisioner has the following configuration: ++ + +[source, javascript] +---- +"customSensitiveConfiguration" : "kadmin { password = ''}", +---- ++ +Change this to reflect your user principal password, for example: ++ + +[source, javascript] +---- +"customSensitiveConfiguration" : "kadmin { password = 'Passw0rd'}" +---- + +-- +The following section describes the configuration parameters in the sample Kerberos connector configuration. For a complete list of the configuration properties for the Kerberos connector, see xref:#configuration-properties-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0["Configuration Properties"]: +-- + +`host`:: +The host name or IP address of the SSH server on which the `kadmin` command is run. + +`port`:: +The port number on which the SSH server listens. + ++ +Default: `22` (the default SSH port) + +`user`:: +The username of the account that is used to connect to the SSH server. ++ + +[NOTE] +====== +This is __not__ the same as your Kerberos user principal. This account must be able to `ssh` into the server on which Kerberos is running, with the password provided in the next parameter. +====== + +`password`:: +The password of the account that is used to connect to the SSH server. + +`prompt`:: +A string representing the remote SSH session prompt. This must be the exact prompt string, in the format `username@target:`, for example `root@localhost:~$`. + ++ +If the prompt includes a trailing space, you must include the space in the value of this property. + ++ +Consider customizing your Linux prompt with the `PS1` and `PS2` variables, to set a __safe__ prompt. For information about customizing promtps, see link:https://help.ubuntu.com/community/CustomizingBashPrompt[this article, window=\_blank]. + +`sudoCommand`:: +A string that shows the full path to the `sudo` command, for example `/usr/bin/sudo`. + +`echoOff`:: +If set to `true` (the default), the input command echo is disabled. If set to `false`, every character that is sent to the server is sent back to the client in the `expect()` call. + +`terminalType`:: +Sets the terminal type to use for the session. The list of supported types is determined by your Linux/UNIX system. For more information, see the `terminfo` manual page (`$ man terminfo`). + ++ +Default: `vt102` + +`setLocale`:: +If set to `true`, indicates that the default environment locale should be changed to the value of the `locale` property. + ++ +Default: `false` + +locale:: +Sets the locale for LC_ALL, LANG and LANGUAGE environment variables, if `setLocale` is set to `true`. + ++ +Default: `en_US.utf8` + +`connectionTimeout`:: +Specifies the connection timeout to the remote server, in milliseconds. + ++ +Default: `5000` + +`expectTimeout`:: +Specifies the timeout used by the `expect()` calls in scripts, in milliseconds. + ++ +Default: `5000` + +`authenticationType`:: +Sets the authentication type, either `PASSWORD` or `PUBKEY`. For more information, see xref:chap-ssh.adoc#ssh-authentication["Configuring Authentication to the SSH Server"]. + ++ +Default: `PASSWORD` + +`throwOperationTimeoutException`:: +If `true`, the connector throws an exception when the timeout is reached for an operation. Otherwise, the operation fails silently. + ++ +Default: `true` + +`scriptRoots`:: +The path to the Groovy scripts that will perform the OpenICF operations, relative to your OpenIDM installation directory. For the Kerberos connector, the scripts are bundled up in the connector JAR file, so this path is set to `\jar:file:connectors/kerberos-connector-1.4.0.0.jar!/script/kerberos/` in the sample connector configuration. + +`classpath`:: +The directory in which the compiler should look for compiled classes. The default classpath, if not is specified, is `install-dir/lib`. + +`reloadScriptOnExecution`:: +By default, scripts are loaded and compiled when a connector instance is created and initialized. Setting `reloadScriptOnExecution` to true makes the connector load and compile the script every time it is called. Do not set this property to `true` in a production environment, because it will have a significant impact on performance. + ++ +Default: `false` + +`*ScriptFileName`:: +The script that is used for each OpenICF operation. Do not change these script names in the bundled Kerberos connector. + +-- + + +[#sec-implemented-interfaces-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0] +=== OpenICF Interfaces Implemented by the Kerberos Connector + +The Kerberos Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-ScriptOnResourceApiOp[Script on Resource]:: +Runs a script on the target resource that is managed by this connector. + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0] +=== Kerberos Connector Configuration + +The Kerberos Connector has the following configurable properties. + +[#configuration-properties-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#operation-script-files-properties-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0] +==== Operation Script Files Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#groovy-engine-configuration-properties-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0] +==== Groovy Engine configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#basic-configuration-properties-properties-org-forgerock-openicf-connectors-kerberos-KerberosConnector-1_4_0_0] +==== Basic Configuration Properties Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-ldap.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-ldap.adoc new file mode 100644 index 000000000..02dbb09db --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-ldap.adoc @@ -0,0 +1,650 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-ldap] +== Generic LDAP Connector + +The generic LDAP connector is based on JNDI, and can be used to connect to any LDAPv3-compliant directory server, such as OpenDJ, Active Directory, SunDS, Oracle Directory Server Enterprise Edition, IBM Security Directory Server, and OpenLDAP. + +OpenICF provides a legacy Active Directory (AD) .NET connector. Note, however, that the AD Connector will be deprecated in a future OpenICF release, and, ultimately, support for its use with OpenIDM will be discontinued. For simple Active Directory (and Active Directory LDS) deployments, the generic LDAP Connector works better than the Active Directory connector, in most circumstances. Using the generic LDAP connector avoids the need to install a remote connector server in the overall deployment. In addition, the generic LDAP connector has significant performance advantages over the Active Directory connector. For more complex Active Directory deployments, use the PowerShell Connector Toolkit, as described in xref:chap-powershell.adoc#chap-powershell["PowerShell Connector Toolkit"]. + +[#ldap-connector-config] +=== Setting Up the Generic LDAP Connector + +OpenIDM 4.5 bundles version 1.4.1.2 of the LDAP connector. Three sample LDAP connector configurations are provided in the `path/to/openidm/samples/provisioners/` directory: + +* `provisioner.openicf-opendjldap.json` provides a sample LDAP connector configuration for an OpenDJ directory server. + +* `provisioner.openicf-adldap.json` provides a sample LDAP connector configuration for an Active Directory server. + +* `provisioner.openicf-adldsldap.json` provides a sample LDAP connector configuration for an Active Directory Lightweight Directory Services (AD LDS) server. + +You should be able to adapt one of these sample configurations for any LDAPv3-compliant server. + +The `connectorRef` configuration property provides information about the LDAP connector bundle, and is the same in all three sample LDAP connector configurations: + +[source, javascript] +---- +{ + "connectorRef": { + "connectorHostRef": "#LOCAL", + "connectorName": "org.identityconnectors.ldap.LdapConnector", + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "bundleVersion": "[1.4.0.0,2.0.0.0)" + } + } +---- +The `connectorHostRef` property is optional, if you use the connector .jar provided in `openidm/connectors`, and you use a local connector server. + +The following excerpt shows the configuration properties in the sample LDAP connector for OpenDJ. These properties are described in detail later in this section. For additional information on the properties that affect synchronization, see xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]. For a complete list of the configuration properties for the LDAP connector, see xref:#sec-config-properties-org-identityconnectors-ldap-LdapConnector-1_4_1_2["LDAP Connector Configuration"]: + +[source, javascript] +---- +"configurationProperties" : { + "host" : "localhost", + "port" : 1389, + "ssl" : false, + "startTLS" : false, + "principal" : "cn=Directory Manager", + "credentials" : "password", + "baseContexts" : [ + "dc=example,dc=com" + ], + "baseContextsToSynchronize" : [ + "dc=example,dc=com" + ], + "accountSearchFilter" : null, + "accountSynchronizationFilter" : null, + "groupSearchFilter" : null, + "groupSynchronizationFilter" : null, + "passwordAttributeToSynchronize" : null, + "synchronizePasswords" : false, + "removeLogEntryObjectClassFromFilter" : true, + "modifiersNamesToFilterOut" : [ ], + "passwordDecryptionKey" : null, + "changeLogBlockSize" : 100, + "attributesToSynchronize" : [ ], + "changeNumberAttribute" : "changeNumber", + "passwordDecryptionInitializationVector" : null, + "filterWithOrInsteadOfAnd" : false, + "objectClassesToSynchronize" : [ + "inetOrgPerson" + ], + "vlvSortAttribute" : "uid", + "passwordAttribute" : "userPassword", + "useBlocks" : false, + "maintainPosixGroupMembership" : false, + "failover" : [ ], + "readSchema" : true, + "accountObjectClasses" : [ + "top", + "person", + "organizationalPerson", + "inetOrgPerson" + ], + "accountUserNameAttributes" : [ + "uid" + ], + "groupMemberAttribute" : "uniqueMember", + "passwordHashAlgorithm" : null, + "usePagedResultControl" : true, + "blockSize" : 100, + "uidAttribute" : "dn", + "maintainLdapGroupMembership" : false, + "respectResourcePasswordPolicyChangeAfterReset" : false +}, +---- +-- + +`host`:: +The host name or IP address of the server on which the LDAP instance is running. + +`port`:: +The port on which the LDAP server listens for LDAP requests. The sample configuration specifies a default port of 1389. + +`ssl`:: +If `true`, the specified port listens for LDAPS connections. + ++ +If you use the LDAP connector over SSL, set the `ssl` property to `true`, and the `port` to `636` in the connector configuration file. You must also specify the path to a truststore in your project's `conf/system.properties` file. A truststore is provided by default at `openidm/security/truststore`. Add the following line to the `system.properties` file, substituting the path to your own truststore if you do not want to use the default: ++ + +[source] +---- +# Set the truststore +javax.net.ssl.trustStore=/path/to/openidm/security/truststore +---- + +`startTLS`:: +Specifies whether to use the startTLS operation to initiate a TLS/SSL session. To use startTLS, set `"startTLS":true,` and `"ssl":false`. Your connection should use the insecure LDAP port (typically `389` or `1389` for an OpenDJ server). + +`principal`:: +The bind DN that is used to connect to the LDAP server. + +`credentials`:: +The password of the `principal` that is used to connect to the LDAP server. + +`baseContexts`:: +One or more starting points in the LDAP tree that will be used when searching the tree. Searches are performed when discovering users from the LDAP server or when looking for the groups of which a user is a member. During reconciliation operations, OpenIDM searches through the base contexts listed in this property for changes. (See also xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]). + +`baseContextsToSynchronize`:: +One or more starting points in the LDAP tree that will be used to determine if a change should be synchronized. During LiveSync operations, OpenIDM searches through the base contexts listed in this property for changes. If no value is specified here, the values in listed in the `baseContexts` property are used. (See also xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]). + +`accountSynchronizationFilter`:: +Used during synchronization actions to filter out LDAP accounts. (See also xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]). + +`accountObjectClasses`:: +This property lists all the object classes that represent an account. If this property has multiple values, an `OR` filter is used to determine the affected entries. For example, if the value of this property is `["organizationalPerson", "inetOrgPerson"]`, any entry with the object class `organizationalPerson` OR the object class `inetOrgPerson` is considered as an account entry. The value of this property must not include the `top` object class. + +`accountSearchFilter`:: +Search filter that user accounts must match. (See also xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]). + +`accountUserNameAttributes`:: +Attributes holding the account's user name. Used during authentication to find the LDAP entry matching the user name. + +`attributesToSynchronize`:: +List of attributes used during object synchronization. OpenIDM ignores change log updates that do not include any of the specified attributes. If empty, OpenIDM considers all changes. (See also xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]). + +`blockSize`:: +Block size for simple paged results and VLV index searches, reflecting the maximum number of entries retrieved at any one time. + +`changeLogBlockSize`:: +Block size used when fetching change log entries. + +`changeNumberAttribute`:: +Change log attribute containing the last change number. + +`failover`:: +LDAP URLs specifying alternative LDAP servers to connect to if OpenIDM cannot connect to the primary LDAP server specified in the `host` and `port` properties. + +`filterWithOrInsteadOfAnd`:: +In most cases, the filter to fetch change log entries is AND-based. If this property is set, the filter ORs the required change numbers instead. + +`groupMemberAttribute`:: +LDAP attribute holding members for non-POSIX static groups. + +`groupSearchFilter`:: +Search filter that group entries must match. + +`maintainLdapGroupMembership`:: +If `true`, OpenIDM modifies group membership when entries are renamed or deleted. + ++ +In the sample LDAP connector configuration file provided with OpenIDM, this property is set to `false`. This means that LDAP group membership is not modified when entries are renamed or deleted in OpenIDM. To ensure that entries are removed from LDAP groups when the entries are deleted, set this property to `true` or enable referential integrity on the LDAP server. For information about configuring referential integrity in OpenDJ, see link:../../../opendj/3.5/server-dev-guide/#referential-integrity[Configuring Referential Integrity, window=\_top] in the __OpenDJ Administration Guide__. + +`maintainPosixGroupMembership`:: +If `true`, OpenIDM modifies POSIX group membership when entries are renamed or deleted. + +`modifiersNamesToFilterOut`:: +Use this property to avoid loops caused by changes made to managed user objects being synchronized. For more information, see xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]. + +`objectClassesToSynchronize`:: +OpenIDM synchronizes only entries that have these object classes. See also xref:#ldap-connector-sync-controls["Controlling What the LDAP Connector Synchronizes"]. + +`passwordAttribute`:: +Attribute to which OpenIDM writes the predefined `PASSWORD` attribute. + +`passwordAttributeToSynchronize`:: +OpenIDM synchronizes password values on this attribute. + +`passwordDecryptionInitializationVector`:: +This is a legacy attribute, and its value should remain set to `null`. To configure password synchronization between an LDAP server and OpenIDM, use one of the password synchronization plugins, described in xref:../integrators-guide/chap-passwords.adoc#password-sync["Synchronizing Passwords Between OpenIDM and an LDAP Server"] in the __Integrator's Guide__. + +`passwordDecryptionKey`:: +This is a legacy attribute, and its value should remain set to `null`. To configure password synchronization between an LDAP server and OpenIDM, use one of the password synchronization plugins, described in xref:../integrators-guide/chap-passwords.adoc#password-sync["Synchronizing Passwords Between OpenIDM and an LDAP Server"] in the __Integrator's Guide__. + +`passwordHashAlgorithm`:: +Hash password values with the specified algorithm, if the LDAP server stores them in clear text. ++ +The hash algorithm can be one of the following: + +* `NONE` - Clear text + +* `WIN-AD` - Used for password changes to Active Directory + +* `SHA` - Secure Hash Algorithm + +* `SHA-1` - A 160-bit hash algorithm that resembles the MD5 algorithm + +* `SSHA` - Salted SHA + +* `MD5` - A 128-bit message-digest algorithm + +* `SMD5` - Salted MD5 + + +`readSchema`:: +If `true`, read the schema from the LDAP server. + ++ +This property is used only during the connector setup, to generate the object types. + ++ +If this property is `false`, the LDAP connector provides a basic default schema that can manage LDAP users and groups. The default schema maps `inetOrgPerson` to the OpenICF `__ACCOUNT__` property, and `groupOfUniqueNames` to the OpenICF `__GROUP__` property. The following LDAP object classes are also included in the default schema: ++ +[none] +* `organization` +* `organizationalUnit` +* `person` +* `organizationalPerson` +* `account` +* `groupOfNames` + +`removeLogEntryObjectClassFromFilter`:: +If `true`, the filter to fetch change log entries does not contain the `changeLogEntry` object class, and OpenIDM expects no entries with other object types in the change log. The default setting is `true`. + +`respectResourcePasswordPolicyChangeAfterReset`:: +If `true`, bind with the Password Expired and Password Policy controls, and throw `PasswordExpiredException` and other exceptions appropriately. + +`synchronizePasswords`:: +This is a legacy attribute, and its value should remain set to `false`. To configure password synchronization between an LDAP server and OpenIDM, use one of the password synchronization plugins, described in xref:../integrators-guide/chap-passwords.adoc#password-sync["Synchronizing Passwords Between OpenIDM and an LDAP Server"] in the __Integrator's Guide__. + +`uidAttribute`:: +Specifies the LDAP attribute that should be used as the immutable ID (`_UID_`) for the entry. For an OpenDJ resource, you should use the `entryUUID`. You can use the `DN` as the UID attribute but note that this is __not__ immutable. + +`useBlocks`:: +If `useBlocks` is `false`, no pagination is used. If `useBlocks` is `true`, the connector uses block-based LDAP controls, either the simple paged results control, or the virtual list view control, depending on the setting of the `usePagedResultControl` property. + +`usePagedResultControl`:: +Taken into account only if `useBlocks` is `true`. If `usePagedResultControl` is `false`, the connector uses the virtual list view (VLV) control, if it is available. If `usePagedResultControl` is `true`, the connector uses the simple paged results control for search operations. + +`useTimestampsForSync`:: +If `true`, use timestamps for LiveSync operations, instead of the change log. + ++ +By default, the LDAP connector has a change log strategy for LDAP servers that support a change log (such as OpenDJ and Oracle Directory Server Enterprise Edition). If the LDAP server does not support a change log, or if the change log is disabled, LiveSync for create and modify operations can still occur, based on the timestamps of modifications. + +`vlvSortAttribute`:: +Attribute used as the sort key for virtual list view. + +-- + + +[#ldap-connector-sync-controls] +=== Controlling What the LDAP Connector Synchronizes + +To control the set of LDAP entries that are affected by reconciliation and automatic synchronization operations, set the following properties in the provisioner configuration. Automatic synchronization operations includes LiveSync (synchronization of changes from the LDAP server to OpenIDM) and implicit sync (synchronization from the OpenIDM repository to the LDAP server). +-- + +`baseContexts`:: +The starting points in the LDAP tree that are used when searching the directory tree, for example, `dc=example,dc=com`. These base contexts must include the set of users __and the set of groups__ that must be searched during reconciliation operations. + +`baseContextsToSynchronize`:: +The starting points in the LDAP tree that are used to determine if a change should be synchronized. This property is used only for automatic synchronization operations. Only entries that fall under these base contexts are considered during synchronization operations. + +`accountSearchFilter`:: +Only user accounts that match this filter are searched, and therefore affected by reconciliation and synchronization operations. If you do not set this property, all accounts within the base contexts specified previously are searched. + +`accountSynchronizationFilter`:: +This property is used during reconciliation and automatic synchronization operations, and filters out any LDAP accounts that you specifically want to exclude from these operations. + +`objectClassesToSynchronize`:: +During automatic synchronization operations, only the object classes listed here are considered for changes. OpenIDM ignores change log updates (or changes to managed objects) which do not have any of the object classes listed here. If this property is not set, OpenIDM considers changes to all attributes specified in the mapping. + +`attributesToSynchronize`:: +During automatic synchronization operations, __only__ the attributes listed here are considered for changes. Objects that include these attributes are synchronized. Objects that do not include these attributes are ignored. If this property is not set, OpenIDM considers changes to all attributes specified in the mapping. Automatic synchronization includes LiveSync and implicit synchronization operations. For more information, see xref:../integrators-guide/chap-synchronization.adoc#sync-types["Types of Synchronization"] in the __Integrator's Guide__ + ++ +This attribute works only with LDAP servers that log changes in a change log, not with servers (such as Active Directory) that use other mechanisms to track changes. + +`modifiersNamesToFilterOut`:: +This property enables you to define a list of DNs. During synchronization operations, the connector ignores changes made by these DNs. + ++ +When a managed user object is updated, and that change is synchronized to the LDAP server, the change made on the LDAP server is recorded in the change log. A LiveSync operation picks up the change, and attempts to replay the change on the managed user object, effectively resulting in a loop of updates. + ++ +To avoid this situation, you can specify a unique user in your LDAP directory, that will be used __only__ for the LDAP connector. The unique user must be something other than `cn=directory manager`, for example `cn=openidmuser`. You can then include that user DN as the value of `modifiersNamesToFilterOut`. When a change is made through the LDAP connector, and that change is recorded in the change log, the modifier's name (`cn=openidmuser`) is flagged and OpenIDM does not attempt to replay the change back to the managed user repository. So you are effectively indicating that OpenIDM should not synchronized changes back to managed user that originated from managed user, thus preventing the update loop. + ++ +This attribute works only with LDAP servers that log changes in a change log, not with servers (such as Active Directory) that use other mechanisms to track changes. + +-- + + +[#ldap-connector-with-ad] +=== Using the Generic LDAP Connector With Active Directory + +The LDAP connector provides new functionality for managing Active Directory users and groups. Among other changes, the new connector can handle the following operational attributes to manage Active Directory accounts: + +* `ENABLE` - uses the `userAccountControl` attribute to get or set the account status of an object. ++ +The LDAP connector reads the `userAccountControl` to determine if an account is enabled or disabled. The connector modifies the value of the `userAccountControl` attribute if OpenIDM changes the value of `__ENABLE__`. + +* `__ACCOUNT_EXPIRES__` - gets or sets the `accountExpires` attribute of an Active Directory object. + +* `__LOCK_OUT__` - uses the `msDS-User-Account-Control-Computed` system attribute to check if a user account has been locked. ++ +If OpenIDM sets the `__LOCK_OUT__` to `FALSE`, the LDAP connector sets the Active Directory `lockoutTime` to `0` to unlock the account. ++ +If OpenIDM sets the `__LOCK_OUT__` to `TRUE`, the LDAP connector ignores the change and logs a message. + +* `__PASSWORD_EXPIRED__` - uses the `msDS-User-Account-Control-Computed` system attribute to check if a user password has expired. ++ +To force password expiration (to force a user to change their password when they next log in), `pwdLastSet` must be set to `0`. The LDAP connector sets `pwdLastSet` to `0`, if OpenIDM sets `__PASSWORD_EXPIRED__` to `TRUE`. ++ +To remove password expiration, `pwdLastSet` must be set to `0` and then `-1`. This sets the value of `pwdLastSet` to the current time. The LDAP connector sets `pwdLastSet` to `-1` if OpenIDM sets `__PASSWORD_EXPIRED__` to `FALSE`. + + +[NOTE] +==== +You must update your provisioner configuration to be able to use these new operational attributes. You can use this link:../resources/provisioner.openicf-adldap.json[sample provisioner configuration, window=\_blank] as a guide. +==== + +[#ldap-connector-ad-users] +==== Managing Active Directory Users With the LDAP Connector + +If you create or update users in Active Directory, and those user entries include passwords, you __must__ use the LDAP connector over SSL. You cannot create or update an Active Directory user password in clear text. To use the connector over SSL, set `"ssl" : true` in the provisioner configuration and set the path to your truststore in your project's `conf/system.properties` file. For example, add the following line to that file: + +[source, javascript] +---- +# Set the truststore +javax.net.ssl.trustStore=/path/to/openidm/security/truststore +---- +The following command adds an Active Directory user. The output shows the operational attributes described in the previous section: + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "dn": "CN=Brian Smith,CN=Users,DC=example,DC=com", + "cn": "Brian Smith", + "sAMAccountName": "bsmith", + "userPrincipalName": "bsmith@example.com", + "userAccountControl": "512", + "givenName": "Brian", + "mail": "bsmith@example.com", + "__PASSWORD__": "Passw0rd" + }' \ + http://localhost:8080/openidm/system/ad/account?_action=create +{ + "_id": "", + "mobile": null, + "postalCode": null, + "st": null, + "employeeType": null, + "objectGUID": "", + "cn": "Brian Smith", + "department": null, + "l": null, + "description": null, + "info": null, + "manager": null, + "sAMAccountName": "bsmith", + "sn": null, + "whenChanged": "20151217131254.0Z", + "userPrincipalName": "bsmith@example.com", + "userAccountControl": "512", + "__ENABLE__": true, + "displayName": null, + "givenName": "Brian", + "middleName": null, + "facsimileTelephoneNumber": null, + "lastLogon": "0", + "countryCode": "0", + "employeeID": null, + "co": null, + "physicalDeliveryOfficeName": null, + "pwdLastSet": "2015-12-17T13:12:54Z", + "streetAddress": null, + "homePhone": null, + "__PASSWORD_NOTREQD__": false, + "telephoneNumber": null, + "dn": "CN=Brian Smith,CN=Users,DC=example,DC=com", + "title": null, + "mail": "bsmith@example.com", + "postOfficeBox": null, + "__SMARTCARD_REQUIRED__": false, + "uSNChanged": "86144", + "__PASSWORD_EXPIRED__": false, + "initials": null, + "__LOCK_OUT__": false, + "company": null, + "employeeNumber": null, + "accountExpires": "0", + "c": null, + "whenCreated": "20151217131254.0Z", + "uSNCreated": "86142", + "division": null, + "groups": [], + "__DONT_EXPIRE_PASSWORD__": false, + "otherHomePhone": [] +} +---- +Note that the command sets the `userAccountControl` to `512`, which is an `enabled` account. The value of the `userAccountControl` determines the account policy. The following list describes the common values for the `userAccountControl`. +-- + +`512`:: +Enabled account. + +`514`:: +Disabled account. + +`544`:: +Enabled account, password not required. + +`546`:: +Disabled account, password not required. + +`66048`:: +Enabled account, password does not expire. + +`66050`:: +Disabled account, password does not expire. + +`66080`:: +Enabled account, password does not expire and is not required. + +`66082`:: +Disabled account, password does not expire and is not required. + +`262656`:: +Enabled account, smartcard required. + +`262658`:: +Disabled account, smartcard required. + +`262688`:: +Enabled account, smartcard required, password not required. + +`262690`:: +Disabled account, smartcard required, password not required. + +`328192`:: +Enabled account, smartcard required, password does not expire. + +`328192`:: +Enabled account, smartcard required, password does not expire. + +`328194`:: +Disabled account, smartcard required, password does not expire. + +`328224`:: +Enabled account, smartcard required, password does not expire and is not required. + +`328226`:: +Disabled account, smartcard required, password does not expire and is not required. + +-- + + +[#ldap-connector-ad-groups] +==== Managing Active Directory Groups With the LDAP Connector + +The following command creates a basic Active Directory group with the LDAP connector: + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "dn": "CN=Employees,DC=example,DC=com" + }' \ + http://localhost:8080/openidm/system/ad/group?_action=create +{ + "_id": "" +} +---- +The LDAP connector exposes two special attributes to handle Active Directory group scope and type: `GROUP_SCOPE` and `GROUP_TYPE`. + +The `GROUP_SCOPE` attribute is defined in the provisioner configuration as follows: + +[source, javascript] +---- +... + "__GROUP_SCOPE__" : { + "type" : "string", + "nativeName" : "__GROUP_SCOPE__", + "nativeType" : "string" + }, +---- +The value of the `GROUP_SCOPE` attribute can be `global`, `domain`, or `universal`. If no group scope is set when the group is created, the scope is `global` by default. For more information about the different group scopes, see the corresponding link:https://technet.microsoft.com/en-us/library/cc755692(v=ws.10).aspx[Microsoft documentation, window=\_top]. + +The `GROUP_TYPE` attribute is defined in the provisioner configuration as follows: + +[source, javascript] +---- +... +"__GROUP_TYPE__" : { + "type" : "string", + "nativeName" : "__GROUP_TYPE__", + "nativeType" : "string" + }, +---- +The value of the `GROUP_TYPE` attribute can be `security` or `distribution`. If no group type is set when the group is created, the type is `security` by default. For more information about the different group types, see the corresponding link:https://technet.microsoft.com/en-us/library/cc781446(v=ws.10).aspx[Microsoft documentation, window=\_top]. + +The following example creates a new distribution group, with universal scope: + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "dn": "CN=NewGroup,DC=example,DC=com", + "__GROUP_SCOPE__": "universal", + "__GROUP_TYPE__": "distribution" + }' \ + http://localhost:8080/openidm/system/ad/group?_action=create +{ + "_id": "" +} +---- + + +[#ad-dates] +==== Handling Active Directory Dates + +Most dates in Active Directory are represented as the number of 100-nanosecond intervals since January 1, 1601 (UTC). For example: + +[source] +---- +pwdLastSet: 130698687542272930 +---- +OpenIDM generally represents dates as an ISO 8601-compliant string with `yyyy-MM-dd'T'HH:mm:ssZ` format. For example: + +[source] +---- +2015-03-02T20:17:48Z +---- +The generic LDAP connector therefore converts any dates from Active Directory to ISO 8601 format, for fields such as `pwdLastSet`, `accountExpires`, `lockoutTime`, and `lastLogon`. + + + +[#sec-implemented-interfaces-org-identityconnectors-ldap-LdapConnector-1_4_1_2] +=== OpenICF Interfaces Implemented by the LDAP Connector + +The LDAP Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-identityconnectors-ldap-LdapConnector-1_4_1_2] +=== LDAP Connector Configuration + +The LDAP Connector has the following configurable properties. + +[#configuration-properties-org-identityconnectors-ldap-LdapConnector-1_4_1_2] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-overview.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-overview.adoc new file mode 100644 index 000000000..08b3ebe6f --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-overview.adoc @@ -0,0 +1,103 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-overview] +== Overview of the Connectors Supported With OpenIDM 4.5 + +This chapter provides a high-level overview of the connectors supported with OpenIDM 4.5. Additional OpenICF connectors, not supported with OpenIDM, are available on the OpenICF download site, but are not described in this guide. + +For instructions on building connector configurations interactively, see xref:../integrators-guide/chap-resource-conf.adoc#connector-wiz["Creating Default Connector Configurations"] in the __Integrator's Guide__. + +[WARNING] +==== +This guide is still a work in progress and does not yet list all the connectors that are supported with OpenIDM. +==== + +[#connectors-provided-with-openidm] +=== Overview of the Connectors Supported With OpenIDM + +-- +OpenIDM provides a number of samples in the `openidm/samples` directory. This section describes the purpose of each sample, and corresponds to the list of samples described in the README, in the `openidm/samples` directory. + +Generic LDAP Connector:: +The generic LDAP connector is based on JNDI, and can be used to connect to any LDAPv3-compliant directory server, such as OpenDJ, Active Directory, SunDS, Oracle Directory Server Enterprise Edition, IBM Security Directory Server, and OpenLDAP. + ++ +For information about installing and configuring the LDAP connector, see xref:chap-ldap.adoc#chap-ldap["Generic LDAP Connector"]. + +CSV File Connector:: +The CSV file connector is useful when importing users, either for initial provisioning or for ongoing updates. When used continuously in production, a CSV file serves as a change log, often containing only user records that have changed. + ++ +For information about installing and configuring the CSV file connector, see xref:chap-csv.adoc#chap-csv["CSV File Connector"]. + +Database Table Connector:: +The Database Table connector enables provisioning to a single table in a JDBC database. + ++ +For information about installing and configuring the Database Table connector, see xref:chap-database.adoc#chap-database["Database Table Connector"]. + +PowerShell Connector:: +The scripted PowerShell Connector toolkit allows you to create a connector customized to communicate with Microsoft systems such as Azure AD and Active Directory. + ++ +For information about installing and configuring the PowerShell connector, see xref:chap-powershell.adoc#chap-powershell["PowerShell Connector Toolkit"]. + +Groovy Connector:: +The scripted Groovy Connector toolkit enables you to run a Groovy script for any OpenICF operation, such as search, update, create, and others, on any external resource. + ++ +For information about installing and configuring the Groovy connector, see xref:chap-groovy.adoc#chap-groovy["Groovy Connector Toolkit"]. + +Scripted SAP Connector:: +The scripted SAP connector is an implementation of the Scripted Groovy Connector Toolkit that connects to any SAP system using the SAP JCo Java libraries. + ++ +For information about installing and configuring the SAP connector, see xref:chap-sap.adoc#chap-sap["Scripted SAP Connector"]. + +Google Apps Connector:: +The Google Apps connector enables you to interact with Google's web applications. + ++ +For information about installing and configuring the Google Apps connector, see xref:chap-google.adoc#chap-google["Google Apps Connector"]. + +Salesforce Connector:: +The Salesforce connector enables provisioning, reconciliation, and synchronization between Salesforce and the OpenIDM repository. + ++ +For information about installing and configuring the Salesforce connector, see xref:chap-salesforce.adoc#chap-salesforce["Salesforce Connector"]. + +XML File Connector:: +The XML File connector is really useful only in a demonstration context and should not be used in the general provisioning of XML data stores. + ++ +For information about configuring the XML File connector, see xref:chap-xml.adoc#chap-xml["XML File Connector"]. + +Active Directory Connector:: +The Active Directory connector is a legacy connector, written in C# for the .NET platform. + ++ +For information about installing and configuring the Active Directory connector, see xref:chap-ad.adoc#chap-ad["Active Directory Connector"]. + +-- + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-powershell.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-powershell.adoc new file mode 100644 index 000000000..b1d8ec0ea --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-powershell.adoc @@ -0,0 +1,261 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-powershell] +== PowerShell Connector Toolkit + +The PowerShell Connector Toolkit is not a complete connector in the traditional sense. Rather, it is a framework within which you must write your own PowerShell scripts to address the requirements of your Microsoft Windows ecosystem. You can use the PowerShell Connector Toolkit to create connectors that can provision any Microsoft system, including, but not limited to, Active Directory, MS SQL, MS Exchange, SharePoint, Azure, and Office365. Essentially, any task that can be performed with PowerShell can be executed through connectors based on this toolkit. + +The PowerShell Connector Toolkit is available, with a subscription, from the link:https://backstage.forgerock.com/[ForgeRock Backstage, window=\_blank] site. + +OpenIDM includes Active Directory and Azure sample scripts for the PowerShell connector that can help you get started with this toolkit. For more information, see xref:../samples-guide/chap-powershell-samples.adoc#chap-powershell-samples["Samples That Use the PowerShell Connector Toolkit to Create Scripted Connectors"] in the __Samples Guide__. + +The sample scripts illustrate the following scenarios: + +* Synchronization of users between Windows AD DS and OpenIDM. + +* Synchronization of users between Windows Azure AD and OpenIDM. + + +[#powershell-before-you-start] +=== Before You Start + +To implement the scripted PowerShell connector, you need to install the following: + +* Microsoft .NET Framework 4.5 or later. Connectors created with the PowerShell Connector Toolkit run on the .NET platform and require the installation of a .NET connector server on the Windows system. To install the .NET connector, follow the instructions in xref:../integrators-guide/index.adoc["Installing and Configuring a .NET Connector Server"] in the __Integrator's Guide__. + +* PowerShell version 2.0 or above. The PowerShell Connector Toolkit is not bundled with OpenIDM, but is available, with a ForgeRock community account, from link:https://backstage.forgerock.com/[ForgeRock Backstage, window=\_blank]. To install the connector, download the archive (`mspowershell-connector-1.4.2.0.zip`) and extract the `MsPowerShell.Connector.dll` to the same directory where the Connector Server (`ConnectorServerService.exe` or the legacy version `ConnectorServer.exe`) is located. + +If you're running a supported version of Microsoft Windows Server, you should already meet these requirements. + + +[#powershell-connector-setup] +=== Setting Up the PowerShell Connector + + +==== +To run the commands in this procedure, start with the PowerShell command line. Some of the commands in this procedure require administrative privileges. + +. Install, configure, and start the .NET connector server on a Windows host. If you are running an Active Directory Domain Controller, install that .NET connector server on the same host on which the Windows PowerShell module is installed. ++ +For instructions on installing the .NET connector server, see xref:../integrators-guide/index.adoc["Installing and Configuring a .NET Connector Server"] in the __Integrator's Guide__. + +. Configure OpenIDM to connect to the .NET connector server. ++ +To do so, copy the remote connector provisioner file from the `openidm\samples\provisioners` directory to your project's `conf\` directory, and edit the file to match your configuration. ++ + +[source, console] +---- +PS C:\ cd \path\to\openidm +PS C:\path\to\openidm cp samples\provisioners\provisioner.openicf.connectorinfoprovider.json conf +---- ++ +For instructions on editing this file, see xref:../integrators-guide/chap-resource-conf.adoc#net-connector-openidm["Configuring OpenIDM to Connect to the .NET Connector Server"] in the __Integrator's Guide__. + +. Download the PowerShell Connector Toolkit archive (`mspowershell-connector-1.4.2.0.zip`) from the link:https://backstage.forgerock.com/[ForgeRock Backstage, window=\_blank] site. ++ +Extract the archive and move the `MsPowerShell.Connector.dll` to the folder in which the connector server application executable files (`ConnectorServerService.exe` and the legacy `ConnectorServer.exe`) are located. + +. OpenIDM includes PowerShell scripts in `openidm\samples` subdirectories, including `powershell2AD/` for Active Directory, and `powershell2AzureAD` for Azure AD. Copy these scripts to the host on which the .NET connector server is installed. ++ +The location of the scripts must be referenced in your connector configuration file, for example: ++ + +[source, console] +---- +"CreateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADCreate.ps1", +... +---- + +. Copy the sample connector configuration for the PowerShell connector from the `samples\provisioners` directory to your project's `conf` directory. ++ +OpenIDM includes two sample PowerShell connector configurations: ++ + +* Active Directory: `provisioner.openicf-adpowershell.json` + +* Azure AD: `provisioner.openicf-azureadpowershell.json` + ++ +Each sample connector configuration file points to scripts in a specific directory. You may need to change them to match your deployment. If you're connecting to a remote system such as Azure AD, you should also specify the `Host` and `Port` for your .NET server, as well as authentication information for your remote Azure AD deployment. ++ + +[source] +---- +"configurationProperties" : { + ... + "CreateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADCreate.ps1", + "DeleteScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADDelete.ps1", + ... + "Host" : "[substitute Hostname or IP Address]", + "Port" : [substitute port number], + "Login" : "[substitute Windows Server auth]", + "Password" : "[substitute password]", + ... +}, +---- ++ + +[NOTE] +====== +In provisioner files, the OpenICF framework requires the path to use forward slash characters and not the backslash characters that you would expect in a Windows path. +====== + +==== + + +[#powershell-connector-test] +=== Testing the PowerShell Connector + +Start OpenIDM with the configuration for your PowerShell connector project. + +The following tests assume that the configuration is in the default `path/to/openidm` directory. If your PowerShell project is in a different directory, use the `startup` command with the `-p` option to point to that directory. + +[source, console] +---- +$ cd path/to/openidm +$ ./startup.sh +---- + +[#powershell-connector-test-correct] +==== Confirming the Connector Configuration + +To test that the PowerShell connector has been configured correctly, run the following REST call: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=test" +{ + "name" : "azureadpowershell", + "enabled" : true, + "config" : "config/provisioner.openicf/azureadpowershell", + "objectTypes" : [ "__ALL__", "group", "account ], + "connectorRef" : { + "connectorName" : "Org.Forgerock.OpenICF.Connectors.MsPowerShell.MsPowerShellConnector", + "bundleName" : "MsPowerShell.Connector", + "bundleVersion" : "1.4.2.0" + }, + "displayName" : "PowerShell Connector", + "ok" : true +} +---- +The displayed output demonstrates a successful configuration of an Azure AD connector. + +When you run this test, you should also see a log entry associated with the .NET connector server, in the `logs/` subdirectory of that server. + + +[#powershell-connector-search] +==== Searching With the Connector + +You can use the connector, with a PowerShell search script, to retrieve information from a target system. The PowerShell search script accepts OpenIDM queries, including `query-all-ids` and `_queryFilter` + +With the following command, you can retrieve a list of existing users on an Azure AD system. You can also use any system-enabled filter, such as those described in xref:../integrators-guide/chap-data.adoc#query-presence["Presence Expressions"] in the __Integrator's Guide__. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/azureadpowershell/account?_queryId=query-all-ids" +---- + + +[#powershell-connector-create] +==== Creating With the Connector + +You can use the connector to create new users or groups on the target system, based options listed in the relevant `provisioner.openicf-*` configuration file. + +For example, the following command creates a new user on a remote Azure AD instance: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +--header "content-type: application/json" \ +--data '{ + "PasswordNeverExpires": false, + "AlternateEmailAddresses": ["Robert.Smith@example.com"], + "LastName": "Smith", + "PreferredLanguage": "en-US", + "FirstName": "Robert", + "UserPrincipalName": "Robert.Smith@example.onmicrosoft.com", + "DisplayName": "Robert Smith" +}' \ +"http://localhost:8080/openidm/system/azureadpowershell/account?_action=create" +---- + + +[#powershell-connector-update] +==== Updating With the Connector + +The PowerShell scripts associated with update functionality support changes to the following properties: + +* Password + +* Principal Name + +* License + +* Common user attributes + +As an example, you could use the following command to change the password for the user with the noted `_id`: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request PATCH \ +--header "content-type: application/json" \ +--data '{ + "operation": "replace", + "Field": "__PASSWORD__", + "value": "Passw1rd" +}' \ +"http://localhost:8080/openidm/system/azureadpowershell/account/1d4c9276-6937-4d9e-9c60-67e8b4207f4e" +---- + + +[#powershell-script-delete] +==== Deleting With the Connector + +You can use the PowerShell connector to delete user and group objects. As an example, the following command deletes one user from an Azure AD deployment, based on their `_id`: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request DELETE \ +"http://localhost:8080/openidm/system/azureadpowershell/account/1d4c9276-6937-4d9e-9c60-67e8b4207f4e" +---- + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-salesforce.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-salesforce.adoc new file mode 100644 index 000000000..b62f045ec --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-salesforce.adoc @@ -0,0 +1,31 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-salesforce] +== Salesforce Connector + +The link:https://forgerock.org/downloads/[Enterprise build, window=\_blank] of OpenIDM includes a Salesforce connector, along with a sample connector configuration. The Salesforce connector enables provisioning, reconciliation, and synchronization between Salesforce and the OpenIDM repository. + +To use this connector, you need a Salesforce account, and a Connected App that has OAuth enabled, which will allow you to retrieve the required consumer key and consumer secret. + +For additional instructions, and a sample Salesforce configuration, see xref:../samples-guide/chap-salesforce-sample.adoc#chap-salesforce-sample["Salesforce Sample - Salesforce With the Salesforce Connector"] in the __Samples Guide__. + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-sap.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-sap.adoc new file mode 100644 index 000000000..dd25ee394 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-sap.adoc @@ -0,0 +1,1506 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-sap] +== Scripted SAP Connector + +The scripted SAP connector is an implementation of the Scripted Groovy Connector Toolkit that connects to any SAP system using the SAP JCo Java libraries. This chapter describes how to install and configure the scripted SAP connector, and how to test the sample scripts that are bundled with the connector. + +The sample scripts illustrate the following scenarios: + +* Synchronization of users between an SAP HR module and OpenIDM + +* Synchronization of users between OpenIDM and an SAP (R/3) system + + +[#sap-before-you-start] +=== Before You Start + + +* The SAP connector is provided only with the OpenIDM Enterprise build, and is available on the link:https://backstage.forgerock.com[ForgeRock Backstage, window=\_blank] site. + +* The SAP connector requires the SAP Java Connector (JCo) libraries, version 3.0.12 or later. ForgeRock distributes the SAP connector without these JCo libraries. Before you can use the SAP connector, you must obtain the JCo libraries that correspond to your architecture. + + + +[#sap-connector-setup] +=== Setting Up the SAP Connector + + +==== + +. Download the SAP connector from the link:https://backstage.forgerock.com[ForgeRock Backstage, window=\_blank] site. + +. Copy the SAP connector JAR file (`sap-connector-1.4.0.0.jar`) to the `openidm/connectors` directory: ++ + +[source, console] +---- +$ cp ~/Downloads/sap-connector-1.4.0.0.jar /path/to/openidm/connectors +---- + +. Copy the SAP JCo libraries that correspond to your architecture to the `/path/to/openidm/lib` directory. For example: ++ + +[source, console] +---- +$ cp sapjco3.jar /path/to/openidm/lib +$ cp libsapjco3.so /path/to/openidm/lib +---- + +. Change your OpenIDM logging configuration to log messages from the SAP connector. ++ +By default, OpenIDM logs nothing for the SAP connector. To troubleshoot any issues with the connector, set the following properties in your project's `conf/logging.properties` file: ++ + +[source] +---- +# SAP Connector Logging +org.forgerock.openicf.connectors.sap.level=FINER +samples.r3.level=FINER +samples.hr.level=FINER +samples.level=FINER +---- + +==== + + +[#sap-hr] +=== Using the SAP Connector With an SAP HR System + +The SAP HR sample scripts enable you to manage the email address and global employee UID of records in an SAP HR system. + +The following sections explain how to configure OpenIDM to use these sample scripts, how to test the connection to the SAP HR system, and how to update user records. + +[#sap-hr-openidm-setup] +==== Setting up OpenIDM for the SAP HR Samples + + +==== + +. Create a connector configuration file for the SAP connector and place it in your project's `conf/` directory. ++ +You can use this sample link:../resources/provisioner.openicf-saphr.json[provisioner.openicf-saphr.json, window=\_blank] as a guide. ++ +Edit that file with the connection details for your SAP HR system. Specifically, set at least the following properties: ++ +-- + +`destination`:: +An alias to the SAP system to which you are connecting, for example, `SAP1`. If you are connecting to more than one SAP system, the `destination` property for each system must be unique. ++ +The sample connector configuration assumes a connection to a single SAP system, so the value for this property in the sample configuration is `OPENIDM`. + +`asHost`:: +The FQDN of your SAP Application Server, for example `sap.example.com`. + +`user`:: +Your SAP user account. + +`password`:: +The password of this SAP user account. + +`client`:: +The SAP Client number that will be used to connect to the SAP system. + +`systemNumber`:: +The SAP system number. + +`directConnection`:: +A boolean (true/false). If `true`, the connection goes directly to an SAP ABAP Application server or SAP router. If `false`, the connection goes to a group of SAP instances, through an SAP message server. + +`sapRouter`:: +The IP address and port of the SAP router, if applicable. The syntax is `/H/host[/S/port]`, for example `/H/203.0.113.0/S/3299`. + +`poolCapacity`:: +The maximum number of idle connections kept open by the destination. If there is no connection pooling, set this to `0`. The default value is `1`. ++ +For optimum performance, set this value to an integer between `5` and `10`. + +-- + +. To test this connector, you can use the sample Groovy scripts available from the link:https://maven.forgerock.org/repo/webapp/#/artifacts/browse/tree/General/releases/org/forgerock/openicf/connectors/sap-connector/1.4.0.0/sap-connector-1.4.0.0-sources.jar/samples/[ForgeRock Artifact Repository Browser, window=\_blank]. You can find the source for these scripts in this location, in the `samples/` directory, as well as the `samples/hr/` subdirectory. ++ +[none] +* `TestSAP.groovy` +* `SearchSAPHR.groovy` +* `UpdateSAPHR.groovy` +* `SchemaSAPHR.groovy` +* `EmplComm.groovy` ++ +Update your connector configuration to point to those scripts. The sample connector configuration assumes the following locations for the scripts (relative to the value of the `scriptRoots` property): ++ + +[source, console] +---- +"testScriptFileName" : "samples/TestSAP.groovy", +"searchScriptFileName" : "samples/hr/SearchSAPHR.groovy", +"updateScriptFileName" : "samples/hr/UpdateSAPHR.groovy", +"schemaScriptFileName" : "samples/hr/SchemaSAPHR.groovy", +---- ++ +The `EmplComm.groovy` must be placed in the same location as the Search, Update, and Schema scripts. ++ + +[IMPORTANT] +====== +The Groovy scripts belong to a specific package. The parent directory where the scripts are located must be the same as the package name. So the `TestSAP.groovy` script must be under a `samples` directory (because it belongs to the `samples` package) and the remaining HR scripts must be under a `samples/hr` directory (because they belong to the `hr` package). +====== + +==== + + +[#sap-hr-connector-test] +==== Testing the Connection to the SAP HR System + + +==== + +. Start OpenIDM with the configuration for your SAP connector project. ++ +This procedure assumes that the configuration is in the default `path/to/openidm` directory. If your SAP project is in a different directory, use the `-p` option with the startup command to point to that directory. ++ + +[source, console] +---- +$ cd path/to/openidm +$ ./startup.sh +---- + +. Test that the connector has been configured correctly and that the SAP HR system can be reached: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/saphr/?_action=test" +{ + "name" : "saphr", + "enabled" : true, + "config" : "config/provisioner.openicf/saphr2", + "objectTypes" : [ "__ALL__", "employee" ], + "connectorRef" : { + "connectorName" : "org.forgerock.openicf.connectors.sap.SapConnector", + "bundleName" : "org.forgerock.openicf.connectors.sap-connector", + "bundleVersion" : "1.4.0.0" + }, + "displayName" : "Sap Connector", + "ok" : true +} +---- + +. Retrieve a list of the existing users (with their employee number) in the SAP HR system: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/saphr/employee?_queryId=query-all-ids" +{ + "result" : [ { + "_id" : "00000010", + "__NAME__" : "00000010" + }, { + "_id" : "00000069", + "__NAME__" : "00000069" + }, { + "_id" : "00000070", + "__NAME__" : "00000070" + }, +... +---- + +. Retrieve the complete record of an employee in the SAP HR system by including the employee's ID in the URL. ++ +The following command retrieves the record for employee Maria Gonzales: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/saphr/employee/55099307" +{ + "_id" : "55099307", + "PERSONAL_DATA" : { + "PERNO" : "55099307", + "INFOTYPE" : "0002", + "TO_DATE" : "Fri Dec 31 00:00:00 CET 9999", + "FROM_DATE" : "Tue Mar 30 00:00:00 CET 1954", + "SEQNO" : "000", + "CH_ON" : "Thu Mar 27 00:00:00 CET 2003", + "CHANGED_BY" : "MAYROCK", + "LAST_NAME" : "Gonzales", + "FIRSTNAME" : "Maria", + "NAME_FORM" : "00", + "FORMOFADR" : "2", + "GENDER" : "2", + "BIRTHDATE" : "Tue Mar 30 00:00:00 CET 1954", + "LANGU" : "D", + "NO_O_CHLDR" : "0", + "BIRTHYEAR" : "1954", + "BIRTHMONTH" : "03", + "BIRTHDAY" : "30", + "LASTNAME_M" : "GONZALES", + "FSTNAME_M" : "MARIA" + }, +... +} +---- + +==== + + +[#sap-connector-uname-email] +==== Using the SAP Connector to Manage Employee Information (SAP HR) + +The following sample commands show how the SAP connector is used to manage the email account of user Maria Gonzales, retrieved in the previous step. Management of the global UID (`SYS-UNAME`) works in the same way. + +==== + +. Check if Maria Gonzales already has an email account on the SAP HR system by filtering a query on her user account for the `EMAIL` field: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/saphr/employee/55099307?_fields=EMAIL" +{ + "_id" : "55099307", +} +---- ++ +No email account is found for Maria Gonzales. + +. Add an email account by sending a PUT request. The JSON payload should include the email address as the value of the `ID` property: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "EMAIL": { "ID": "maria.gonzales@example.com" } + }' \ + "http://localhost:8080/openidm/system/saphr/employee/55099307" +{ + "_id" : "55099307", + "EMAIL" : [ { + "EMPLOYEENO" : "55099307", + "SUBTYPE" : "0010", + "VALIDEND" : "Fri Dec 31 00:00:00 CET 9999", + "VALIDBEGIN" : "Fri March 18 00:00:00 CET 2016", + "RECORDNR" : "000", + "COMMTYPE" : "0010", + "NAMEOFCOMMTYPE" : "E-mail", + "ID" : "Maria.Gonzales@example.com" + } ], +... +---- ++ +By default, the connector sets the `VALIDBEGIN` date to the current date, and the `VALIDEND` date to the SAP "END" date (12/31/9999). You can specify different temporal constraints by including these properties in the JSON payload, with the format `YYYYMMDD`. For example: ++ + +[source, console] +---- +{ + "EMAIL": { + "ID": "maria.gonzales@example.com" + "VALIDBEGIN": "20160401", + "VALIDEND": "20161231" + } +} +---- + +. To change the value of an existing email account, provide a new value for the `ID`. ++ +The JSON payload of the change request must also include the `RECORDNR` attribute, as well as the `VALIDBEGIN` and `VALIDEND` dates, in SAP format (`YYYYMMDD`). ++ +The following example changes Maria Gonzales' email address to `maria.gonzales-admin@example.com`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "EMAIL": { + "ID": "maria.gonzales-admin@example.com", + "RECORDNR" : "000", + "VALIDEND" : "99991231", + "VALIDBEGIN" : "20000101" + } + }' \ + "http://localhost:8080/openidm/system/saphr/employee/55099307" +---- + +. To change the temporal constraint (`VALIDEND` date) of the record, include the existing `VALIDEND` data in the JSON payload, and specify the new end date as a value of the `DELIMIT_DATE` attribute. ++ +The following example changes the end date of Maria Gonzale's new mail address to December 31st, 2016: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "EMAIL": { + "ID": "maria.gonzales-admin@example.com", + "RECORDNR" : "000", + "VALIDEND" : "99991231", + "VALIDBEGIN" : "20000101", + "DELIMIT_DATE": "20161231" + } + }' \ + "http://localhost:8080/openidm/system/saphr/employee/55099307" +---- + +. To delete the email address of the record, send a PUT request with the current `RECORDNR`, `VALIDBEGIN`, and `VALIDEND` attributes, but without the `ID`. ++ +The following request removes the email address from Maria Gonzales' record: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "EMAIL": { + "RECORDNR" : "000", + "VALIDEND" : "99991231", + "VALIDBEGIN" : "20000101" + } + }' \ + "http://localhost:8080/openidm/system/saphr/employee/55099307" +---- + +==== + + + +[#sap-r3] +=== Using the SAP Connector to Manage SAP Basis System (R/3) Users + +The SAP Connector enables you to perform the following operations on SAP system user accounts: + +* List all users + +* List all activity groups (roles) + +* Manage user profiles + +* List all user companies + +* Obtain a user's details + +* Create a user + +* Update a user + +* Assign roles to a user + +* Lock a user account + +* Unlock a user account + +* Delete a user account + +Currently, the SAP connector cannot detect changes on the SAP system in real time. You must run an OpenIDM reconciliation operation to detect changes on the SAP system. + +[#sap-r3-openidm-setup] +==== Setting up OpenIDM for the SAP R/3 Samples + + +==== + +. Create a connector configuration file for the SAP connector and place it in your project's `conf/` directory. ++ +You can use this sample link:../resources/provisioner.openicf-sapr3.json[provisioner.openicf-sapr3.json, window=\_blank] as a guide. ++ +Edit that file with the connection details for your SAP R/3 system. Specifically, set at least the following properties: ++ +-- + +`destination`:: +An alias to the SAP system to which you are connecting, for example, `SAP1`. If you are connecting to more than one SAP system, the `destination` property for each system must be unique. ++ +The sample connector configuration assumes a connection to a single SAP system, `MYSAP`. + +`asHost`:: +The FQDN of your SAP Application Server, for example `sap.example.com`. + +`user`:: +Your SAP user account. + +`password`:: +The password of this SAP user account. + +`client`:: +The SAP Client number that will be used to connect to the SAP system. + +`systemNumber`:: +The SAP system number. + +`directConnection`:: +A boolean (true/false). If `true`, the connection goes directly to an SAP ABAP Application server or SAP router. If `false`, the connection goes to a group of SAP instances, through an SAP message server. + +`sapRouter`:: +The IP address and port of the SAP router, if applicable. The syntax is `/H/host[/S/port]`, for example `/H/203.0.113.0/S/3299`. + +`poolCapacity`:: +The maximum number of idle connections kept open by the destination. If there is no connection pooling, set this to `0`. The default value is `1`. ++ +For optimum performance, set this value to an integer between `5` and `10`. + +-- + +. To test this connector, you can use the sample Groovy scripts available from the link:https://maven.forgerock.org/repo/webapp/#/artifacts/browse/tree/General/releases/org/forgerock/openicf/connectors/sap-connector/1.4.0.0/sap-connector-1.4.0.0-sources.jar/samples/[ForgeRock Artifact Repository Browser, window=\_blank]. You can find the source for these scripts in this location, in the `samples/` directory, as well as the `samples/r3/` subdirectory. ++ +[none] +* `TestSAP.groovy` +* `SearchSAPR3.groovy` +* `CreateSAPR3.groovy` +* `UpdateSAPR3.groovy` +* `DeleteSAPR3.groovy` +* `SchemaSAPR3.groovy` ++ +Update your connector configuration to point to those scripts. The sample connector configuration assumes the following locations for the scripts (relative to the value of the `scriptRoots` property): ++ + +[source, console] +---- +"testScriptFileName" : "samples/TestSAP.groovy", +"searchScriptFileName" : "samples/r3/SearchSAPR3.groovy", +"createScriptFileName" : "samples/r3/CreateSAPR3.groovy", +"updateScriptFileName" : "samples/r3/UpdateSAPR3.groovy", +"deleteScriptFileName" : "samples/r3/DeleteSAPR3.groovy", +"schemaScriptFileName" : "samples/r3/SchemaSAPR3.groovy", +---- ++ + +[IMPORTANT] +====== +The Groovy scripts belong to a specific package. The parent directory where the scripts are located must be the same as the package name. So the `TestSAP.groovy` script must be under a `samples` directory (because it belongs to the `samples` package) and the R/3 scripts must be under a `samples/r3` directory (because they belong to the `r3` package). +====== + +==== + + +[#sap-r3-connector-test] +==== Testing the Connection to the SAP R/3 System + + +==== + +. Start OpenIDM with the configuration for your SAP R/3 project. ++ +This procedure assumes that the configuration is in the default `path/to/openidm` directory. If your SAP project is in a different directory, use the `-p` option with the startup command to point to that directory. ++ + +[source, console] +---- +$ cd path/to/openidm +$ ./startup.sh +---- + +. Test that the connector has been configured correctly and that the SAP R/3 system can be reached: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/mysap/?_action=test" +{ + "name": "mysap", + "enabled": true, + "config": "config/provisioner.openicf/mysap", + "objectTypes": [ + "__ALL__", + "user", + "activity_group", + "company", + "profile" + ], + "connectorRef": { + "connectorName": "org.forgerock.openicf.connectors.sap.SapConnector", + "bundleName": "org.forgerock.openicf.connectors.sap-connector", + "bundleVersion": "1.4.0.0" + }, + "displayName": "Sap Connector", + "ok": true +} +---- + +==== + + +[#sap-r3-user-management] +==== Using the SAP Connector to Manage SAP R/3 Users + +This section provides sample commands for managing users in an SAP system. + +[#sap-list-users] +===== Listing the Users in the SAP System + +The following command returns a list of the existing users in the SAP system, with their IDs: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/mysap/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "BJENSEN", + "__NAME__": "BJENSEN" + }, + { + "_id": "DDIC", + "__NAME__": "DDIC" + }, + ... + { + "_id": "USER4", + "__NAME__": "USER4" + }, + { + "_id": "USER6", + "__NAME__": "USER6" + }, + { + "_id": "USER7", + "__NAME__": "USER7" + } + ], + "resultCount": 9, + "pagedResultsCookie": null, + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "remainingPagedResults": -1 +} +---- + + +[#sap-user-get] +===== Obtaining the Details of an SAP User + +The following command uses the SAP connector to obtain a user's details from a target SAP system: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/mysap/user/BJENSEN" +{ + "__NAME__": "BJENSEN", + "__ENABLE__": true, + "__ENABLE_DATE__": "2015-09-01", + "__DISABLE_DATE__": "2016-09-01", + "__LOCK_OUT__": false, + "ADDTEL": [ + { + "COUNTRY": "DE", + "TELEPHONE": "19851444", + ... + }, + ... + ], + "PROFILES": [ + { + "BAPIPROF": "T_ALM_CONF", + ... + } + ], + "ISLOCKED": { + "WRNG_LOGON": "U", + ... + }, + "ACTIVITYGROUPS": [ + { + "AGR_NAME": "MW_ADMIN", + "FROM_DAT": "2015-07-15", + "TO_DAT": "9999-12-31", + "AGR_TEXT": "Middleware Administrator" + }, + ... + ], + "DEFAULTS": { + ... + }, + "COMPANY": { + "COMPANY": "SAP AG" + }, + "ADDRESS": { + ... + }, + "UCLASS": { + ... + }, + "LASTMODIFIED": { + "MODDATE": "2015-07-15", + "MODTIME": "14:22:57" + }, + "LOGONDATA": { + "GLTGV": "2015-09-01", + "GLTGB": "2016-09-01", + ... + }, + "_id": "BJENSEN" +} +---- +In addition to the standard user attributes, the GET request returns the following OpenICF operational attributes: + +* `__ENABLE__` - indicates whether the account is enabled, based on the value of the `LOGONDATA` attribute + +* `__ENABLE_DATE__` - set to the value of `LOGONDATA/GLTGV` (date from which the user account is valid) + +* `__DISABLE_DATE__` - set to the value of `LOGONDATA/GLTGB` (date to which the user account is valid) + +* `__LOCK_OUT__` - indicates whether the account is locked + + + +[#sap-create-user] +===== Creating SAP User Accounts + +To create a user, you must supply __at least__ a username and password. If you do not provide a lastname, the connector uses the value of the username. + +The following command creates a new SAP user, `SCARTER`: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__" : "SCARTER", + "__PASSWORD__": "Passw0rd" + }' \ + "http://localhost:8080/openidm/system/mysap/user/?_action=create" +{ + "_id": "SCARTER", + "COMPANY": { + "COMPANY": "SAP AG" + }, + "__LOCK_OUT__": false, + "ADDRESS": { + ... + }, + "__NAME__": "SCARTER", + "LASTMODIFIED": { + "MODDATE": "2016-04-20", + "MODTIME": "04:14:29" + }, + "UCLASS": { + "COUNTRY_SURCHARGE": "0", + "SUBSTITUTE_FROM": "0000-00-00", + "SUBSTITUTE_UNTIL": "0000-00-00" + }, + "__ENABLE__": true, + "DEFAULTS": { + "SPDB": "H", + "SPDA": "K", + "DATFM": "1", + "TIMEFM": "0" + }, + "LOGONDATA": { + ... + }, + "ISLOCKED": { + "WRNG_LOGON": "U", + "LOCAL_LOCK": "U", + "GLOB_LOCK": "U", + "NO_USER_PW": "U" + } +} +---- +The SAP account that is created is valid and enabled, but the password is expired by default. To log into the SAP system, the newly created user must first provide a new password. + +To create a user with a valid (non-expired) password, include the `__PASSWORD_EXPIRED__` attribute in the JSON payload, with a value of `false`. For example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__" : "SCARTER", + "__PASSWORD__": "Passw0rd", + "__PASSWORD_EXPIRED__": false + }' \ + "http://localhost:8080/openidm/system/mysap/user/?_action=create" +---- +To create an account that is locked by default, include the `__LOCK_OUT__` attribute in the JSON payload, with a value of `true`. For example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__" : "SCARTER", + "__PASSWORD__": "Passw0rd", + "__LOCK_OUT__": true + }' \ + "http://localhost:8080/openidm/system/mysap/user/?_action=create" +{ + "__NAME__": "SCARTER", + "__ENABLE__": false, + "__LOCK_OUT__": true, + "LOGONDATA": { + "GLTGV": "0000-00-00", + "GLTGB": "0000-00-00", + "USTYP": "A", + "LTIME": "00:00:00", + "BCODE": "2FC0D86C99AA5862", + "CODVN": "B", + "PASSCODE": "1DBBD983287D7CB4D8177B4333F439F808A395FA", + "CODVC": "F", + "PWDSALTEDHASH": "{x-issha, 1024}zrs3Zm/fX/l/KFGATp3kvOGlis3zLLiPmPVCDpJ9XF0=", + "CODVS": "I" + }, + "LASTMODIFIED": { + "MODDATE": "2015-10-01", + "MODTIME": "15:25:18" + }, + "ISLOCKED": { + "WRNG_LOGON": "U", + "LOCAL_LOCK": "L", // "L" indicates that the user is locked on the local system + "GLOB_LOCK": "U", + "NO_USER_PW": "U" + }, +... +---- + +[#sap-user-schema] +====== Schema Used by the SAP Connector For User Accounts + +For the most part, the SAP connector uses the standard SAP schema to create a user account. The most common attributes in an SAP user account are as follows: + +* `ADDRESS` - user address data + +* `LOGONDATA` - user logon data + +* `DEFAULTS` - user account defaults + +* `COMPANY` - the company to which the user is assigned + +* `REF_USER` - the usernames of the Reference User + +* `ALIAS` - an alias for the username + +* `UCLASS` - license-related user classification + +* `LASTMODIFIED` - read-only attribute that indicates the date and time that the account was last changed + +* `ISLOCKED` - read-only attribute that indicates the lockout status of the account + +* `IDENTITY` - assignment of a personal identity to the user account + +* `PROFILES` - any profiles assigned to the user account (see xref:#user-profiles["Managing User Profiles"]). + +* `ACTIVITYGROUPS` - activity groups assigned to the user + +* `ADDTEL` - telephone numbers assigned to the user + +In addition, the SAP connector supports the following OpenICF operational attributes for CREATE requests: + +* `LOCK_OUT` + +* `PASSWORD` + +* `PASSWORD_EXPIRED` + +The following example creates a user, KVAUGHAN, with all of the standard attributes: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__" : "KVAUGHAN", + "__PASSWORD__": "Passw0rd", + "__PASSWORD_EXPIRED__": false, + "LOGONDATA": { + "GLTGV": "2016-04-01", + "GLTGB": "2016-12-01", + "USTYP": "A" + }, + "ADDRESS": { + "FIRSTNAME": "Katie", + "LASTNAME": "Vaughan", + "TEL1_NUMBR": "33297603177", + "E_MAIL": "katie.vaughan@example.com", + "FUNCTION": "Test User" + }, + "COMPANY": { + "COMPANY": "EXAMPLE.COM" + }, + "ALIAS": { + "USERALIAS": "KVAUGHAN" + } + }' \ + "http://localhost:8080/openidm/system/mysap/user/?_action=create" +{ + "_id": "KVAUGHAN", + "ADDRESS": { + "PERS_NO": "0000010923", + "ADDR_NO": "0000010765", + "FIRSTNAME": "Katie", + "LASTNAME": "Vaughan", + "FULLNAME": "Katie Vaughan", + ... + "E_MAIL": "katie.vaughan@example.com", + "LANGU_CR_P": "E", + "LANGUCPISO": "EN" + }, + "LOGONDATA": { + "GLTGV": "2016-04-01", + "GLTGB": "2016-12-01", + ... + }, + "COMPANY": { + "COMPANY": "SAP AG" + }, + "__ENABLE__": true, + "ADDTEL": [ + { + ... + } + ], + "ISLOCKED": { + "WRNG_LOGON": "U", + "LOCAL_LOCK": "U", + "GLOB_LOCK": "U", + "NO_USER_PW": "U" + }, + "UCLASS": { + "COUNTRY_SURCHARGE": "0", + "SUBSTITUTE_FROM": "0000-00-00", + "SUBSTITUTE_UNTIL": "0000-00-00" + }, + "ALIAS": { + "USERALIAS": "KVAUGHAN" + }, + "__NAME__": "KVAUGHAN", + "__LOCK_OUT__": false, + "LASTMODIFIED": { + "MODDATE": "2016-04-20", + "MODTIME": "04:55:08" + }, + "__ENABLE_DATE__": "2016-04-01", // (Value of LOGONDATA/GLTGV) + "DEFAULTS": { + "SPDB": "H", + "SPDA": "K", + "DATFM": "1", + "TIMEFM": "0" + }, + "__DISABLE_DATE__": "2016-12-01" // (Value of LOGONDATA/GLTGB) +} +---- + + + +[#sap-update-user] +===== Updating SAP User Accounts + +The following sections provide sample commands for updating an existing user account. + +[#update-account-lock] +====== Locking and Unlocking an Account + +To lock or unlock a user's account, send a PUT request, and set the value of the user's `__LOCK_OUT__` attribute to `true`. + +The following example locks user KVAUGHAN's account: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "__LOCK_OUT__": true + }' \ + "http://localhost:8080/openidm/system/mysap/user/KVAUGHAN" +---- +The following example unlocks KVAUGHAN's account: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "__LOCK_OUT__": false + }' \ + "http://localhost:8080/openidm/system/mysap/user/KVAUGHAN" +---- + + +[#update-standard-attributes] +====== Updating the Standard Attributes of a User's Account + +To update a user's standard attributes, send a PUT request to the user ID. The JSON payload must respect the structure for each attribute, as indicated in xref:#sap-user-schema["Schema Used by the SAP Connector For User Accounts"]. + +The following command updates the `ADDRESS` attribute of user KVAUGHAN: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "ADDRESS": { + "FIRSTNAME": "Katie", + "LASTNAME": "Vaughan", + "FULLNAME": "Katie Vaughan", + "FUNCTION": "Administrator", + "TITLE": "Company", + "NAME": "EXAMPLE.COM", + "CITY": "San Francisco", + "POSTL_COD1": "94105", + "STREET": "Sacramento St", + "HOUSE_NO": "2912", + "COUNTRY": "US", + "COUNTRYISO": "US", + "LANGU": "E", + "LANGU_ISO": "EN", + "REGION": "CA", + "TIME_ZONE": "PST", + "TEL1_NUMBR": "33297603177", + "E_MAIL": "katie.vaughan@example.com", + "LANGU_CR_P": "E", + "LANGUCPISO": "EN" + } +}' \ + "http://localhost:8080/openidm/system/mysap/user/KVAUGHAN" +---- + + +[#update-reset-password] +====== Resetting a User's Password + +To reset the user's password, provide the new password as the value of the `__PASSWORD__` attribute, in a PUT request. The following command resets KVAUGHAN's password to `MyPassw0rd`: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "__PASSWORD__": "MyPassw0rd" + }' \ + "http://localhost:8080/openidm/system/mysap/user/KVAUGHAN" +---- +Note that unless you set the `__PASSWORD_EXPIRED__` attribute to `false`, the user will be required to reset her password the next time she logs into the SAP system. + +The following command resets KVAUGHAN's password to `MyPassw0rd`, and ensures that she does not have to reset her password the next time she logs in: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PUT \ + --data '{ + "__PASSWORD__": "MyPassw0rd", + "__PASSWORD_EXPIRED__": false + }' + "http://localhost:8080/openidm/system/mysap/user/KVAUGHAN" +---- + + + +[#sap-delete-user] +===== Deleting User Accounts + +To delete a user account, send a DELETE request to the user ID. The following example deletes KVAUGHAN: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/system/mysap/user/KVAUGHAN" +---- +The command returns the complete user object that was deleted. + + +[#user-profiles] +===== Managing User Profiles + +An SAP system uses __profiles__ to manage authorization. The following examples demonstrate how to add, change, and remove a user's profiles. + +[#create-user-profiles] +====== Creating a User With One or More Profiles + +Profiles are added as an array of one or more objects. + +The following command creates a user BJENSEN, with the system administrator profile (`S_A.SYSTEM`): + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__" : "BJENSEN", + "__PASSWORD__": "Passw0rd", + "__PASSWORD_EXPIRED__": false, + "PROFILES": [ + {"BAPIPROF": "S_A.SYSTEM"} + ] + }' \ + "http://localhost:8080/openidm/system/mysap/user/?_action=create" +{ + "_id": "BJENSEN", + "COMPANY": { + "COMPANY": "SAP AG" + }, + "PROFILES": [ + { + "BAPIPROF": "S_A.SYSTEM", + "BAPIPTEXT": "System administrator (Superuser)", + "BAPITYPE": "S", + "BAPIAKTPS": "A" + } + ], + ... + "__NAME__": "BJENSEN" +} +---- +Note that the additional information regarding that profile is added to the user account automatically. + + +[#update-user-profiles] +====== Updating a User's Profiles + +To update a user's profiles, send a PUT request to the user's ID, specifying the new profiles as an array of values for the `PROFILES` attribute. The values provided in the PUT request will replace the current profiles, so you must include the existing profiles in the request. + +The following example adds the `SAP_ALL` profile to user BJENSEN's account: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "PROFILES": [ + {"BAPIPROF": "S_A.SYSTEM"}, + {"BAPIPROF": "SAP_ALL"} + ] +}' \ + "http://localhost:8080/openidm/system/mysap/user/BJENSEN" +{ + "_id": "BJENSEN", + "COMPANY": { + "COMPANY": "SAP AG" + }, + "PROFILES": [ + { + "BAPIPROF": "SAP_ALL", + "BAPIPTEXT": "All SAP System authorizations", + "BAPITYPE": "C", + "BAPIAKTPS": "A" + }, + { + "BAPIPROF": "S_A.SYSTEM", + "BAPIPTEXT": "System administrator (Superuser)", + "BAPITYPE": "S", + "BAPIAKTPS": "A" + } + ], + ... + "__NAME__": "BJENSEN" +} +---- + + +[#remove-user-profiles] +====== Removing All Profiles From a User Account + +To remove all profiles from a user's account, update the account with an empty array. The following example removes all profiles from BJENSEN's account: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "PROFILES": [] +}' \ + "http://localhost:8080/openidm/system/mysap/user/BJENSEN" + + "_id": "BJENSEN", + "COMPANY": { + "COMPANY": "SAP AG" + }, + ... + "__NAME__": "BJENSEN" +} +---- +The output shows no `PROFILES` attribute, as this attribute is now empty for this user. + + + +[#user-roles] +===== Managing User Roles + +SAP user roles (or __activity groups__) are an alternative mechanism to grant authorization to an SAP system. Essentially, a role encapsulates a set of one or more profiles. + +Roles can be granted with __temporal constraints__, that is, a period during which the role is valid. If no temporal constraints are specified, the SAP connector sets the FROM date to the current date and the TO date to 9999-12-31. + +[#create-user-roles] +====== Creating a User With One or More Profiles + +Roles are added as an array of one or more objects. + +The following command creates a user SCARTER, with two roles: `SAP_AUDITOR_SA_CCM_USR` and `SAP_ALM_ADMINISTRATOR`. The auditor role has a temporal constraint, and is valid only from May 1st, 2016 to April 30th, 2017. The format of the temporal constraint is `YYYY-mm-dd`: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__" : "SCARTER", + "__PASSWORD__": "Passw0rd", + "__PASSWORD_EXPIRED__": false, + "ACTIVITYGROUPS": [ + { + "AGR_NAME": "SAP_AUDITOR_SA_CCM_USR", + "FROM_DAT": "2016-05-01", + "TO_DAT": "2017-04-30" + }, + { + "AGR_NAME": "SAP_ALM_ADMINISTRATOR" + } + ] + }' \ + "http://localhost:8080/openidm/system/mysap/user/?_action=create" +{ + "_id": "SCARTER", + "COMPANY": { + "COMPANY": "SAP AG" + }, + "PROFILES": [ + { + "BAPIPROF": "T_ALM_CONF", + "BAPIPTEXT": "Profile for the Role SAP_ALM_ADMINISTRATOR", + "BAPITYPE": "G", + "BAPIAKTPS": "A" + } + ], + ... + "ACTIVITYGROUPS": [ + { + "AGR_NAME": "SAP_ALM_ADMINISTRATOR", + "FROM_DAT": "2016-04-20", + "TO_DAT": "9999-12-31", + "AGR_TEXT": "Alert Management Administrator" + }, + { + "AGR_NAME": "SAP_AUDITOR_SA_CCM_USR", + "FROM_DAT": "2016-05-01", + "TO_DAT": "2017-04-30", + "AGR_TEXT": "AIS - System Audit - Users and Authorizations" + } + ], + "__NAME__": "SCARTER" +} +---- +When a role is granted, the corresponding profiles are attached to the user account automatically. + + +[#update-user-roles] +====== Updating a User's Roles + +To update a user's roles, send a PUT request to the user's ID, specifying the new roles as an array of values of the `ACTIVITYGROUPS` attribute. The values provided in the PUT request will replace the current `ACTIVITYGROUPS`. + +The following example removes the `SAP_AUDITOR_SA_CCM_USR` role and changes the temporal constraints on the `SAP_ALM_ADMINISTRATOR` role for SCARTER's account: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "ACTIVITYGROUPS": [ + { + "AGR_NAME": "SAP_ALM_ADMINISTRATOR", + "FROM_DAT": "2015-06-02", + "TO_DAT": "2016-06-02" + } + ] +}' \ + "http://localhost:8080/openidm/system/mysap/user/SCARTER" +{ + "_id": "SCARTER", + "COMPANY": { + "COMPANY": "SAP AG" + }, + "PROFILES": [ + { + "BAPIPROF": "T_ALM_CONF", + "BAPIPTEXT": "Profile for the Role SAP_ALM_ADMINISTRATOR", + "BAPITYPE": "G", + "BAPIAKTPS": "A" + } + ], + ... + "ACTIVITYGROUPS": [ + { + "AGR_NAME": "SAP_ALM_ADMINISTRATOR", + "FROM_DAT": "2015-06-02", + "TO_DAT": "2016-06-02", + "AGR_TEXT": "Alert Management Administrator" + } + ], + "__NAME__": "SCARTER" +} +---- + + +[#remove-user-roles] +====== Removing All Roles From a User Account + +To remove all roles from a user's account, update the value of the `ACTIVITYGROUPS` attribute with an empty array. The following example removes all roles from SCARTER's account: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "ACTIVITYGROUPS": [] +}' \ + "http://localhost:8080/openidm/system/mysap/user/SCARTER" +{ + "_id": "SCARTER", + "COMPANY": { + "COMPANY": "SAP AG" + }, + ... + "LASTMODIFIED": { + "MODDATE": "2016-04-21", + "MODTIME": "04:27:00" + }, + "__NAME__": "SCARTER" +} +---- +The output shows no `ACTIVITYGROUPS` attribute, as this attribute is now empty. + + + + + +[#sec-implemented-interfaces-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +=== OpenICF Interfaces Implemented by the SAP Connector + +The SAP Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-ScriptOnResourceApiOp[Script on Resource]:: +Runs a script on the target resource that is managed by this connector. + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +=== SAP Connector Configuration + +The SAP Connector has the following configurable properties. + +[#configuration-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#basic-configuration-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +==== Basic Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#sap-jco-logs-configuration-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +==== SAP Jco Logs Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#advanced-configuration-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +==== Advanced Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#sap-secure-network-connection-configuration-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +==== SAP Secure Network Connection Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#jco-connection-pool-configuration-properties-org-forgerock-openicf-connectors-sap-SapConnector-1_4_0_0] +==== JCo Connection Pool Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-ssh.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-ssh.adoc new file mode 100644 index 000000000..ce95f0146 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-ssh.adoc @@ -0,0 +1,298 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-ssh] +== Scripted SSH Connector + +The scripted SSH connector is an implementation of the Scripted Groovy Connector Toolkit, and is based on Java Secure Channel (JSch) and the Java implementation of the Expect library (Expect4j). This connector enables you to interact with any SSH server, using Groovy scripts for the OpenICF operations. + +The SSH connector is a __poolable connector__. This means that each connector instance is placed into a connection pool every time an action is completed. Subsequent actions can re-use connector instances from the connector pool. When a new connector instance is created, a new SSH client connection is created against the target SSH server. This SSH connection remains open as long as the connector instance is in the connection pool. Note that when a new action is performed, it finds the SSH connection in the exact state that it was left by the previous action. + +The following image shows the relationship between SSH connector instances and SSH connections to the target server: + +image::images/ssh-connector.png[] + +[#ssh-authentication] +=== Configuring Authentication to the SSH Server + +The SSH connector authenticates to the SSH server using either a login/password or a public/private key. The authentication method is specified in the `authenticationType` property in the connector configuration file (`conf/provisioner.openicf-ssh.json`). +-- + +Authenticating with a login and password:: +To authenticate with a login and password, set the `authenticationType` to `PASSWORD` in the connector configuration file, and set a `user` and `password`. For example: ++ + +[source, javascript] +---- +"configurationProperties" : { + ... + "authenticationType" : "PASSWORD", + "user" : "", + "password" : "", + ... +---- ++ +The password is encrypted when OpenIDM loads the provisioner file. + +Authenticating with a passphrase and private key:: +To authenticate with a secure certificate, generate a pair of public/private keys. Install the public key on the server side and the private key on the OpenIDM host (where the connector is located). Set the `authenticationType` to `PUBKEY` in the connector configuration file and set the `user`, `password`, `passphrase` and `privateKey` properties. For example: ++ + +[source, javascript] +---- +"configurationProperties" : { + ... + "authenticationType" : "PUBKEY", + "user" : "", + "password" : "", + "passphrase" : "secret", + "privateKey" : ["-----BEGIN DSA PRIVATE KEY-----", + "MIIBugIBAAKBgQDcB0ztVMCFptpJhqlLNZSdN/5cDL3S7aOVy52Ae7vwwCqQPCQr", + "6NyUk+wtkDr07NlYd3sg7a9hbsEnlYChsuX+/WUIvbOKdMfeqcQ+jKK26YdkTCGj", + "g86dBj9JYhobSHDoQ9ov31pYN/cfW5BAZwkm9TdpEjHPvMIaOxx7GPGKWwIVALbD", + "CEuf1yJk9UB7v0dmJS7bKkbxAoGARcbAuDP4rB6MsgAAkVwf+1sHXEiGPShYWrVV", + "qBgCZ/S45ELqUuiaN/1N/nip/Cc/0SBPKqwl7o50CUg9GH9kTAjmXiwmbkwvtUv+", + "Xjn5vCHS0w18yc3rGwyr2wj+D9KtDLFJ8+T5HmsbPoDQ3mIZ9xPmRQuRFfVMd9wr", + "DY0Rs7cCgYAxjGjWDSKThowsvOUCiE0ySz6tWggHH3LTrS4Mfh2t0tnbUfrXq2cw", + "3CN+T6brgnpYbyX5XI17p859C+cw90MD8N6vvBxaN8QMDRFk+hHNUeSy8gXeem9x", + "O0vdIxCgKvA4dh5nSVb5VGKENEGNEHRlYxEPzbqlPa/C/ZvzIvdKXQIUQMoidPFC", + "n9z+mE2dAADnPf2m9vk=", + "-----END DSA PRIVATE KEY-----" + ], + ... +---- ++ +The default value for the `passphrase` property is `null`. If you do not set a passphrase for the private key, the passphrase value must be equal to an empty string. + ++ +You __must__ set a value for the `password` property, because the connector uses sudo to perform actions on the SSH server. + ++ +The private key (PEM certificate) must be defined as a JSON String array. + ++ +The values of the `passphrase`, `password` and `privateKey` are encrypted when OpenIDM loads the provisioner file. + +-- + + +[#ssh-connector-config] +=== Configuring the SSH Connector + +OpenIDM provides a sample connector configuration (`provisioner.openicf-ssh.json`) in the `/path/to/openidm/samples/ssh/conf/` directory. You can copy the sample connector configuration to your project's `conf/` directory, and adjust it to match your Kerberos environment. + +Set the authentication properties, as described in xref:#ssh-authentication["Configuring Authentication to the SSH Server"]. In addition, set at least the following properties: +-- + +`host`:: +Specify the hostname or IP address of the SSH server. + +`port`:: +Set the port on which the SSH server listens. + ++ +Default: `22` + +`user`:: +The username of the account that connects to the SSH server. + ++ +This account must be able to `ssh` into the server, with the password provided in the next parameter. + +`password`:: +The password of the account that is used to connect to the SSH server. + +`prompt`:: +A string representing the remote SSH session prompt. This must be the exact prompt string, in the format `username@target:`, for example `admin@myserver:~$`. Include any trailing spaces. + +-- +The following list describes the configuration properties of the SSH connector shown in the sample connector configuration file. You can generally use the defaults provided in the sample connector configuration file, in most cases. For a complete list of all the configuration properties of the SSH connector, see xref:#configuration-properties-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0["Configuration Properties"]. +-- + +`sudoCommand`:: +A string that shows the full path to the `sudo` command, for example `/usr/bin/sudo`. + +`echoOff`:: +If set to `true` (the default), the input command echo is disabled. If set to `false`, every character that is sent to the server is sent back to the client in the `expect()` call. + +`terminalType`:: +Sets the terminal type to use for the session. The list of supported types is determined by your Linux/UNIX system. For more information, see the `terminfo` manual page (`$ man terminfo`). + ++ +Default: `vt102` + +`setLocale`:: +If set to `true`, indicates that the default environment locale should be changed to the value of the `locale` property. + ++ +Default: `false` + +`locale`:: +Sets the locale for the LC_ALL, LANG and LANGUAGE environment variables, if `setLocale` is set to `true`. + ++ +Default: `en_US.utf8` + +`connectionTimeout`:: +Specifies the connection timeout to the remote server, in milliseconds. + ++ +Default: `5000` + +`expectTimeout`:: +Specifies the timeout used by the `expect()` calls in scripts, in milliseconds. + ++ +Default: `5000` + +`authenticationType`:: +Sets the authentication type, either `PASSWORD` or `PUBKEY`. For more information, see xref:#ssh-authentication["Configuring Authentication to the SSH Server"]. + ++ +Default: `PASSWORD` + +`throwOperationTimeoutException`:: +If `true`, the connector throws an exception when the `expectTimeout` is reached for an operation. Otherwise, the operation fails silently. + ++ +Default: `true` + +`scriptRoots`:: +The path to the Groovy scripts that will perform the OpenICF operations, relative to your OpenIDM installation directory. The sample connector configuration expects the scripts in `project-dir/tools`, so this parameter is set to `&{launcher.project.location}/tools` in the sample configuration. + +`classpath`:: +The directory in which the compiler should look for compiled classes. The default classpath, if not is specified, is `install-dir/lib`. + +`reloadScriptOnExecution`:: +By default, scripts are loaded and compiled when a connector instance is created and initialized. Setting `reloadScriptOnExecution` to true makes the connector load and compile the script every time it is called. Do not set this property to `true` in a production environment, because it will have a significant impact on performance. + ++ +Default: `false` + +`*ScriptFileName`:: +The name of the Groovy script that is used for each OpenICF operation. + +-- + + +[#sec-implemented-interfaces-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0] +=== OpenICF Interfaces Implemented by the SSH Connector + +The SSH Connector implements the following OpenICF interfaces. +-- + +link:../connectors-guide/index.html#interface-AuthenticationApiOp[Authenticate]:: +Provides simple authentication with two parameters, presumed to be a user name and password. + +link:../connectors-guide/index.html#interface-CreateApiOp[Create]:: +Creates an object and its `uid`. + +link:../connectors-guide/index.html#interface-DeleteApiOp[Delete]:: +Deletes an object, referenced by its `uid`. + +link:../connectors-guide/index.html#interface-ResolveUsernameApiOp[Resolve Username]:: +Resolves an object by its username and returns the `uid` of the object. + +link:../connectors-guide/index.html#interface-SchemaApiOp[Schema]:: +Describes the object types, operations, and options that the connector supports. + +link:../connectors-guide/index.html#interface-ScriptOnConnectorApiOp[Script on Connector]:: +Enables an application to run a script in the context of the connector. Any script that runs on the connector has the following characteristics: ++ + +* The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access. + +* The script has access to a `connector` variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration. + +* The script has access to any script-arguments passed in by the application. + + +link:../connectors-guide/index.html#interface-ScriptOnResourceApiOp[Script on Resource]:: +Runs a script on the target resource that is managed by this connector. + +link:../connectors-guide/index.html#interface-SearchApiOp[Search]:: +Searches the target resource for all objects that match the specified object class and filter. + +link:../connectors-guide/index.html#interface-SyncApiOp[Sync]:: +Polls the target resource for synchronization events, that is, native changes to objects on the target resource. + +link:../connectors-guide/index.html#interface-TestApiOp[Test]:: +Tests the connector configuration. Testing a configuration checks all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid. + ++ +This operation might need to connect to a resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out). + ++ +You can invoke the test operation before a connector configuration has been validated. + +link:../connectors-guide/index.html#interface-UpdateApiOp[Update]:: +Updates (modifies or replaces) objects on a target resource. + +-- + + +[#sec-config-properties-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0] +=== SSH Connector Configuration + +The SSH Connector has the following configurable properties. + +[#configuration-properties-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0] +==== Configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#operation-script-files-properties-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0] +==== Operation Script Files Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#groovy-engine-configuration-properties-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0] +==== Groovy Engine configuration Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + +[#basic-configuration-properties-properties-org-forgerock-openicf-connectors-ssh-SSHConnector-1_4_0_0] +==== Basic Configuration Properties Properties + + +[cols="33%,17%,16%,17%,17%"] +|=== +|Property |Type |Default |Encrypted |Required +|=== + + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/chap-xml.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/chap-xml.adoc new file mode 100644 index 000000000..85e35e917 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/chap-xml.adoc @@ -0,0 +1,74 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-xml] +== XML File Connector + +OpenIDM includes a simple XML file connector. This connector is really useful only in a demonstration context and should not be used in the general provisioning of XML data stores. In real deployments, if you need to connect to a custom XML data file, you should create your own scripted connector by using the Groovy connector toolkit. + +The XML file connector is deprecated and support for its use in OpenIDM will be removed in a future release. + +[#xml-connector-config] +=== Configuring the XML File Connector + +A sample XML connector configuration is provided in `path/to/openidm/samples/provisioners/provisioner.openicf-xml.json`. The following excerpt of the provisioner configuration shows the main configurable properties: + +[source, javascript] +---- +{ + "connectorRef": { + "connectorHostRef": "#LOCAL", + "bundleName": "org.forgerock.openicf.connectors.xml-connector", + "bundleVersion": "1.1.0.3", + "connectorName": "org.forgerock.openicf.connectors.xml.XMLConnector" + } +} +---- +The `connectorHostRef` is optional if the connector server is local. + +The configuration properties for the XML file connector set the relative path to the file containing the identity data, and also the paths to the required XML schemas: + +[source, javascript] +---- +{ + "configurationProperties": { + "xsdIcfFilePath" : "&{launcher.project.location}/data/resource-schema-1.xsd", + "xsdFilePath" : "&{launcher.project.location}/data/resource-schema-extension.xsd", + "xmlFilePath" : "&{launcher.project.location}/data/xmlConnectorData.xml" + } +} +---- +`&{launcher.project.location}` refers to the project directory of your OpenIDM instance, for example, `path/to/openidm/samples/sample1`. Note that relative paths such as these work only if your connector server runs locally. For remote connector servers, you must specify the absolute path to the schema and data files. +-- + +`xsdIcfFilePath`:: +References the XSD file defining schema common to all XML file resources. Do not change the schema defined in this file. + +`xsdFilePath`:: +References custom schema defining attributes specific to your project. + +`xmlFilePath`:: +References the XML file that contains account entries. + +-- + + diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/index.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/index.adoc new file mode 100644 index 000000000..1060fad3f --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/index.adoc @@ -0,0 +1,48 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + += Connectors Guide +:doctype: book +:toc: +:authors: Lana Frost +:copyright: Copyright 2011-2017 ForgeRock AS. +:copyright: Portions Copyright 2024 3A Systems LLC. + +:imagesdir: ../ +:figure-caption!: +:example-caption!: +:table-caption!: +[abstract] +This guide describes the connectors that are supported with OpenIDM 4.5. The guide provides installation and configuration instructions for each connectors, and examples that demonstrate how to use the connectors in a deployment. + +include::./preface.adoc[] +include::./chap-overview.adoc[] +include::./chap-ldap.adoc[] +include::./chap-csv.adoc[] +include::./chap-database.adoc[] +include::./chap-powershell.adoc[] +include::./chap-groovy.adoc[] +include::./chap-sap.adoc[] +include::./chap-ssh.adoc[] +include::./chap-google.adoc[] +include::./chap-kerberos.adoc[] +include::./chap-salesforce.adoc[] +include::./chap-ad.adoc[] +include::./chap-xml.adoc[] +include::./appendix-interfaces.adoc[] +include::./appendix-options.adoc[] +include::./appendix-pooling.adoc[] diff --git a/openidm-doc/src/main/asciidoc/connectors-guide/preface.adoc b/openidm-doc/src/main/asciidoc/connectors-guide/preface.adoc new file mode 100644 index 000000000..565ce8576 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/connectors-guide/preface.adoc @@ -0,0 +1,44 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[preface] +[#preface] +== Preface + +This guide describes the OpenICF connectors that are supported in the context of an OpenIDM deployment. The guide focuses on getting the connectors installed and configured with OpenIDM. This guide does not describe all OpenICF connectors. Additional OpenICF connectors are available on the OpenICF download site but have not been tested with OpenIDM and are not described in this guide. + +[#d8013e155] +=== Who Should Use This Guide + +This guide is written for anyone using supported OpenICF connectors with OpenIDM. + +You do not need to be an OpenIDM wizard to learn something from this guide, although a background in identity management and maintaining web application software can help. You do need some background in managing services on your operating systems and in your application servers. You can nevertheless get started with this guide, and then learn more as you go along. + + + +include::../partials/sec-formatting-conventions.adoc[] + +include::../partials/sec-accessing-doc-online.adoc[] + +include::../partials/sec-joining-the-community.adoc[] + +include::../partials/sec-support-contact.adoc[] \ No newline at end of file diff --git a/openidm-doc/src/main/asciidoc/getting-started/chap-basic-install.adoc b/openidm-doc/src/main/asciidoc/getting-started/chap-basic-install.adoc new file mode 100644 index 000000000..3c2e15f95 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/getting-started/chap-basic-install.adoc @@ -0,0 +1,176 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-basic-install] +== Getting Started With OpenIDM + +Whenever you need access to important information, administrators need to know who you are. They need to know your identity, which may be distributed in multiple accounts. +As a user, you might have several accounts even within your own company, for functions such as: + +* Email + +* Human Resources + +* Payroll + +* Engineering, Support, Accounting, and other functions + +Each of these accounts may be stored in different resources, such as Active Directory, OpenDJ, OpenLDAP, and more. Keeping track of user identities in each of these resources (also known as data stores) can get complex. OpenIDM simplifies the process, as it reconciles differences between resources. + +With situational policies, OpenIDM can handle discrepancies such as a missing or updated address for a specific user. OpenIDM includes default but configurable policies to handle such conditions. In this way, OpenIDM ensures consistency and predictability in an otherwise chaotic resource environment. + +OpenIDM can make it easier to track user identities across these resources. OpenIDM has a highly scalable, modular, readily deployable architecture that can help you manage workflows and user information. + +[#gsg-what-openidm-cando] +=== What Can You Do With OpenIDM? + +With OpenIDM, you can simplify the management of identity, as it can help you synchronize data across multiple resources. Each organization can maintain control of accounts within their respective domains. + +OpenIDM works equally well with user, group, and device identities. + +You can also configure workflows to help users manage how they sign up for accounts, as part of how OpenIDM manages the life cycle of users and their accounts. + +You can manage employee identities as they move from job to job. You will make their lives easier as OpenIDM can automatically register user accounts on different systems. Later, OpenIDM will increase productivity when it reconciles information from different accounts, saving users the hassle of entering the same information on different systems. + + +[#gsg-learning] +=== What You Will See In This Document + +In this guide, you will see how OpenIDM reconciles user data between two data stores. We will look at a department that is adding a third engineer, Jane Sanchez. + +Your Human Resources department has updated their data store with Jane Sanchez's information. You want to use OpenIDM to update the internal Engineering data store. But first, you have to start OpenIDM. + + +[#before-you-start] +=== What You Need Before Starting OpenIDM + +This section covers what you need to have on your system before running OpenIDM: + +* Operating System: Windows or UNIX/Linux. + +* Java: Java Runtime Environment (JRE) Standard Edition (Java SE) 7, update 6 or later, or Java 8. Alternatively, you can use the same version of the Java Development Kit (JDK). On Linux, you may also install the OpenJDK package native to your updated Linux distribution. + +* At least 250 MB of free disk space. + +* At least 1 GB of free RAM. + +* If your operating system includes a firewall, make sure that it allows traffic through (default) ports 8080 and 8443. + +We provide this document, __Getting Started with OpenIDM__, for demonstration purposes only. + +With this document, we want to make it as easy as possible to set up a demonstration of OpenIDM. To that end, we have written this document for installations on a desktop operating system, Microsoft Windows 7. + +[#java-prerequisites] +==== Java Environment + +On Windows systems, after installing Java, set the `JAVA_HOME` environment variable. To do so on Windows 7, take the following steps: + +==== + +. Locate your JRE or JDK installation directory. For a default installation of Java 8 on Windows 7, you should find the directory here: `C:\Program Files\Java\jre-version`. + +. Select Start > Control Panel > System and Security > Advanced System Settings to open a System Properties window. + +. Select Advanced > Environment Variables. + +. Set the value of `JAVA_HOME` to match the JRE or JDK installation directory. + +==== + + + +[#download-and-start] +=== Downloading and Starting OpenIDM + + +[#download-start-openidm] +==== +This procedure assumes that you are downloading and starting OpenIDM as a regular (not administrative) user named `user`. + +. Download enterprise software releases through the ForgeRock link:https://backstage.forgerock.com/[BackStage, window=\_blank] site. ForgeRock enterprise releases are thoroughly validated builds for ForgeRock customers who run OpenIDM in production deployments, and for those who want to try or test with release builds. ++ +For more information on the contents of the OpenIDM binary package, see xref:../integrators-guide/appendix-file-layout.adoc#appendix-file-layout["File Layout"] in the __Integrator's Guide__. + +. Extract the contents of the OpenIDM binary file to your user's `Downloads` directory. The process should unpack the contents of OpenIDM to the `Downloads/openidm` subdirectory. + +. Navigate to the `Downloads/openidm` subdirectory: ++ + +* In Microsoft Windows, use Windows Explorer to navigate to the `C:\Users\user\Downloads\openidm` directory. ++ +Double-click the `getting-started(.bat)` file. Do not select the `getting-started.sh` file, as that is intended for use on UNIX/Linux systems. + +* In Linux/UNIX, open a command-line interface and run the following commands: ++ + +[source, console] +---- +$ cd /home/user/Downloads/openidm +$ ./getting-started.sh +---- + + +. When OpenIDM is ready, you will see the following message: ++ + +[source, console] +---- +-> OpenIDM ready +---- + +==== +Once OpenIDM is ready, you can administer it from a web browser. To do so, navigate to `\http://localhost:8080/admin` or `\https://localhost:8443/admin`. If you have installed OpenIDM on a remote system, substitute that hostname or IP address for `localhost`. + +[#d7821e389] +image::images/gsg-windows.png[] + +[NOTE] +==== +We recommend that you connect to OpenIDM via the default secure port, 8443, and import a signed certificate into the OpenIDM truststore, as discussed in xref:../integrators-guide/chap-security.adoc#security-management-service["Accessing the Security Management Service"] in the __Integrator's Guide__. + +Until you install that certificate, you will see a warning in your browser at least the first time you access OpenIDM over a secure port. +==== +The default username and password for the OpenIDM Administrator is `openidm-admin` and `openidm-admin`. + +When you log into OpenIDM at a URL with the `/admin` endpoint, you are logging into the OpenIDM Administrative User Interface, also known as the Admin UI. + +[WARNING] +==== +The default password for the OpenIDM administrative user, `openidm-admin`, is `openidm-admin`. To protect your deployment in production, change this password. +==== +All users, including `openidm-admin`, can change their password through the Self-Service UI, at `\http://localhost:8080/` or `\https://localhost:8443/`. Once logged in, click Profile > Password. + + +[#gsg-data-files] +=== The Getting Started Data Files + +In a production deployment, you are likely to see resources like Active Directory and OpenDJ. But the setup requirements for each are extensive, and beyond the scope of this document. + +For simplicity, this guide uses two static files as data stores: + +* `hr.csv` represents the Human Resources data store. It is in CSV format, commonly used to share data between spreadsheet applications. + +* `engineering.xml` represents the Engineering data store. It is in XML format, a generic means for storing complex data that is commonly used over the Internet. + +You can find these files in the OpenIDM binary package that you downloaded earlier, in the following subdirectory: `openidm/samples/getting-started/data`. + + diff --git a/openidm-doc/src/main/asciidoc/getting-started/chap-openidm-demo.adoc b/openidm-doc/src/main/asciidoc/getting-started/chap-openidm-demo.adoc new file mode 100644 index 000000000..8cce192fc --- /dev/null +++ b/openidm-doc/src/main/asciidoc/getting-started/chap-openidm-demo.adoc @@ -0,0 +1,84 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-openidm-demo] +== Reconciling Identity Data + +Now that you have installed OpenIDM with a "Getting Started" configuration, you will learn how OpenIDM reconciles information between two data stores. + +While the reconciliation demonstrated in this guide uses two simplified data files, you can set up the same operations at an enterprise level on a variety of resources. + +Return to the situation described earlier, where you have Jane Sanchez joining the engineering department. The following illustration depicts what OpenIDM has to do to reconcile the differences. + +[#d7821e475] +image::images/gsg-recon-top.png[] + +[#data-files] +=== Using OpenIDM to Reconcile Differences + +A central feature of OpenIDM is reconciliation. In other words, OpenIDM can compare the contents of two data stores, and make decisions on what to do, depending on the differences. + +This scenario is based on two data files: + +* `hr.csv`, which represents the Human Resources data store + +* `engineering.xml`, which represents the Engineering data store + +OpenIDM will modify the Engineering data store by adding the newly hired Jane Sanchez. As suggested by the following illustration, it will also address detailed differences between Jane's Human Resources account and the Engineering data store. + +[#d7821e506] +image::images/gsg-differences.png[] +OpenIDM includes configuration files that map detailed information from the Human Resources data store to the Engineering data store. For example, the OpenIDM configuration maps the `firstName` entry in Human Resources to the `firstname` entry in Engineering. + +[NOTE] +==== +Mapping between data stores may require additional configuration. You should find two `provisioner.openicf-*.json` files in the `/path/to/openidm/samples/getting-started/conf` subdirectory. The provisioner files configure connections to external resources, such as Active Directory, OpenDJ or even the `engineering.xml` and `hr.csv` files used in this guide. For more information, see xref:../integrators-guide/chap-resource-conf.adoc#chap-resource-conf["Connecting to External Resources"] in the __Integrator's Guide__. +==== +In the Admin UI, you can see how OpenIDM reconciles the different categories for user Jane Sanchez. Log in to the Admin UI at `\https://localhost:8443/admin`. The default username is `openidm-admin` and default password is `openidm-admin`. + +Select Configure > Mappings > `HumanResources_Engineering` > Properties. + +In the Sample Source text box, enter `Sanchez`. You should see a drop-down entry for Jane Sanchez that you can select. You should now see how OpenIDM would reconcile Jane Sanchez's entry in the Human Resources data store into the Engineering data store. + +[#d7821e563] +image::images/gsg-recon-details.png[] +Scroll back up the same page. Select Reconcile Now. + +When you reconcile the two data stores, OpenIDM will make the change to the Engineering data store. + +For those of you who prefer the command-line interface, you can see how the mapping works in the `sync.json` file, in the `/path/to/openidm/samples/getting-started/conf` directory. + + +[#gsg-try-one-change] +=== Reconciling Identity Data After One Update + +Now that you have used OpenIDM to reconcile two data stores, try something else. Assume the Engineering organization wants to overwrite all user telephone numbers in its employee data store with one central telephone number. + +For this purpose, you can set up a default telephone number for the next reconciliation. + +In the HumanResources_Engineering page, scroll down and select `telephoneNumber` > Default Values. + +[#d7821e598] +image::images/gsg-telephone.png[] +When you select Update, and Save Properties, OpenIDM changes the `sync.json` configuration file. The next time OpenIDM reconciles from Human Resources to Engineering, it will include that default telephone number for all employees in the Engineering group. + + diff --git a/openidm-doc/src/main/asciidoc/getting-started/chap-where-to-go.adoc b/openidm-doc/src/main/asciidoc/getting-started/chap-where-to-go.adoc new file mode 100644 index 000000000..6b27d324c --- /dev/null +++ b/openidm-doc/src/main/asciidoc/getting-started/chap-where-to-go.adoc @@ -0,0 +1,198 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-where-to-go] +== Where To Go From Here + +OpenIDM can do much more than reconcile data between two different sources. In this chapter, you will read about the key features of OpenIDM, with links to additional information about each feature. + +[#gsg-bpmn] +=== Integrating Business Processes and Workflows + +A business process begins with an objective and includes a well-defined sequence of tasks to meet that objective. In OpenIDM, you can configure many of these tasks as self-service workflows, such as self-registration, new user onboarding, and account certification. + +With OpenIDM, you can automate many of these tasks as a workflow. + +Once you configure the right workflows, a newly hired engineer can log into OpenIDM and request access to manufacturing information. + +That request is sent to the appropriate manager for approval. Once approved, the OpenIDM provisions the new engineer with access to manufacturing. + +OpenIDM supports workflow-driven provisioning activities, based on the embedded link:http://activiti.org[Activiti, window=\_blank] Process Engine, which complies with the link:http://www.omg.org/spec/BPMN/2.0/[Business Process Model and Notation 2.0, window=\_blank] (BPMN 2.0) standard. + +OpenIDM integrates additional workflows such as new user onboarding, orphan account detection, and password change reminders. For more information, see xref:../samples-guide/chap-workflow-samples.adoc#chap-workflow-samples["Workflow Samples"] in the __Samples Guide__. + + +[#gsg-passwords] +=== Managing Passwords + +You can manage passwords from the Self-Service User Interface, also known as the Self-Service UI. From the Admin UI, click on the icon in the upper-right corner. In the menu that appears, click Self-Service: + +[#d7821e651] +image::images/gsg-selfservice-menu.png[] +You should now be in the Self-Service UI. Click Profile > Password. You can now change your password, subject to the policy limits shown. + +[#d7821e664] +image::images/gsg-password.png[] +As you can see, OpenIDM supports a robust password policy. You can modify the rules shown, or add more rules such as the following: + +* Elements that should not be a part of a password, such as a family name + +* Password expiration dates + +* Password histories, to prevent password reuse + +For more information, see xref:../integrators-guide/chap-passwords.adoc#chap-passwords["Managing Passwords"] in the __Integrator's Guide__. + + +[#gsg-role-management] +=== Managing User Roles + +Some users need accounts on multiple systems. For example, insurance agents may also have insurance policies with the company that they work for. In that situation, the insurance agent is also a customer of the company. + +Alternatively, a salesperson may also test customer engineering scenarios. That salesperson may also need access to engineering systems. + +In OpenIDM, each of these user scenarios is known as a __role__. OpenIDM allows you to set up a consolidated set of attributes associated with each role. To do so, you would configure custom roles to assign to selected users. For example, you may assign both __insured__ and __agent__ roles to an agent, while assigning the __insured__ role to all customers. + +In a similar fashion, OpenIDM allows you to assign both __sales__ and __engineering__ roles to the sales engineer. + +You can then synchronize users with those roles into appropriate data stores. + +For more information, see xref:../integrators-guide/chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"] in the __Integrator's Guide__. For a sample of how you can configure external roles within OpenIDM, see xref:../samples-guide/chap-roles-sample.adoc#chap-roles-sample["Roles Samples - Demonstrating the OpenIDM Roles Implementation"] in the __Samples Guide__. + + +[#gsg-connectors] +=== Connecting to Remote Data Stores + +You can use OpenIDM to connect to a substantial variety of user and device data stores, on premise and in the cloud. While OpenIDM can connect to some connectors dedicated to a few data stores, OpenIDM can also connect to many more data stores using a scripted connector framework. +OpenIDM includes support for connectors to the following external resources: + +* Google Web Applications (see xref:../connectors-guide/chap-google.adoc#chap-google["Google Apps Connector"] in the __Connectors Guide__). + +* Salesforce (see xref:../connectors-guide/chap-salesforce.adoc#chap-salesforce["Salesforce Connector"] in the __Connectors Guide__). + +* Any LDAPv3-compliant directory, including link:../../../opendj/3.5/install-guide[OpenDJ, window=\_blank] and Active Directory (see xref:../connectors-guide/chap-ldap.adoc#chap-ldap["Generic LDAP Connector"] in the __Connectors Guide__). + +* CSV Files (see xref:../connectors-guide/chap-csv.adoc#chap-csv["CSV File Connector"] in the __Connectors Guide__). + +* Database Tables (see xref:../connectors-guide/chap-database.adoc#chap-database["Database Table Connector"] in the __Connectors Guide__). + +If the resource that you need is not on the list, you should be able to use one of the OpenIDM scripted connector frameworks to connect to that resource: + +* For connectors associated with Microsoft Windows, OpenIDM includes a PowerShell Connector Toolkit that you can use to provision a variety of Microsoft services, including but not limited to Active Directory, SQL Server, Microsoft Exchange, SharePoint, Azure Active Directory, and Office 365. For more information, see xref:../connectors-guide/chap-powershell.adoc#chap-powershell["PowerShell Connector Toolkit"] in the __Connectors Guide__. OpenIDM includes a sample PowerShell Connector Toolkit configuration, described in xref:../samples-guide/chap-powershell-samples.adoc#chap-powershell-samples["Samples That Use the PowerShell Connector Toolkit to Create Scripted Connectors"] in the __Samples Guide__. + +* For other external resources, OpenIDM includes a Groovy Connector Toolkit that allows you to run Groovy scripts to interact with any external resource. For more information, see xref:../connectors-guide/chap-groovy.adoc#chap-groovy["Groovy Connector Toolkit"] in the __Connectors Guide__. xref:../samples-guide/chap-groovy-samples.adoc#chap-groovy-samples["Samples That Use the Groovy Connector Toolkit to Create Scripted Connectors"] in the __Samples Guide__ includes samples of how you might implement the scripted Groovy connector. + + + +[#gsg-recon] +=== Reconciliation + +OpenIDM supports reconciliation between two data stores, as a source and a target. + +In identity management, reconciliation compares the contents of objects in different data stores, and makes decisions based on configurable policies. + +For example, if you have an application that maintains its own user store, OpenIDM can ensure your canonical directory attributes are kept up to date by reconciling their values as they are changed. + +For more information, see xref:../integrators-guide/chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"] in the __Integrator's Guide__. + + +[#gsg-auth-modules] +=== Authentication Modules Available for OpenIDM + +OpenIDM has access to several different authentication modules that can help you protect your systems. For more information, see xref:../integrators-guide/chap-auth.adoc#supported-auth-session-modules["Supported Authentication and Session Modules"] in the __Integrator's Guide__. + + +[#gsg-usecases] +=== Finding Additional Use Cases + +OpenIDM is a lightweight and highly customizable identity management product. + +The OpenIDM documentation includes additional use cases. Most of them are known as __Samples__, and are described in xref:../samples-guide/chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"] in the __Samples Guide__. + +These samples include step-by-step instructions on how you can connect to different data stores, customize product behavior using JavaScript and Groovy, and administer OpenIDM with ForgeRock's commons RESTful API commands. + + +[#gsg-integration] +=== How OpenIDM Can Help Your Organization + +Now that you have seen how OpenIDM can help you manage users, review the features that OpenIDM can bring to your organization: + +* __Web-Based Administrative User Interface__ ++ +Configure OpenIDM with the Web-Based Administrative User Interface. You can configure many major components of OpenIDM without ever touching a text configuration file. + +* __Self-Service Functionality__ ++ +User self-service features can streamline onboarding, account certification, new user registration, username recovery, and password reset. OpenIDM self-service features are built upon a link:http://www.omg.org/spec/BPMN/2.0/[BPMN 2.0-compliant workflow engine., window=\_blank] + +* __Role-Based Provisioning__ ++ +Create and manage users based on attributes such as organizational need, job function, and geographic location. + +* __Backend Flexibility__ ++ +Choose the desired backend database for your deployment. OpenIDM supports MySQL, Microsoft SQL Server, Oracle Database, IBM DB2, and PostgreSQL. + +* __Password Management__ ++ +Set up fine-grained control of passwords to ensure consistent password policies across all applications and data stores. Supports separate passwords per external resource. + +* __Logging, Auditing, and Reporting__ ++ +OpenIDM logs all activity, internally and within connected systems. With such logs, you can track information for access, activity, authentication, configuration, reconciliation, and synchronization. + +* __Access to External Resources__ ++ +OpenIDM can access a generic scripted connector that allows you to set up communications with many external data stores. + + + +[#stop-and-remove] +=== Stopping and Removing OpenIDM + +Follow these steps to stop and remove OpenIDM. + +[#going-further] +==== + +. To stop OpenIDM, return to the console window where you saw the following message: ++ + +[source, console] +---- +-> OpenIDM ready +---- ++ +Press Return, and enter the following command: ++ + +[source, console] +---- +-> shutdown +---- + +. OpenIDM is self-contained. After you shut down OpenIDM, you can choose to delete the files in the `/path/to/openidm` directory. OpenIDM includes no artifacts in system registries or elsewhere. + +==== +We hope that you want to continue exploring OpenIDM. To do so, review the rest of the link:../../../openidm/4.5[OpenIDM documentation, window=\_blank]. + + diff --git a/openidm-doc/src/main/asciidoc/getting-started/index.adoc b/openidm-doc/src/main/asciidoc/getting-started/index.adoc new file mode 100644 index 000000000..7ccd61dee --- /dev/null +++ b/openidm-doc/src/main/asciidoc/getting-started/index.adoc @@ -0,0 +1,35 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + += Getting Started +:doctype: book +:toc: +:authors: Mike Jang +:copyright: Copyright 2015-2017 ForgeRock AS. +:copyright: Portions Copyright 2024 3A Systems LLC. + +:imagesdir: ../ +:figure-caption!: +:example-caption!: +:table-caption!: +[abstract] +Guide to installing and evaluating OpenIDM. The OpenIDM project offers flexible, open source services for automating management of the identity life cycle. + +include::./preface.adoc[] +include::./chap-basic-install.adoc[] +include::./chap-openidm-demo.adoc[] +include::./chap-where-to-go.adoc[] diff --git a/openidm-doc/src/main/asciidoc/getting-started/preface.adoc b/openidm-doc/src/main/asciidoc/getting-started/preface.adoc new file mode 100644 index 000000000..4d6c03997 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/getting-started/preface.adoc @@ -0,0 +1,47 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[preface] +[#preface] +== Preface + +This guide shows you how to install and get started with OpenIDM. + +[#d7821e154] +=== Who Should Use This Guide + +This guide is written for identity management developers and administrators who build, deploy, and maintain OpenIDM services for their organizations. This guide covers the tasks you need to quickly get OpenIDM running on your system. + +As you read this guide, you will see how OpenIDM reconciles customer identity data to ensure accurate information across disparate resources within an organization. + +You will also read about what else OpenIDM can do, in the areas of provisioning, self-service workflows, and password management. You will also read about how OpenIDM connects to a variety of remote data stores, with links to detailed documentation. + +For example, engineers might access their systems through Active Directory accounts. Those same engineers might need to update their information in a Human Resources database, stored in a separate LDAP directory. With OpenIDM, you can keep those user identities synchronized, so each engineer only has to update their data once. + + +include::../partials/sec-accessing-doc-online.adoc[] + +include::../partials/sec-joining-the-community.adoc[] + +include::../partials/sec-support-contact.adoc[] + + diff --git a/openidm-doc/src/main/asciidoc/images/OpenICFArch.png b/openidm-doc/src/main/asciidoc/images/OpenICFArch.png new file mode 100644 index 000000000..7cbbdb677 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/OpenICFArch.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ServiceTree.png b/openidm-doc/src/main/asciidoc/images/ServiceTree.png new file mode 100644 index 000000000..d0893177d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ServiceTree.png differ diff --git a/openidm-doc/src/main/asciidoc/images/active-sf-connector.png b/openidm-doc/src/main/asciidoc/images/active-sf-connector.png new file mode 100644 index 000000000..1f9ed63e6 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/active-sf-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/admin-ui-authentication.png b/openidm-doc/src/main/asciidoc/images/admin-ui-authentication.png new file mode 100644 index 000000000..e52f0db66 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/admin-ui-authentication.png differ diff --git a/openidm-doc/src/main/asciidoc/images/admin-ui-corr-script.png b/openidm-doc/src/main/asciidoc/images/admin-ui-corr-script.png new file mode 100644 index 000000000..ce5f9dd15 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/admin-ui-corr-script.png differ diff --git a/openidm-doc/src/main/asciidoc/images/admin-ui-mappings-sample1.png b/openidm-doc/src/main/asciidoc/images/admin-ui-mappings-sample1.png new file mode 100644 index 000000000..0b1344d2e Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/admin-ui-mappings-sample1.png differ diff --git a/openidm-doc/src/main/asciidoc/images/adminui-dashboard.png b/openidm-doc/src/main/asciidoc/images/adminui-dashboard.png new file mode 100644 index 000000000..3b8b75d7c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/adminui-dashboard.png differ diff --git a/openidm-doc/src/main/asciidoc/images/approval-task.png b/openidm-doc/src/main/asciidoc/images/approval-task.png new file mode 100644 index 000000000..39d015e02 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/approval-task.png differ diff --git a/openidm-doc/src/main/asciidoc/images/audit-event-topic.png b/openidm-doc/src/main/asciidoc/images/audit-event-topic.png new file mode 100644 index 000000000..be708e478 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/audit-event-topic.png differ diff --git a/openidm-doc/src/main/asciidoc/images/auth-iwa-module.png b/openidm-doc/src/main/asciidoc/images/auth-iwa-module.png new file mode 100644 index 000000000..3f08943fc Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/auth-iwa-module.png differ diff --git a/openidm-doc/src/main/asciidoc/images/bundle-version.png b/openidm-doc/src/main/asciidoc/images/bundle-version.png new file mode 100644 index 000000000..ab3501ba3 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/bundle-version.png differ diff --git a/openidm-doc/src/main/asciidoc/images/conditional-role.png b/openidm-doc/src/main/asciidoc/images/conditional-role.png new file mode 100644 index 000000000..0fad0de42 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/conditional-role.png differ diff --git a/openidm-doc/src/main/asciidoc/images/consumer-key.png b/openidm-doc/src/main/asciidoc/images/consumer-key.png new file mode 100644 index 000000000..c94c865c3 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/consumer-key.png differ diff --git a/openidm-doc/src/main/asciidoc/images/db2-kerberos.png b/openidm-doc/src/main/asciidoc/images/db2-kerberos.png new file mode 100644 index 000000000..d0ebfc08b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/db2-kerberos.png differ diff --git a/openidm-doc/src/main/asciidoc/images/dj-control-panel.png b/openidm-doc/src/main/asciidoc/images/dj-control-panel.png new file mode 100644 index 000000000..77a415ce3 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/dj-control-panel.png differ diff --git a/openidm-doc/src/main/asciidoc/images/dotnet-service.png b/openidm-doc/src/main/asciidoc/images/dotnet-service.png new file mode 100644 index 000000000..8ec211796 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/dotnet-service.png differ diff --git a/openidm-doc/src/main/asciidoc/images/edit-sf-connector.png b/openidm-doc/src/main/asciidoc/images/edit-sf-connector.png new file mode 100644 index 000000000..15b1a8d82 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/edit-sf-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/elastic-audit.png b/openidm-doc/src/main/asciidoc/images/elastic-audit.png new file mode 100644 index 000000000..6adbd4447 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/elastic-audit.png differ diff --git a/openidm-doc/src/main/asciidoc/images/expression-builder.png b/openidm-doc/src/main/asciidoc/images/expression-builder.png new file mode 100644 index 000000000..e396bce76 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/expression-builder.png differ diff --git a/openidm-doc/src/main/asciidoc/images/fullstack-openam-users.png b/openidm-doc/src/main/asciidoc/images/fullstack-openam-users.png new file mode 100644 index 000000000..464ea9b1b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/fullstack-openam-users.png differ diff --git a/openidm-doc/src/main/asciidoc/images/generic-tables-erd.png b/openidm-doc/src/main/asciidoc/images/generic-tables-erd.png new file mode 100644 index 000000000..31c8faa74 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/generic-tables-erd.png differ diff --git a/openidm-doc/src/main/asciidoc/images/google-apps-active.png b/openidm-doc/src/main/asciidoc/images/google-apps-active.png new file mode 100644 index 000000000..9b7d7ae58 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/google-apps-active.png differ diff --git a/openidm-doc/src/main/asciidoc/images/google-apps-allow.png b/openidm-doc/src/main/asciidoc/images/google-apps-allow.png new file mode 100644 index 000000000..91914c3a0 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/google-apps-allow.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-differences.png b/openidm-doc/src/main/asciidoc/images/gsg-differences.png new file mode 100644 index 000000000..cc6fba81a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-differences.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-password.png b/openidm-doc/src/main/asciidoc/images/gsg-password.png new file mode 100644 index 000000000..867dd607c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-password.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-recon-details.png b/openidm-doc/src/main/asciidoc/images/gsg-recon-details.png new file mode 100644 index 000000000..2cdf6b434 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-recon-details.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-recon-top.png b/openidm-doc/src/main/asciidoc/images/gsg-recon-top.png new file mode 100644 index 000000000..09f30b874 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-recon-top.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-selfservice-menu.png b/openidm-doc/src/main/asciidoc/images/gsg-selfservice-menu.png new file mode 100644 index 000000000..3e0e1c443 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-selfservice-menu.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-telephone.png b/openidm-doc/src/main/asciidoc/images/gsg-telephone.png new file mode 100644 index 000000000..09a585307 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-telephone.png differ diff --git a/openidm-doc/src/main/asciidoc/images/gsg-windows.png b/openidm-doc/src/main/asciidoc/images/gsg-windows.png new file mode 100644 index 000000000..1ecd75028 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/gsg-windows.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ha-cluster-config.png b/openidm-doc/src/main/asciidoc/images/ha-cluster-config.png new file mode 100644 index 000000000..beed7ccca Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ha-cluster-config.png differ diff --git a/openidm-doc/src/main/asciidoc/images/kerberos-auth.png b/openidm-doc/src/main/asciidoc/images/kerberos-auth.png new file mode 100644 index 000000000..df489e363 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/kerberos-auth.png differ diff --git a/openidm-doc/src/main/asciidoc/images/link-qualifier-script.png b/openidm-doc/src/main/asciidoc/images/link-qualifier-script.png new file mode 100644 index 000000000..7cf3ade5d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/link-qualifier-script.png differ diff --git a/openidm-doc/src/main/asciidoc/images/link-qualifier.png b/openidm-doc/src/main/asciidoc/images/link-qualifier.png new file mode 100644 index 000000000..88d93a988 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/link-qualifier.png differ diff --git a/openidm-doc/src/main/asciidoc/images/new-app-data.png b/openidm-doc/src/main/asciidoc/images/new-app-data.png new file mode 100644 index 000000000..c88d3b8da Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/new-app-data.png differ diff --git a/openidm-doc/src/main/asciidoc/images/new-user.png b/openidm-doc/src/main/asciidoc/images/new-user.png new file mode 100644 index 000000000..c46a1cdc1 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/new-user.png differ diff --git a/openidm-doc/src/main/asciidoc/images/oauth-credentials.png b/openidm-doc/src/main/asciidoc/images/oauth-credentials.png new file mode 100644 index 000000000..a5f8b403a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/oauth-credentials.png differ diff --git a/openidm-doc/src/main/asciidoc/images/openam-auth-adv.png b/openidm-doc/src/main/asciidoc/images/openam-auth-adv.png new file mode 100644 index 000000000..5b21c3dbf Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/openam-auth-adv.png differ diff --git a/openidm-doc/src/main/asciidoc/images/openam-auth-basic.png b/openidm-doc/src/main/asciidoc/images/openam-auth-basic.png new file mode 100644 index 000000000..5d61787e1 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/openam-auth-basic.png differ diff --git a/openidm-doc/src/main/asciidoc/images/openam-djconn.png b/openidm-doc/src/main/asciidoc/images/openam-djconn.png new file mode 100644 index 000000000..b9f495fb6 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/openam-djconn.png differ diff --git a/openidm-doc/src/main/asciidoc/images/openidm-admin-role.png b/openidm-doc/src/main/asciidoc/images/openidm-admin-role.png new file mode 100644 index 000000000..a6a50e474 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/openidm-admin-role.png differ diff --git a/openidm-doc/src/main/asciidoc/images/openidm2-architecture.png b/openidm-doc/src/main/asciidoc/images/openidm2-architecture.png new file mode 100644 index 000000000..f625c1088 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/openidm2-architecture.png differ diff --git a/openidm-doc/src/main/asciidoc/images/password-reset-steps.png b/openidm-doc/src/main/asciidoc/images/password-reset-steps.png new file mode 100644 index 000000000..00019ed42 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/password-reset-steps.png differ diff --git a/openidm-doc/src/main/asciidoc/images/profile-kba-questions.png b/openidm-doc/src/main/asciidoc/images/profile-kba-questions.png new file mode 100644 index 000000000..010486398 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/profile-kba-questions.png differ diff --git a/openidm-doc/src/main/asciidoc/images/provisioning-add-user.png b/openidm-doc/src/main/asciidoc/images/provisioning-add-user.png new file mode 100644 index 000000000..a374d3c39 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/provisioning-add-user.png differ diff --git a/openidm-doc/src/main/asciidoc/images/provisioning-dashboard.png b/openidm-doc/src/main/asciidoc/images/provisioning-dashboard.png new file mode 100644 index 000000000..5f02fb521 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/provisioning-dashboard.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ps-azure-add-directory.png b/openidm-doc/src/main/asciidoc/images/ps-azure-add-directory.png new file mode 100644 index 000000000..06acd4f41 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ps-azure-add-directory.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ps-azure-add-user.png b/openidm-doc/src/main/asciidoc/images/ps-azure-add-user.png new file mode 100644 index 000000000..16bad91df Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ps-azure-add-user.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ps-azure-credentials.png b/openidm-doc/src/main/asciidoc/images/ps-azure-credentials.png new file mode 100644 index 000000000..c5550ab86 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ps-azure-credentials.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ps-azure-user-pwd.png b/openidm-doc/src/main/asciidoc/images/ps-azure-user-pwd.png new file mode 100644 index 000000000..08ea35b81 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ps-azure-user-pwd.png differ diff --git a/openidm-doc/src/main/asciidoc/images/reconstats-dashboard.png b/openidm-doc/src/main/asciidoc/images/reconstats-dashboard.png new file mode 100644 index 000000000..e1164c8ad Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/reconstats-dashboard.png differ diff --git a/openidm-doc/src/main/asciidoc/images/relationships-graph.png b/openidm-doc/src/main/asciidoc/images/relationships-graph.png new file mode 100644 index 000000000..7eacdded3 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/relationships-graph.png differ diff --git a/openidm-doc/src/main/asciidoc/images/remote-csv.png b/openidm-doc/src/main/asciidoc/images/remote-csv.png new file mode 100644 index 000000000..4a55cfd14 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/remote-csv.png differ diff --git a/openidm-doc/src/main/asciidoc/images/roles-auth.png b/openidm-doc/src/main/asciidoc/images/roles-auth.png new file mode 100644 index 000000000..53219cd5f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/roles-auth.png differ diff --git a/openidm-doc/src/main/asciidoc/images/salesforce-bjensen.png b/openidm-doc/src/main/asciidoc/images/salesforce-bjensen.png new file mode 100644 index 000000000..67ead20b8 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/salesforce-bjensen.png differ diff --git a/openidm-doc/src/main/asciidoc/images/salesforce-connector.png b/openidm-doc/src/main/asciidoc/images/salesforce-connector.png new file mode 100644 index 000000000..b141932df Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/salesforce-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/salesforce-mappings.png b/openidm-doc/src/main/asciidoc/images/salesforce-mappings.png new file mode 100644 index 000000000..cc012b2c5 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/salesforce-mappings.png differ diff --git a/openidm-doc/src/main/asciidoc/images/salesforce-reconcile.png b/openidm-doc/src/main/asciidoc/images/salesforce-reconcile.png new file mode 100644 index 000000000..d97f4ab4f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/salesforce-reconcile.png differ diff --git a/openidm-doc/src/main/asciidoc/images/salesforce-users.png b/openidm-doc/src/main/asciidoc/images/salesforce-users.png new file mode 100644 index 000000000..019f3be48 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/salesforce-users.png differ diff --git a/openidm-doc/src/main/asciidoc/images/sample2-mappings.png b/openidm-doc/src/main/asciidoc/images/sample2-mappings.png new file mode 100644 index 000000000..590ea0d08 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/sample2-mappings.png differ diff --git a/openidm-doc/src/main/asciidoc/images/sample2d-groups.png b/openidm-doc/src/main/asciidoc/images/sample2d-groups.png new file mode 100644 index 000000000..8ace9ee6f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/sample2d-groups.png differ diff --git a/openidm-doc/src/main/asciidoc/images/self-service-ui.png b/openidm-doc/src/main/asciidoc/images/self-service-ui.png new file mode 100644 index 000000000..3358fac57 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/self-service-ui.png differ diff --git a/openidm-doc/src/main/asciidoc/images/service-acct.png b/openidm-doc/src/main/asciidoc/images/service-acct.png new file mode 100644 index 000000000..9f59c04f8 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/service-acct.png differ diff --git a/openidm-doc/src/main/asciidoc/images/sql-tables.png b/openidm-doc/src/main/asciidoc/images/sql-tables.png new file mode 100644 index 000000000..c8e447170 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/sql-tables.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ssh-connector.png b/openidm-doc/src/main/asciidoc/images/ssh-connector.png new file mode 100644 index 000000000..25f7fdc96 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ssh-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/temporal-role.png b/openidm-doc/src/main/asciidoc/images/temporal-role.png new file mode 100644 index 000000000..82b553210 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/temporal-role.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_OpenICFArch.png b/openidm-doc/src/main/asciidoc/images/thumb_OpenICFArch.png new file mode 100644 index 000000000..95f11bda2 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_OpenICFArch.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ServiceTree.png b/openidm-doc/src/main/asciidoc/images/thumb_ServiceTree.png new file mode 100644 index 000000000..da1372f31 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ServiceTree.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_active-sf-connector.png b/openidm-doc/src/main/asciidoc/images/thumb_active-sf-connector.png new file mode 100644 index 000000000..7799c9aad Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_active-sf-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-authentication.png b/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-authentication.png new file mode 100644 index 000000000..98cad2cea Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-authentication.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-corr-script.png b/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-corr-script.png new file mode 100644 index 000000000..e6231e7d7 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-corr-script.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-mappings-sample1.png b/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-mappings-sample1.png new file mode 100644 index 000000000..ae654c70e Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_admin-ui-mappings-sample1.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_adminui-dashboard.png b/openidm-doc/src/main/asciidoc/images/thumb_adminui-dashboard.png new file mode 100644 index 000000000..a660a378b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_adminui-dashboard.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_approval-task.png b/openidm-doc/src/main/asciidoc/images/thumb_approval-task.png new file mode 100644 index 000000000..2a452bdf0 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_approval-task.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_audit-event-topic.png b/openidm-doc/src/main/asciidoc/images/thumb_audit-event-topic.png new file mode 100644 index 000000000..efcbc2a95 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_audit-event-topic.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_auth-iwa-module.png b/openidm-doc/src/main/asciidoc/images/thumb_auth-iwa-module.png new file mode 100644 index 000000000..ba8a7f29b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_auth-iwa-module.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_bundle-version.png b/openidm-doc/src/main/asciidoc/images/thumb_bundle-version.png new file mode 100644 index 000000000..6ca8ac3e8 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_bundle-version.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_conditional-role.png b/openidm-doc/src/main/asciidoc/images/thumb_conditional-role.png new file mode 100644 index 000000000..c56665983 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_conditional-role.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_consumer-key.png b/openidm-doc/src/main/asciidoc/images/thumb_consumer-key.png new file mode 100644 index 000000000..473f6a5fb Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_consumer-key.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_db2-kerberos.png b/openidm-doc/src/main/asciidoc/images/thumb_db2-kerberos.png new file mode 100644 index 000000000..d0ebfc08b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_db2-kerberos.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_dj-control-panel.png b/openidm-doc/src/main/asciidoc/images/thumb_dj-control-panel.png new file mode 100644 index 000000000..dde1a69ed Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_dj-control-panel.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_dotnet-service.png b/openidm-doc/src/main/asciidoc/images/thumb_dotnet-service.png new file mode 100644 index 000000000..e5aa360f7 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_dotnet-service.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_edit-sf-connector.png b/openidm-doc/src/main/asciidoc/images/thumb_edit-sf-connector.png new file mode 100644 index 000000000..1273b4599 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_edit-sf-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_elastic-audit.png b/openidm-doc/src/main/asciidoc/images/thumb_elastic-audit.png new file mode 100644 index 000000000..f9e636436 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_elastic-audit.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_expression-builder.png b/openidm-doc/src/main/asciidoc/images/thumb_expression-builder.png new file mode 100644 index 000000000..983e8d66d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_expression-builder.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_fullstack-openam-users.png b/openidm-doc/src/main/asciidoc/images/thumb_fullstack-openam-users.png new file mode 100644 index 000000000..256fe2ae4 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_fullstack-openam-users.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_generic-tables-erd.png b/openidm-doc/src/main/asciidoc/images/thumb_generic-tables-erd.png new file mode 100644 index 000000000..1f7876d21 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_generic-tables-erd.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_google-apps-active.png b/openidm-doc/src/main/asciidoc/images/thumb_google-apps-active.png new file mode 100644 index 000000000..c1717103f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_google-apps-active.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_google-apps-allow.png b/openidm-doc/src/main/asciidoc/images/thumb_google-apps-allow.png new file mode 100644 index 000000000..d6f16f4f9 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_google-apps-allow.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-differences.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-differences.png new file mode 100644 index 000000000..350a1f891 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-differences.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-password.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-password.png new file mode 100644 index 000000000..6f5e1ee3d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-password.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-recon-details.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-recon-details.png new file mode 100644 index 000000000..caafea5c4 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-recon-details.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-recon-top.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-recon-top.png new file mode 100644 index 000000000..a63c0c53a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-recon-top.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-selfservice-menu.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-selfservice-menu.png new file mode 100644 index 000000000..6aaaf00f3 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-selfservice-menu.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-telephone.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-telephone.png new file mode 100644 index 000000000..6fd6b2800 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-telephone.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_gsg-windows.png b/openidm-doc/src/main/asciidoc/images/thumb_gsg-windows.png new file mode 100644 index 000000000..ad2213d9c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_gsg-windows.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ha-cluster-config.png b/openidm-doc/src/main/asciidoc/images/thumb_ha-cluster-config.png new file mode 100644 index 000000000..15889ff0a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ha-cluster-config.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_kerberos-auth.png b/openidm-doc/src/main/asciidoc/images/thumb_kerberos-auth.png new file mode 100644 index 000000000..3958ec902 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_kerberos-auth.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_link-qualifier-script.png b/openidm-doc/src/main/asciidoc/images/thumb_link-qualifier-script.png new file mode 100644 index 000000000..31778a8cd Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_link-qualifier-script.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_link-qualifier.png b/openidm-doc/src/main/asciidoc/images/thumb_link-qualifier.png new file mode 100644 index 000000000..50b60ec6c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_link-qualifier.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_new-app-data.png b/openidm-doc/src/main/asciidoc/images/thumb_new-app-data.png new file mode 100644 index 000000000..b4121232a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_new-app-data.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_new-user.png b/openidm-doc/src/main/asciidoc/images/thumb_new-user.png new file mode 100644 index 000000000..c79ebc419 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_new-user.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_oauth-credentials.png b/openidm-doc/src/main/asciidoc/images/thumb_oauth-credentials.png new file mode 100644 index 000000000..c29558459 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_oauth-credentials.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_openam-auth-adv.png b/openidm-doc/src/main/asciidoc/images/thumb_openam-auth-adv.png new file mode 100644 index 000000000..0ba9a928f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_openam-auth-adv.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_openam-auth-basic.png b/openidm-doc/src/main/asciidoc/images/thumb_openam-auth-basic.png new file mode 100644 index 000000000..772796fad Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_openam-auth-basic.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_openam-djconn.png b/openidm-doc/src/main/asciidoc/images/thumb_openam-djconn.png new file mode 100644 index 000000000..8cd905075 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_openam-djconn.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_openidm-admin-role.png b/openidm-doc/src/main/asciidoc/images/thumb_openidm-admin-role.png new file mode 100644 index 000000000..7089578e5 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_openidm-admin-role.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_openidm2-architecture.png b/openidm-doc/src/main/asciidoc/images/thumb_openidm2-architecture.png new file mode 100644 index 000000000..a97e27607 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_openidm2-architecture.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_password-reset-steps.png b/openidm-doc/src/main/asciidoc/images/thumb_password-reset-steps.png new file mode 100644 index 000000000..62467be6f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_password-reset-steps.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_profile-kba-questions.png b/openidm-doc/src/main/asciidoc/images/thumb_profile-kba-questions.png new file mode 100644 index 000000000..30b6762b8 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_profile-kba-questions.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_provisioning-add-user.png b/openidm-doc/src/main/asciidoc/images/thumb_provisioning-add-user.png new file mode 100644 index 000000000..0a4c6b557 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_provisioning-add-user.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_provisioning-dashboard.png b/openidm-doc/src/main/asciidoc/images/thumb_provisioning-dashboard.png new file mode 100644 index 000000000..194739d80 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_provisioning-dashboard.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-add-directory.png b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-add-directory.png new file mode 100644 index 000000000..9c3404ce1 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-add-directory.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-add-user.png b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-add-user.png new file mode 100644 index 000000000..355ebed09 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-add-user.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-credentials.png b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-credentials.png new file mode 100644 index 000000000..be851b819 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-credentials.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-user-pwd.png b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-user-pwd.png new file mode 100644 index 000000000..f13374394 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ps-azure-user-pwd.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_reconstats-dashboard.png b/openidm-doc/src/main/asciidoc/images/thumb_reconstats-dashboard.png new file mode 100644 index 000000000..e1164c8ad Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_reconstats-dashboard.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_relationships-graph.png b/openidm-doc/src/main/asciidoc/images/thumb_relationships-graph.png new file mode 100644 index 000000000..1df9ae17a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_relationships-graph.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_remote-csv.png b/openidm-doc/src/main/asciidoc/images/thumb_remote-csv.png new file mode 100644 index 000000000..c7e16e477 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_remote-csv.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_roles-auth.png b/openidm-doc/src/main/asciidoc/images/thumb_roles-auth.png new file mode 100644 index 000000000..8f1ae2153 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_roles-auth.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_salesforce-bjensen.png b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-bjensen.png new file mode 100644 index 000000000..2f5bba9ef Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-bjensen.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_salesforce-connector.png b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-connector.png new file mode 100644 index 000000000..77d77f75c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_salesforce-mappings.png b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-mappings.png new file mode 100644 index 000000000..66706adf2 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-mappings.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_salesforce-reconcile.png b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-reconcile.png new file mode 100644 index 000000000..255da39b8 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-reconcile.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_salesforce-users.png b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-users.png new file mode 100644 index 000000000..d65bb447d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_salesforce-users.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_sample2-mappings.png b/openidm-doc/src/main/asciidoc/images/thumb_sample2-mappings.png new file mode 100644 index 000000000..69ac05654 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_sample2-mappings.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_sample2d-groups.png b/openidm-doc/src/main/asciidoc/images/thumb_sample2d-groups.png new file mode 100644 index 000000000..ce690a85c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_sample2d-groups.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_self-service-ui.png b/openidm-doc/src/main/asciidoc/images/thumb_self-service-ui.png new file mode 100644 index 000000000..ef76f277a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_self-service-ui.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_service-acct.png b/openidm-doc/src/main/asciidoc/images/thumb_service-acct.png new file mode 100644 index 000000000..9977d06fa Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_service-acct.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_sql-tables.png b/openidm-doc/src/main/asciidoc/images/thumb_sql-tables.png new file mode 100644 index 000000000..c8e447170 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_sql-tables.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ssh-connector.png b/openidm-doc/src/main/asciidoc/images/thumb_ssh-connector.png new file mode 100644 index 000000000..decb5af78 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ssh-connector.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_temporal-role.png b/openidm-doc/src/main/asciidoc/images/thumb_temporal-role.png new file mode 100644 index 000000000..112dbce0f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_temporal-role.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-access-request.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-access-request.png new file mode 100644 index 000000000..75ebec879 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-access-request.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-custom-selfreg.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-custom-selfreg.png new file mode 100644 index 000000000..fcbe28de5 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-custom-selfreg.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-data-account.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-data-account.png new file mode 100644 index 000000000..8be904326 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-data-account.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-device-array.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-array.png new file mode 100644 index 000000000..b3173364e Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-array.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-device-relation.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-relation.png new file mode 100644 index 000000000..23714ffb0 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-relation.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-device-resource-collection.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-resource-collection.png new file mode 100644 index 000000000..4c414401b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-resource-collection.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-device-resource.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-resource.png new file mode 100644 index 000000000..84234a38b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-device-resource.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-direct-reports.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-direct-reports.png new file mode 100644 index 000000000..d3b0f2dec Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-direct-reports.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-email-valid.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-email-valid.png new file mode 100644 index 000000000..001a38c0c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-email-valid.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-initial-mo.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-initial-mo.png new file mode 100644 index 000000000..cef182c1c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-initial-mo.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-mo-iot.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-mo-iot.png new file mode 100644 index 000000000..725c5ad15 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-mo-iot.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-mo-wearable.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-mo-wearable.png new file mode 100644 index 000000000..730b1f17c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-mo-wearable.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-oneiot-device.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-oneiot-device.png new file mode 100644 index 000000000..8bc04678d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-oneiot-device.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-selfservice-selfreg.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-selfservice-selfreg.png new file mode 100644 index 000000000..aab5ff763 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-selfservice-selfreg.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-tamper.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-tamper.png new file mode 100644 index 000000000..5de44cb1d Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-tamper.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-updated-profile.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-updated-profile.png new file mode 100644 index 000000000..eb571ca5c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-updated-profile.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-user-iot-device.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-user-iot-device.png new file mode 100644 index 000000000..521400c90 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-user-iot-device.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_ui-valid-query.png b/openidm-doc/src/main/asciidoc/images/thumb_ui-valid-query.png new file mode 100644 index 000000000..21fa8f53c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_ui-valid-query.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_widget-icons.png b/openidm-doc/src/main/asciidoc/images/thumb_widget-icons.png new file mode 100644 index 000000000..963502984 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_widget-icons.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_windows-service.png b/openidm-doc/src/main/asciidoc/images/thumb_windows-service.png new file mode 100644 index 000000000..eeca0febd Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_windows-service.png differ diff --git a/openidm-doc/src/main/asciidoc/images/thumb_workflow-notifications.png b/openidm-doc/src/main/asciidoc/images/thumb_workflow-notifications.png new file mode 100644 index 000000000..9ea2b8205 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/thumb_workflow-notifications.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-access-request.png b/openidm-doc/src/main/asciidoc/images/ui-access-request.png new file mode 100644 index 000000000..6f1173572 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-access-request.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-custom-selfreg.png b/openidm-doc/src/main/asciidoc/images/ui-custom-selfreg.png new file mode 100644 index 000000000..426d5bff5 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-custom-selfreg.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-data-account.png b/openidm-doc/src/main/asciidoc/images/ui-data-account.png new file mode 100644 index 000000000..e9d9483d6 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-data-account.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-device-array.png b/openidm-doc/src/main/asciidoc/images/ui-device-array.png new file mode 100644 index 000000000..13d69769c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-device-array.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-device-relation.png b/openidm-doc/src/main/asciidoc/images/ui-device-relation.png new file mode 100644 index 000000000..851f31444 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-device-relation.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-device-resource-collection.png b/openidm-doc/src/main/asciidoc/images/ui-device-resource-collection.png new file mode 100644 index 000000000..4c414401b Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-device-resource-collection.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-device-resource.png b/openidm-doc/src/main/asciidoc/images/ui-device-resource.png new file mode 100644 index 000000000..907982394 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-device-resource.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-direct-reports.png b/openidm-doc/src/main/asciidoc/images/ui-direct-reports.png new file mode 100644 index 000000000..3772baf0a Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-direct-reports.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-email-valid.png b/openidm-doc/src/main/asciidoc/images/ui-email-valid.png new file mode 100644 index 000000000..f9dc3b797 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-email-valid.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-initial-mo.png b/openidm-doc/src/main/asciidoc/images/ui-initial-mo.png new file mode 100644 index 000000000..41b615fd6 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-initial-mo.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-mo-iot.png b/openidm-doc/src/main/asciidoc/images/ui-mo-iot.png new file mode 100644 index 000000000..6f07632ba Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-mo-iot.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-mo-wearable.png b/openidm-doc/src/main/asciidoc/images/ui-mo-wearable.png new file mode 100644 index 000000000..2e57493e8 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-mo-wearable.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-oneiot-device.png b/openidm-doc/src/main/asciidoc/images/ui-oneiot-device.png new file mode 100644 index 000000000..b251028d5 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-oneiot-device.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-selfservice-selfreg.png b/openidm-doc/src/main/asciidoc/images/ui-selfservice-selfreg.png new file mode 100644 index 000000000..915a43ffc Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-selfservice-selfreg.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-tamper.png b/openidm-doc/src/main/asciidoc/images/ui-tamper.png new file mode 100644 index 000000000..85116235c Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-tamper.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-updated-profile.png b/openidm-doc/src/main/asciidoc/images/ui-updated-profile.png new file mode 100644 index 000000000..e2b86e95f Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-updated-profile.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-user-iot-device.png b/openidm-doc/src/main/asciidoc/images/ui-user-iot-device.png new file mode 100644 index 000000000..bfb22b7c0 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-user-iot-device.png differ diff --git a/openidm-doc/src/main/asciidoc/images/ui-valid-query.png b/openidm-doc/src/main/asciidoc/images/ui-valid-query.png new file mode 100644 index 000000000..5afa70af7 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/ui-valid-query.png differ diff --git a/openidm-doc/src/main/asciidoc/images/widget-icons.png b/openidm-doc/src/main/asciidoc/images/widget-icons.png new file mode 100644 index 000000000..963502984 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/widget-icons.png differ diff --git a/openidm-doc/src/main/asciidoc/images/windows-service.png b/openidm-doc/src/main/asciidoc/images/windows-service.png new file mode 100644 index 000000000..2ed3cbedd Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/windows-service.png differ diff --git a/openidm-doc/src/main/asciidoc/images/workflow-notifications.png b/openidm-doc/src/main/asciidoc/images/workflow-notifications.png new file mode 100644 index 000000000..9ea2b8205 Binary files /dev/null and b/openidm-doc/src/main/asciidoc/images/workflow-notifications.png differ diff --git a/openidm-doc/src/main/asciidoc/install-guide/appendix-ro-install.adoc b/openidm-doc/src/main/asciidoc/install-guide/appendix-ro-install.adoc new file mode 100644 index 000000000..ad8fea3b7 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/appendix-ro-install.adoc @@ -0,0 +1,236 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-ro-install] +== Installing OpenIDM on a Read-Only Volume + +Some enterprises choose to enhance security of their applications by installing them on a dedicated read-only (ro) filesystem volume. In this appendix, we describe how you can set up OpenIDM on such a volume. + +This appendix assumes that you have prepared the read-only volume appropriate for your Linux/UNIX installation environment. + +[#ro-install-volumes] +=== Preparing Your System + +Before you continue, read xref:chap-install.adoc#chap-install["Installing OpenIDM Services"]. + +This appendix assumes that you have set up a regular Linux user named `idm` and a dedicated volume for the `/idm` directory. + +Configure the dedicated volume device, `/dev/volume` in the `/etc/fstab` file, as follows: + +[source, console] +---- +/dev/volume /idm ext4 ro,defaults 1,2 +---- +When you run the `mount -a` command, the `/dev/volume` volume device gets mounted on the `/idm` directory. + +You can switch between read-write and read-only mode for the `/idm` volume with the following commands: + +[source, console] +---- +$ sudo mount -o remount,rw /idm +$ sudo mount -o remount,ro /idm +---- +You can confirm the result with the `mount` command, which should show whether the `/idm` volume is mounted in read-only or read-write mode: + +[source, console] +---- +/dev/volume on /idm type ext4 (ro) +---- +Set up the `/idm` volume in read-write mode: + +[source, console] +---- +$ sudo mount -o remount,rw /idm +---- +With the following commands, you can unpack the OpenIDM binary in the `/idm` directory, and give user `idm` ownership of all files in that directory: + +[source, console] +---- +$ sudo unzip /idm/openidm-4.5.1-20.zip +$ sudo chown -R idm.idm /idm +---- + + +[#redirect-openidm-writes] +=== Redirect Output Through Configuration Files + +In this section, you will modify appropriate configuration files for OpenIDM to redirect data to writable volumes. This procedure assumes that you have a user `idm` with Linux administrative (superuser) privileges. + +==== + +. Create an external directory where OpenIDM can send logging, auditing, and internal repository information. ++ + +[source, console] +---- +$ sudo mkdir -p /var/log/openidm/audit +$ sudo mkdir /var/log/openidm/logs +$ sudo mkdir -p /var/cache/openidm/felix-cache +$ sudo mkdir /var/run/openidm +---- ++ + +[NOTE] +====== +OpenIDM can help you route audit data to a remote data store. For an example of how OpenIDM sends data to a MySQL repository, review xref:../samples-guide/chap-audit-sample.adoc#chap-audit-sample["Audit Samples"] in the __Samples Guide__. +====== + +. Give user `idm` ownership of the newly created directories: ++ + +[source, console] +---- +$ sudo chown -R idm.idm /var/log/openidm +$ sudo chown -R idm.idm /var/cache/openidm +$ sudo chown -R idm.idm /var/run/openidm +---- ++ + +[NOTE] +====== +If you use the unsupported OrientDB repository, you should also set up a writable directory to substitute for `project-dir/db/openidm`. +====== + +. Open the audit configuration file for your project, `project-dir/conf/audit.json`. ++ +Make sure `handlerForQueries` is set to `repo`. ++ +Redirect the `logDirectory` property to the newly created `/var/log/openidm/audit` subdirectory: ++ + +[source, javascript] +---- +{ + "auditServiceConfig" : { + "handlerForQueries" : "repo", + "availableAuditEventHandlers" : [ + "org.forgerock.audit.events.handlers.csv.CSVAuditEventHandler", + "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "org.forgerock.openidm.audit.impl.RouterAuditEventHandler" + ] + }, + "eventHandlers" : [ + { + "name" : "csv", + "class" : "org.forgerock.audit.events.handlers.csv.CSVAuditEventHandler", + "config" : { + "logDirectory" : "/var/log/openidm/audit" + }, + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ] + }, +---- + +. Open the logging configuration file for your project: `project-dir/conf/logging.properties`. ++ +Find the `java.util.logging.FileHandler.pattern` property and redirect it as shown: ++ + +[source, javascript] +---- +java.util.logging.FileHandler.pattern = /var/log/openidm/logs/openidm%u.log +---- + +. Open the configuration properties file for your project: `project-dir/conf/config.properties`. ++ +Activate the `org.osgi.framework.storage` property. Activate and redirect the `felix.cache.rootdir` property and change them as shown: ++ + +[source, console] +---- +# If this value is not absolute, then the felix.cache.rootdir controls +# how the absolute location is calculated. (See buildNext property) +org.osgi.framework.storage=${felix.cache.rootdir}/felix-cache + +# The following property is used to convert a relative bundle cache +# location into an absolute one by specifying the root to prepend to +# the relative cache path. The default for this property is the +# current working directory. +felix.cache.rootdir=/var/cache/openidm +---- + +==== + +[NOTE] +==== +You may want to set up additional redirection. Watch for the following configuration details: + +* Connectors. Depending on the connector, and the read-only volume, you may need to configure connectors to direct output to writable volumes. + +* Scripts. If you're using Groovy, examine the `conf/script.json` file for your project. Make sure that output such as to the `groovy.target.directory` is directed to an appropriate location, such as `launcher.working.location` + +==== + + +[#ro-install-final] +=== Additional Details + +In a production environment, you must configure a supported repository for OpenIDM, as described in xref:chap-repository.adoc#chap-repository["Installing a Repository For Production"]. + +Disable monitoring of JSON configuration files. To do so, open the `project-dir/conf/system.properties` file, and activate the following option: + +[source, javascript] +---- +openidm.fileinstall.enabled=false +---- +You should address one more detail, the value of the `OPENIDM_PID_FILE` in the `startup.sh` and `shutdown.sh` scripts. + +For RHEL 6 and Ubuntu 14.04 systems, the default shell is bash. You can set the value of `OPENIDM_PID_FILE` for user `idm` by adding the following line to `/home/idm/.bashrc`: + +[source, console] +---- +export OPENIDM_PID_FILE=/var/run/openidm/openidm.pid +---- +If you have set up a different command line shell, adjust your changes accordingly. + +You can now log in again as user `idm`. When you do, your `OPENIDM_PID_FILE` variable should now redirect the OpenIDM process identifier file, `openidm.pid` to the `/var/run/openidm` directory, ready for access by the `shutdown.sh` script. + +You need to set up security keystore and truststore files, either by importing a signed certificate or by generating a self-signed certificate. For more information, see xref:../integrators-guide/chap-security.adoc#chap-security["Securing & Hardening OpenIDM"] in the __Integrator's Guide__. + +While the volume is still mounted in read-write mode, start OpenIDM normally: + +[source, console] +---- +$ ./startup.sh -p project-dir +---- +The first startup of OpenIDM either processes the signed certificate that you added, or generates a self-signed certificate. + +Stop OpenIDM: + +[source, console] +---- +-> shutdown +---- +You can now mount the `/idm` directory in read-only mode. The configuration in `/etc/fstab` ensures that Linux mounts the `/idm` directory in read-only mode the next time that system is booted. + +[source, console] +---- +$ sudo mount -o remount,ro /idm +---- +You can now start OpenIDM, configured on a secure read-only volume. + +[source, console] +---- +$ ./startup.sh -p project-dir +---- + + diff --git a/openidm-doc/src/main/asciidoc/install-guide/chap-install.adoc b/openidm-doc/src/main/asciidoc/install-guide/chap-install.adoc new file mode 100644 index 000000000..3f49e2983 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/chap-install.adoc @@ -0,0 +1,622 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-install] +== Installing OpenIDM Services + +This chapter covers the tasks required to install and start OpenIDM. + +[NOTE] +==== +ForgeRock documentation includes a separate xref:../samples-guide/index.adoc[Samples Guide]. When you have read the first two chapters of this document, you can use the xref:../samples-guide/index.adoc[Samples Guide] to test OpenIDM in a number of different configurations. +==== + +[#before-you-start] +=== Before You Run OpenIDM + +This section covers what you need to know before running OpenIDM. + +[#java-prerequisites] +==== Java Environment + +On Windows systems, you must set the `JAVA_HOME` environment variable to point to the root of a valid Java installation. The following steps indicate how to set the `JAVA_HOME` environment variable on Windows Server 2008 R2. Adjust the steps for your specific environment: + +. Locate your JRE Installation Directory. If you have not changed the installation path for the Java Runtime Environment during installation, it will be in a directory under `C:\Program Files\Java\`. + +. Select Start > Control Panel > System and Security > System. + +. Click Advanced System Settings. + +. Click Environment Variables. + +. Under System Variables, click New. + +. Enter the Variable name (`JAVA_HOME`) and set the Variable value to the JRE installation directory, for example `C:\Program Files\Java\jre7`. + +. Click OK. + + + +[#application-container-prerequisites] +==== Application Container + +OpenIDM services run in an OSGi container with an embedded Servlet container, and an embedded noSQL database. By default the OSGi container is Apache Felix (Felix). The default Servlet container is Jetty. For OpenIDM 4.5, the only supported configuration is running the services in Felix and Jetty. + + + +[#installing-openidm] +=== Installing and Running OpenIDM + +Follow the procedures in this section to install and run OpenIDM in UNIX and Windows environments. + +[#install-openidm] +.To Install OpenIDM Services +==== +Follow these steps to install OpenIDM: + +. Make sure you have an appropriate version of Java installed: ++ + +[source, console] +---- +$ java -version +java version "1.7.0_79" +Java(TM) SE Runtime Environment (build 1.7.0_79-b15) +Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode) +---- + +. Download enterprise software releases through the ForgeRock link:https://backstage.forgerock.com/[BackStage, window=\_blank] site. ForgeRock enterprise releases are thoroughly validated builds for ForgeRock customers who run OpenIDM in production deployments, and for those who want to try or test with release builds. + +. Unpack the contents of the .zip file into the install location: ++ + +[source, console] +---- +$ cd /path/to +$ unzip ~/Downloads/openidm-4.5.1-20.zip +Archive: openidm-4.5.1-20.zip + inflating: openidm/.checksums.csv + creating: openidm/bundle/ + extracting: openidm/bundle/openidm-audit-4.5.1-20.jar +... +---- + +. (Optional) By default, OpenIDM listens for HTTP and HTTPS connections on ports 8080 and 8443, respectively. To change the default port, edit your project's `conf/boot/boot.properties` file. For more information, see xref:../integrators-guide/appendix-ports-used.adoc#appendix-ports-used["Ports Used"] in the __Integrator's Guide__. + +. (Optional) Before running OpenIDM in production, replace the default OrientDB repository, provided for evaluation, with a supported JDBC repository. ++ +For more information, see xref:chap-repository.adoc#chap-repository["Installing a Repository For Production"]. + +==== + +[#run-openidm] +.To Start OpenIDM Services +==== +To run OpenIDM as a background process, see xref:../integrators-guide/chap-services.adoc#chap-services["Starting and Stopping OpenIDM"] in the __Integrator's Guide__. + +Follow these steps to run OpenIDM interactively: + +. Start the Felix container, load all OpenIDM services, and start a command shell to allow you to manage the container: ++ + +* Start OpenIDM (UNIX): ++ + +[source, console] +---- +$ ./startup.sh + +Using OPENIDM_HOME: /path/to/openidm +Using PROJECT_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> OpenIDM ready +---- + +* Start OpenIDM (Windows): ++ + +[source, console] +---- +C:\> cd \path\to\openidm +C:\> startup.bat + +"Using OPENIDM_HOME: \path\to\openidm" +"Using PROJECT_HOME: \path\to\openidm" +"Using OPENIDM_OPTS: -Xmx1024m -Xms1024m -Dfile.encoding=UTF-8" +"Using LOGGING_CONFIG: -Djava.util.logging.config.file=\path\to\openidm\conf\logging.properties" +Using boot properties at \path\to\openidm\conf\boot\boot.properties +-> OpenIDM ready +-> +---- + ++ +At the OSGi console `->` prompt, you can enter commands such as `help` for usage, or `ps` to view the bundles installed. To see a list of all the OpenIDM core services and their states, enter the following command: ++ + +[source, console] +---- +-> scr list + Id State Name +[ 16] [active ] org.forgerock.openidm.endpoint +[ 17] [active ] org.forgerock.openidm.endpoint +[ 37] [active ] org.forgerock.openidm.endpoint +[ 36] [active ] org.forgerock.openidm.endpoint +[ 18] [active ] org.forgerock.openidm.endpoint +[ 38] [active ] org.forgerock.openidm.endpoint +[ 39] [active ] org.forgerock.openidm.endpoint +[ 40] [active ] org.forgerock.openidm.endpoint +[ 32] [active ] org.forgerock.openidm.endpoint +[ 19] [active ] org.forgerock.openidm.config.enhanced +[ 49] [active ] org.forgerock.openidm.selfservice.userupdate +[ 3] [unsatisfied ] org.forgerock.openidm.datasource.jdbc +[ 7] [active ] org.forgerock.openidm.http.context +[ 23] [active ] org.forgerock.openidm.info +[ 41] [active ] org.forgerock.openidm.info +[ 24] [active ] org.forgerock.openidm.info +[ 35] [active ] org.forgerock.openidm.provisioner.openicf.connectorinfoprovider +[ 44] [unsatisfied ] org.forgerock.openidm.provisioner.salesforce +[ 28] [active ] org.forgerock.openidm.maintenance.update.log +[ 26] [active ] org.forgerock.openidm.maintenance.updatemanager +[ 5] [active ] org.forgerock.openidm.repo.orientdb +[ 34] [active ] org.forgerock.openidm.openicf.syncfailure +[ 8] [active ] org.forgerock.openidm.api-servlet +[ 2] [active ] org.forgerock.openidm.config.enhanced.starter +[ 0] [active ] org.forgerock.openidm.security +[ 25] [active ] org.forgerock.openidm.maintenance.update +[ 10] [active ] org.forgerock.openidm.audit +[ 57] [unsatisfied ] org.forgerock.openidm.schedule +[ 52] [active ] org.forgerock.openidm.servletfilter.registrator +[ 11] [active ] org.forgerock.openidm.auth.config +[ 4] [unsatisfied ] org.forgerock.openidm.repo.jdbc +[ 55] [active ] org.forgerock.openidm.workflow +[ 33] [unsatisfied ] org.forgerock.openidm.provisioner.openicf +[ 15] [active ] org.forgerock.openidm.managed +[ 22] [active ] org.forgerock.openidm.health +[ 31] [active ] org.forgerock.openidm.provisioner +[ 42] [active ] org.forgerock.openidm.internal +[ 27] [active ] org.forgerock.openidm.maintenance.update.config +[ 43] [active ] org.forgerock.openidm.provisioner.salesforce.confighelper +[ 56] [active ] org.forgerock.openidm.taskscanner +[ 21] [active ] org.forgerock.openidm.external.rest +[ 50] [active ] org.forgerock.openidm.ui.context +[ 51] [active ] org.forgerock.openidm.ui.context +[ 46] [active ] org.forgerock.openidm.selfservice.kbaservice +[ 9] [active ] org.forgerock.openidm.router +[ 58] [active ] org.forgerock.openidm.scheduler +[ 20] [unsatisfied ] org.forgerock.openidm.external.email +[ 30] [active ] org.forgerock.openidm.policy +[ 6] [active ] org.forgerock.openidm.cluster +[ 13] [active ] org.forgerock.openidm.sync +[ 45] [active ] org.forgerock.openidm.script +[ 14] [active ] org.forgerock.openidm.recon +[ 53] [active ] org.forgerock.openidm.servletfilter +[ 54] [active ] org.forgerock.openidm.servletfilter +[ 48] [unsatisfied ] org.forgerock.openidm.selfservice +[ 47] [active ] org.forgerock.openidm.selfservice.kba +[ 12] [active ] org.forgerock.openidm.authentication +[ 1] [active ] org.forgerock.openidm.config.manage +[ 29] [active ] org.forgerock.openidm.maintenance +-> +---- ++ +A default startup does not include certain configurable services, which will indicate an `unsatisfied` state until they are included in the configuration. As you work through the sample configurations described later in this guide, you will notice that these services are active. ++ +Startup errors and messages are logged to the console by default. You can also view these messages in the log files at `/path/to/openidm/logs`. + +. Alternatively, you can manage the container and services from the Apache Felix Web Console. ++ +Use these hints to connect to the Apache Felix Web Console: + +* Default URL: link:https://localhost:8443/system/console[https://localhost:8443/system/console, window=\_blank] + +* Default user name: `admin` + +* Default password: `admin` + ++ +Select Main > Components to see OpenIDM core services and their respective states. + +==== + +[#stop-openidm] +.To Stop the OpenIDM Services +==== + +. You can stop OpenIDM Services from the `->` prompt in the OSGi console, or through the Apache Felix Web Console. Both of these options stop the Felix container: ++ + +* In the OSGi console, enter the `shutdown` command at the `->` prompt: ++ + +[source, console] +---- +-> shutdown +... +$ +---- + +* In the Apache Felix Web Console, select Web Console > System Information to stop the container. + + +. On Unix systems, you can stop OpenIDM by using the `shutdown.sh` script, located in the `/path/to/openidm` directory: ++ + +[source, console] +---- +$ ./shutdown.sh +./shutdown.sh +Stopping OpenIDM (31391) +---- + +==== + +[NOTE] +==== +If you want to set up OpenIDM on a read-only volume, read xref:appendix-ro-install.adoc#appendix-ro-install["Installing OpenIDM on a Read-Only Volume"]. +==== + +[#install-windows-service] +.To Install OpenIDM as a Windows Service +==== +You can install OpenIDM to run as a Windows service so that the server starts and stops automatically when Windows starts and stops. You must be logged in as an administrator to install OpenIDM as a Windows service: + +[NOTE] +====== +On a 64-bit Windows server, you must have a 64-bit Java version installed to start the service. If a 32-bit Java version is installed, you will be able to install OpenIDM as a service, but starting the service will fail. + +__Before__ you launch the `install-service.bat` file, which registers the `OpenIDM` service within the Windows registry, make sure that your `JAVA_HOME` environment variable points to a valid 64-bit version of the JRE or JDK. If you have already installed the service with the `JAVA_HOME` environment variable pointing to a 32-bit JRE or JDK, delete the service first, then reinstall the service. +====== + +. Unpack the OpenIDM .zip file, as described previously, and change to the `install-location\bin` directory: ++ + +[source, console] +---- +C:\>cd openidm\bin +C:\openidm\bin> +---- + +. Run the `install-service.bat` command, specifying the name the service should run as: ++ + +[source, console] +---- +C:\openidm\bin>install-service.bat openidm +ForgeRock Launcher Java Service successfully installed as "openidm" service +---- + +. Use the Windows Service manager to manage the OpenIDM service. + + +[#d9505e641] +image::images/windows-service.png[] + + +. Change the user account for this service from the default (`local system`) account to an account with administrative privileges. The `local system` account has limited permissions and an OpenIDM service that runs with this account will encounter problems during synchronization. ++ +To change the user account: ++ + +.. Double click the `openidm` service in the Windows Service manager. + +.. Select the Log On tab. + +.. Select This Account and browse for an Active Directory administrative account. + +.. Enter the password for the administrative account. + + +[#d9505e676] +image::images/service-acct.png[] + + +.. Click Apply to save the changes. + + +. Use the Windows Service Manager to start, stop, or restart the service. + +. To uninstall the OpenIDM service stop the service, then run the following command: ++ + +[source, console] +---- +C:\install-location\openidm\bin>launcher.bat /uninstall openidm +... + Service "openidm" removed successfully +... +---- + +==== + + +[#first-steps-with-rest] +=== Getting Started With the OpenIDM REST Interface + +OpenIDM provides RESTful access to users in the OpenIDM repository. To access the OpenIDM repository over REST, you can use a browser-based REST client, such as the Simple REST Client for Chrome, or RESTClient for Firefox. Alternatively you can use the `curl` command-line utility that is included with most operating systems. For more information about `curl`, see link:https://github.com/bagder/curl[https://github.com/bagder/curl, window=\_top]. + +OpenIDM is accessible over the regular and secure HTTP ports of the Jetty Servlet container, 8080 and 8443. + +If you want to run `curl` over the secure port, 8443, you must either include the `--insecure` option, or follow the instructions in xref:../integrators-guide/chap-security.adoc#rest-over-https["Restrict REST Access to the HTTPS Port"] in the __Integrator's Guide__. You can use those instructions with the self-signed certificate that is generated when OpenIDM starts, or with a `*.crt` file provided by a certificate authority. + +In numerous cases, `curl` commands to the secure port are depicted with a `--cacert self-signed.crt` option. Instructions for creating that `self-signed.crt` file are shown in xref:../integrators-guide/chap-security.adoc#rest-over-https["Restrict REST Access to the HTTPS Port"] in the __Integrator's Guide__. + +If you would rather use `curl` to connect to the regular HTTP port, omit the `--cacert self-signed.crt` file and point to a regular Jetty HTTP URL such as `\http://localhost:8080/openidm/...`. + +[NOTE] +==== +All RESTful command-line examples in this guide, as depicted with `curl`, are based on the default configuration of OpenIDM. If you change configuration files in directories such as `openidm/conf` and `openidm/script`, you might need to modify the RESTful commands to reflect those changes. + +Most of the examples in this guide use client-assigned IDs when creating resources, as it makes the examples easier to read. + +In general, server-assigned UUIDs are better in production, as they can be generated easily in clustered environments. + +For some versions of Mac OS X, the stock version of the `curl` command with the `--cacert` option may lead to error messages. You may use the `-k` or `--insecure` options as a workaround. +==== + +[#first-rest-steps] +==== + +. Access the following URL to obtain the JSON representation of all users in the OpenIDM repository: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + http://localhost:8080/openidm/managed/user/?_queryId=query-all-ids +---- ++ +When you first install OpenIDM with an empty repository, no users exist. + +. Create a user `joe` by sending a RESTful POST. ++ +The following `curl` commands create the user `joe` in the repository. ++ + +* Create `joe` (UNIX): ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "userName":"joe", + "givenName":"joe", + "sn":"smith", + "mail":"joe@example.com", + "telephoneNumber":"555-123-1234", + "password":"TestPassw0rd", + "description":"My first user", + "_id":"joe" + }' \ + https://localhost:8443/openidm/managed/user?_action=create +{ + "_id": "joe", + "_rev": "1", + "userName": "joe", + "givenName": "joe", + "sn": "smith", + "mail": "joe@example.com", + "telephoneNumber": "555-123-1234", + "description": "My first user", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +* Create `joe` (Windows): ++ + +[source, console] +---- +C:\> curl ^ + --cacert self-signed.crt ^ + --header "Content-Type: application/json" ^ + --header "X-OpenIDM-Username: openidm-admin" ^ + --header "X-OpenIDM-Password: openidm-admin" ^ + --request POST ^ + --data "{ + \"userName\":\"joe\", + \"givenName\":\"joe\", + \"sn\":\"smith\", + \"mail\":\"joe@example.com\", + \"telephoneNumber\":\"555-123-1234\", + \"password\":\"TestPassw0rd\", + \"description\":\"My first user\" + \"_id\":\"joe\" + }" ^ + https://localhost:8443/openidm/managed/user?_action=create +---- + + +. Fetch the newly created user from the repository with a RESTful GET: ++ + +[source, console] +---- +$ 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/joe +{ + "_id": "joe", + "_rev": "1", + "userName": "joe", + "givenName": "joe", + "sn": "smith", + "mail": "joe@example.com", + "telephoneNumber": "555-123-1234", + "description": "My first user", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +. Notice that more attributes are returned for user `joe` than the attributes you added in the previous step. The additional attributes are added by a script named `onCreateUser.js` that is triggered when a new user is created. For more information, see xref:../integrators-guide/appendix-objects.adoc#managed-object-configuration["Managed Object Configuration"] in the __Integrator's Guide__. ++ +When you create a user some attributes might be __required__ by the policy that is associated with that user. These are listed in the `conf/policy.json` file. + +==== + +[#rest-output-format] +==== Format REST Output for Readability + +With all `curl`-based REST calls, OpenIDM returns the JSON object all on one line. + +Without a bit of help, the JSON output is formatted all on one line. One example is shown below, and it is difficult to read: + +[source, console] +---- +{"mail":"joe@example.com","sn":"smith","passwordAttempts":"0", + "lastPasswordAttempt":"Mon Apr 14 2014 11:13:37 GMT-0800 (GMT-08:00)", + "address2":"","givenName":"joe","effectiveRoles":["openidm-authorized"], + "password":{"$crypto":{"type":"x-simple-encryption","value":{"data": + "OBFVL9cG8uaLoo1N+SMJ3g==","cipher":"AES/CBC/PKCS5Padding","iv": + "7rlV4EwkwdRHkt19F8g22A==","key":"openidm-sym-default"}}},"country":"", + "city":"","_rev":"1","lastPasswordSet":"","postalCode":"","_id":"joe3", + "description":"My first user","accountStatus":"active","telephoneNumber": + "555-123-1234","roles":["openidm-authorized"],"effectiveAssignments":{}, + "postalAddress":"","stateProvince":"","userName":"joe3"} +---- +At least two options are available to clean up this output. + +The standard way to format JSON output is with a JSON parser such as link:http://stedolan.github.io/jq/[jq, window=\_top]. You would "pipe" the output of a REST call to `jq`, as follows: + +[source, console] +---- +$ 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/joe" \ +| jq . +---- +The ForgeRock REST API includes an optional `_prettyPrint` request parameter. The default value is `false`. To use the ForgeRock REST API to format output, add a parameter such as `?_prettyPrint=true` or `&_prettyPrint=true`, depending on whether it is added to the end of an existing request parameter. In this case, the following command would return formatted output: + +[source, console] +---- +$ 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/joe?_prettyPrint=true" +---- +Note that most command-line examples in this guide do not show this parameter, although the output is formatted for readability. + + + +[#openidm-uis] +=== OpenIDM User Interfaces + +OpenIDM supports configuration from Web-based user interfaces, called the UI in the OpenIDM documentation set. + +OpenIDM includes UIs at two different endpoints, `/` and `/admin`. We refer to the administrative tools available at each endpoint as the Self-Service UI and the Administrative UI (Admin UI), respectively. + +The Self-Service UI allows regular (non-administrative) users to update parts of their profile, such as passwords and addresses. For more information, see xref:../integrators-guide/chap-ui.adoc#ui-configuring["Configuring User Self-Service"] in the __Integrator's Guide__. When these features are enabled, anonymous users can self-register and regular users can reset their own passwords. For more information, see xref:../integrators-guide/chap-ui.adoc#ui-overview["Working With the Self-Service UI"] in the __Integrator's Guide__. + +In addition, administrative users can configure and manage workflows in the Self-Service UI. For more information, see xref:../integrators-guide/chap-ui.adoc#ui-managing-workflows["Managing Workflows From the Self-Service UI"] in the __Integrator's Guide__. + +In essence, the Self-Service UI supports day-to-day administrative tasks. + +In contrast, the Admin UI allows an administrator to define the overall OpenIDM system configuration. Administrators would access the Admin UI to learn OpenIDM, during initial system setup, and when they identify new requirements. + +Unlike the Self-Service UI, the Admin UI allows you to configure connections to external data stores, as well as the way OpenIDM reconciles information between internal and external data stores. + +When OpenIDM is running on the localhost system, you can access these UIs at `\https://localhost:8443/` and `\https://localhost:8443/admin`, respectively. + + +[#openidm-repository] +=== About the OpenIDM Repository + +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 supported for production use, however, so use a supported JDBC database when moving to production. + +To query the internal noSQL database, download and extract link:https://search.maven.org/remotecontent?filepath=com/orientechnologies/orientdb-community/1.7.10/orientdb-community-1.7.10-distribution.zip[OrientDB (version 1.7.10), window=\_blank]. You will find the shell console in the `bin` directory. Start the OrientDB console, using `console.sh` or `console.bat`, and connect to the running OpenIDM instance, with the `connect` command: + +[source, console] +---- +$ cd /path/to/orientdb-community-1.7.10/bin +$ ./console.sh + +OrientDB console v.1.7.10 (build @BUILD@) www.orientechnologies.com +Type 'help' to display all the commands supported. + +Installing extensions for GREMLIN language v.2.5.0 +orientdb> connect remote:localhost/openidm admin admin +Connecting to database [remote:localhost/openidm] with user 'admin'...OK + +orientdb> +---- +-- +When you have connected to the database, you might find the following commands useful: + +`info`:: +Shows classes and records + +`select * from managed_user`:: +Shows all users in the OpenIDM repository + +`select * from audit_activity`:: +Shows all activity audit records + ++ +This table is created on install and populated when there is any activity on the server. + +`select * from audit_recon`:: +Shows all reconciliation audit records + ++ +This table is created on install and populated when you run a reconciliation operation. + +-- +You can also use OrientDB Studio to query the default OrientDB repository. After you have installed and started OpenIDM, point your browser to link:http://localhost:2480/[http://localhost:2480/, window=\_top]. The default database is `openidm` and the default user and password are `admin` and `admin`. Click Connect to connect to the repository. + +To change the default password, use the following POST request on the `repo` endpoint: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/repo?_action=updateDbCredentials&user=admin&password=newPassword" +---- +You must restart OpenIDM for the change to take effect. + +This command updates both the repository and the repository configuration file. + + diff --git a/openidm-doc/src/main/asciidoc/install-guide/chap-repository.adoc b/openidm-doc/src/main/asciidoc/install-guide/chap-repository.adoc new file mode 100644 index 000000000..2280ca45f --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/chap-repository.adoc @@ -0,0 +1,1088 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-repository] +== Installing a Repository For Production + +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 using OpenIDM in production, however, you must replace OrientDB with a supported JDBC repository. +In production environments, OpenIDM 4.5 supports the use of the following internal repositories: + +* MySQL + +* MS SQL + +* PostgreSQL + +* Oracle Database + +* IBM DB2 Database + +This chapter describes how to set up OpenIDM to work with each of these supported repositories, and lists the minimum rights required for database installation and operation. For information about the general JDBC repository configuration, and how to map OpenIDM objects to JDBC database tables, see xref:../integrators-guide/chap-repo.adoc#chap-repo["Managing the OpenIDM Repository"] in the __Integrator's Guide__. + +[#repository-minimum-rights] +=== Minimum Database Access Rights + +In general, OpenIDM requires minimal access rights to the JDBC repository for daily operation. This section lists the minimum permissions required, and suggests a strategy for restricting database access in an OpenIDM installation. + +The JDBC repository used by OpenIDM requires only one __relevant__ user - the service account that is used to create the tables. Generally, the details of this account are configured in the repository connection file (`datasource.jdbc-default.json`). By default, the username and password for this account are `openidm` and `openidm`, regardless of the database type. + +All other users are created by the `database-type/conf/openidm.sql` script. The `openidm` user account must have SELECT, UPDATE, INSERT, and DELETE permissions on all the openidm tables that are created by this script, and by the scripts that create the tables specific to the Activiti workflow engine. + + +[#repository-mysql] +=== To Set Up OpenIDM With MySQL + + +==== +After you have installed MySQL on the local host and __before starting OpenIDM for the first time__, set up OpenIDM to use the new repository, as described in the following sections. + +This procedure assumes that a password has already been set for the MySQL root user: + +. Download link:http://dev.mysql.com/downloads/connector/j/5.1.html[MySQL Connector/J, window=\_blank], version 5.1 or later from the MySQL website. Unpack the delivery, and copy the .jar into the `openidm/bundle` directory: ++ + +[source, console] +---- +$ cp mysql-connector-java-version-bin.jar /path/to/openidm/bundle/ +---- + +. Make sure that OpenIDM is stopped: ++ + +[source, console] +---- +$ cd /path/to/openidm/ +$ ./shutdown.sh +OpenIDM is not running, not stopping. +---- + +. Remove the OrientDB configuration file (`repo.orientdb.json`) from your project's `conf/` directory. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm/my-project/conf/ +$ rm repo.orientdb.json +---- + +. Copy the MySQL database connection configuration file (`datasource.jdbc-default.json`) and the database table configuration file (`repo.jdbc.json`) to your project's `conf` directory: ++ + +[source, console] +---- +$ cd /path/to/openidm/ +$ cp db/mysql/conf/datasource.jdbc-default.json my-project/conf/ +$ cp db/mysql/conf/repo.jdbc.json my-project/conf/ +---- + +. Import the data definition language script for OpenIDM into MySQL: ++ + +[source, console] +---- +$ cd /path/to/mysql +$ mysql -u root -p < /path/to/openidm/db/mysql/scripts/openidm.sql +Enter password: +$ +---- ++ +This step creates an `openidm` database for use as the internal repository, and a user `openidm` with password `openidm` who has all the required privileges to update the database: ++ + +[source, console] +---- +$ mysql -u root -p +Enter password: +Welcome to the MySQL monitor. Commands end with ; or \g. +Your MySQL connection id is 18 +Server version: 5.5.19 MySQL Community Server (GPL) +... +mysql> use openidm; +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 +mysql> show tables; + +mysql> show tables; ++---------------------------+ +| Tables_in_openidm | ++---------------------------+ +| auditaccess | +| auditactivity | +| auditauthentication | +| ... | +| uinotification | +| updateobjectproperties | +| updateobjects | ++---------------------------+ +27 rows in set (0.00 sec) +---- ++ +The table names are similar to those used with OrientDB. ++ +Exit the mysql console. ++ + +[source, console] +---- +mysql> exit +Bye +---- + +. Run the three scripts that set up the tables required by the Activiti workflow engine. ++ +If you are running MySQL 5.6.4 or higher, run the following scripts: ++ + +[source, console] +---- +$ cd /path/to/mysql +$ mysql -D openidm -u root -p < /path/to/openidm/db/mysql/scripts/activiti.mysql.create.engine.sql +Enter password: +$ mysql -D openidm -u root -p < /path/to/openidm/db/mysql/scripts/activiti.mysql.create.history.sql +Enter password: +$ mysql -D openidm -u root -p < /path/to/openidm/db/mysql/scripts/activiti.mysql.create.identity.sql +Enter password: +---- ++ +If you are running a MySQL version prior to 5.6.4, run the following scripts: ++ + +[source, console] +---- +$ cd /path/to/mysql +$ mysql -D openidm -u root -p < /path/to/openidm/db/mysql/scripts/activiti.mysql55.create.engine.sql +Enter password: +$ mysql -D openidm -u root -p < /path/to/openidm/db/mysql/scripts/activiti.mysql55.create.history.sql +Enter password: +$ mysql -D openidm -u root -p < /path/to/openidm/db/mysql/scripts/activiti.mysql.create.identity.sql +Enter password: +---- + +. Update the connection configuration in `datasource.jdbc-default.json` to reflect your MySQL deployment. The default connection configuration is as follows: ++ + +[source, javascript] +---- +{ + "driverClass" : "com.mysql.jdbc.Driver", + "jdbcUrl" : "jdbc:mysql://localhost:3306/openidm?allowMultiQueries=true&characterEncoding=utf8", + "databaseName" : "openidm", + "username" : "openidm", + "password" : "openidm", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp" + } +} +---- + +==== +When you have set up MySQL for use as the OpenIDM internal repository, start OpenIDM to check that the setup has been successful. After startup, you should see that `repo.jdbc` is `active`, whereas `repo.orientdb` is `unsatisfied`: + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh +Using OPENIDM_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: +-Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> scr list + +Id State Name +[ 19] [active ] org.forgerock.openidm.config.starter +... +[ 18] [unsatisfied ] org.forgerock.openidm.repo.orientdb +... +[ 17] [active ] org.forgerock.openidm.repo.jdbc +... +---- + + +[#repository-mssql] +=== To Set Up OpenIDM With MS SQL + + +==== +These instructions are specific to MS SQL Server 2012 R2 Standard Edition, running on a Windows Server 2012 R2 system. Adapt the instructions for your environment. +When you install Microsoft SQL Server, note that OpenIDM has the following specific configuration requirements: + +* During the Feature Selection installation step, make sure that at least SQL Server Replication, Full Text Search, and Management Tools - Basic are selected. ++ +These instructions require SQL Management Studio so make sure that you include Management Tools in the installation. + +* During the Database Engine Configuration step, select Mixed Mode (SQL Server authentication and Windows authentication). OpenIDM __requires__ SQL Server authentication. + +* TCP/IP must be enabled and configured for the correct IP address and port. To configure TCP/IP, follow these steps: ++ + +. Navigate to SQL Server Configuration Manager. + +. Expand the SQL Server Network Configuration item and select "Protocols for MSSQLSERVER". + +. Check that TCP/IP is Enabled. + +. Select the IP Addresses tab and set the addresses and ports on which the server will listen. ++ +For this sample procedure, scroll down to IPAll and set TCP Dynamic Ports to 1433 (the default port for MS SQL). + +. Click OK. + +. Restart MS SQL Server for the configuration changes to take effect. ++ +To restart the server, select SQL Server Services in the left pane, double click SQL Server (MSSQLSERVER) and click Restart. + +. If you have a firewall enabled, ensure that the port you configured in the previous step is open for OpenIDM to access MS SQL. + + +After you have installed MS SQL on the local host, install OpenIDM, if you have not already done so, but __do not start__ the OpenIDM instance. Import the data definition and set up OpenIDM to use the MS SQL repository, as described in the following steps: + +. Use SQL Management Studio to import the data definition language script for OpenIDM into MS SQL: ++ + +.. Navigate to SQL Server Management Studio. + +.. On the Connect to Server panel, select Windows Authentication and click Connect. + +.. Select File > Open > File and navigate to the OpenIDM data definition language script (`path\to\openidm\db\mssql\scripts\openidm.sql`). Click Open to open the file. + +.. Click Execute to run the script. + + +. This step creates an `openidm` database for use as the internal repository, and a user `openidm` with password `openidm` who has all the required privileges to update the database. You might need to refresh the view in SQL Server Management Studio to see the `openidm` database in the Object Explorer. ++ +Expand Databases > openidm > Tables. You should see the OpenIDM tables in the openidm database, as shown in the following example. ++ + +image::images/sql-tables.png[] ++ +The table names are similar to those used with OrientDB. + +. Execute the three scripts that set up the tables required by the Activiti workflow engine: ++ +You can use the `sqlcmd` command to execute the scripts, for example: ++ + +[source, console] +---- +PS C:\Users\Administrator> sqlcmd -S localhost -d openidm ^ + -i C:\path\to\openidm\db\mssql\scripts\activiti.mssql.create.engine.sql +PS C:\Users\Administrator> sqlcmd -S localhost -d openidm ^ + -i C:\path\to\openidm\db\mssql\scripts\activiti.mssql.create.history.sql +PS C:\Users\Administrator> sqlcmd -S localhost -d openidm ^ + -i C:\path\to\openidm\db\mssql\scripts\activiti.mssql.create.identity.sql +---- + +. OpenIDM requires an MS SQL driver that must be created from two separate JAR files. Create the driver as follows: ++ + +.. Download the JDBC Driver 4.1 for SQL Server (`sqljdbc_4.1.5605.100_enu.tar.gz`) from link:http://www.microsoft.com/en-us/download/details.aspx?id=11774[Microsoft's download site, window=\_top]. The precise URL might vary, depending on your location. ++ +Run the downloaded executable file; it should extract multiple files, include Java archive files, to a dedicated folder. ++ +Extract the executable Java archive file (`sqljdbc41.jar`) from the dedicated folder, using 7-zip or an equivalent file management application. ++ +Copy the Java archive file to `openidm\db\mssql\scripts`. + +.. Download the `bnd` Java archive file (link:https://repo1.maven.org/maven2/biz/aQute/bnd/1.50.0/bnd-1.50.0.jar[bnd-1.50.0.jar, window=\_top]) that enables you to create OSGi bundles. For more information about `bnd`, see link:http://www.aqute.biz/Bnd/Bnd[http://www.aqute.biz/Bnd/Bnd, window=\_top]. ++ +Copy the file to `openidm\db\mssql\scripts`. + +.. Your `openidm\db\mssql\scripts` directory should now contain the following files: ++ + +[source, console] +---- +bnd-1.50.0.jar openidm.sql sqljdbc4.bnd sqljdbc4.jar +---- + +.. Bundle the two JAR files together with the following command: ++ + +[source, console] +---- +C:\> cd \path\to\openidm\db\mssql\scripts +./> java -jar bnd-1.50.0.jar wrap -properties sqljdbc4.bnd sqljdbc41.jar +---- ++ +This step creates a single `.bar` file, named `sqljdbc41.bar`. + +.. Rename the `sqljdbc41.bar` file to `sqljdbc41-osgi.jar` and copy it to the `openidm\bundle` directory: ++ + +[source, console] +---- +./> ren sqljdbc41.bar sqljdbc41-osgi.jar +./> copy sqljdbc41-osgi.jar \path\to\openidm\bundle +---- + + +. Remove the default OrientDB repository configuration file (`repo.orientdb.json`) from your project's configuration directory. For example: ++ + +[source, console] +---- +C:\> cd \path\to\openidm\my-project\conf\ +.\> del repo.orientdb.json +---- + +. Copy the database connection configuration file for MS SQL (`datasource.jdbc-default.json`) and the database table configuration file (`repo.jdbc.json`) to your project's configuration directory. For example: ++ + +[source, console] +---- +C:\> cd \path\to\openidm +.\> copy db\mssql\conf\datasource.jdbc-default.json my-project\conf\ +.\> copy db\mssql\conf\repo.jdbc.json my-project\conf\ +---- + +. Update the connection configuration in `datasource.jdbc-default.json` to reflect your MS SQL deployment. The default connection configuration is as follows: ++ + +[source, javascript] +---- +{ + "driverClass" : "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "jdbcUrl" : "jdbc:sqlserver://localhost:1433;instanceName=default;databaseName=openidm;applicationName=OpenIDM", + "databaseName" : "openidm", + "username" : "openidm", + "password" : "openidm", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp" + } +} +---- ++ +Specifically, check that the host and port match what you have configured in MS SQL. + +==== +When you have completed the preceding steps, start OpenIDM to check that the setup has been successful. After startup, you should see that `repo.jdbc` is `active`, whereas `repo.orientdb` is `unsatisfied`: + +[source, console] +---- +C:> cd \path\to\openidm +./> startup.bat +"Using OPENIDM_HOME: \path\to\openidm" +"Using OPENIDM_OPTS: -Xmx1024m -Xms1024m -Dfile.encoding=UTF-8" +"Using LOGGING_CONFIG: +-Djava.util.logging.config.file=\path\to\openidm\conf\logging.properties" +Using boot properties at \path\to\openidm\conf\boot\boot.properties +-> scr list +Id State Name +... +[ 18] [unsatisfied ] org.forgerock.openidm.repo.orientdb +... +[ 17] [active ] org.forgerock.openidm.repo.jdbc +... +---- + + +[#repository-oracledb] +=== To Set Up OpenIDM With Oracle Database + +When implementing an Oracle database for OpenIDM, confer with an Oracle DBA when creating the database schema, tables, and users. This section assumes that you have configured an Oracle Database with link:http://docs.oracle.com/cd/B28359_01/network.111/b28317/tnsnames.htm[Local Naming Parameters (tnsnames.ora), window=\_blank] and a service user for use by OpenIDM. + +==== + +. Import the OpenIDM schema using the data definition language script (`/path/to/openidm/db/oracle/scripts/openidm.sql`), as the appropriate schema owner user. ++ +When you have run the script, you should be able to query the `internaluser` table. The query should return two records (`openidm-admin` and `anonymous`). The output here has been formatted for legibility: ++ + +[source, console] +---- +SQL> select * from internaluser; + +OBJECTID openidm-admin +----------------------------------------------------------------------------- +REV 0 +----------------------------------------------------------------------------- +PWD openidm-admin +----------------------------------------------------------------------------- +ROLES [ { "_ref" : "repo/internal/role/openidm-admin" }, + { "_ref" : "repo/internal/role/openidm-authorized" } ] +----------------------------------------------------------------------------- + +OBJECTID anonymous +----------------------------------------------------------------------------- +REV 0 +----------------------------------------------------------------------------- +PWD anonymous +----------------------------------------------------------------------------- +ROLES [ { "_ref" : "repo/internal/role/openidm-reg" } ] +----------------------------------------------------------------------------- +---- + +. Run the three scripts that set up the tables required by the Activiti workflow engine. ++ +You can use the Oracle SQL Developer Data Modeler to run the scripts, as described in the corresponding link:http://www.oracle.com/webfolder/technetwork/tutorials/obe/db/11g/r2/prod/appdev/sqldev/datamodel1moddb/datamodel1moddb_otn.htm[Oracle documentation, window=\_blank]. ++ +You must run the following scriptS ++ + +[source, console] +---- +/path/to/openidm/db/oracle/scripts/activiti.oracle.create.engine.sql +/path/to/openidm/db/oracle/scripts/activiti.oracle.create.history.sql +/path/to/openidm/db/oracle/scripts/activiti.oracle.create.identity.sql +---- + +. Create an Oracle DB driver from two separate jar files and set up the OpenIDM repository configuration for Oracle DB, as follows: ++ + +.. Download the Oracle JDBC driver for your Oracle DB version from link:http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html[Oracle Technology Network, window=\_blank] and place it in the `openidm/db/oracle/scripts` directory: ++ + +[source, console] +---- +$ ls /path/to/openidm/db/oracle/scripts +ojdbc7_g.jar openidm.sql +---- + +.. Create a bind file and edit it to match the version information for your JDBC driver. ++ +You can use the sample bind file located in `openidm/db/mssql/scripts`. Copy the bind file to the same location as the JDBC driver: ++ + +[source, console] +---- +$ cd /path/to/openidm/db +$ cp mssql/scripts/sqljdbc4.bnd oracle/scripts +$ ls oracle/scripts +ojdbc7_g.jar openidm.sql sqljdbc4.bnd +---- ++ +The JDBC driver version information for your driver is located in the `Specification-Version` property in the MANIFEST file of the driver: ++ + +[source, console] +---- +$ cd /path/to/openidm/db/oracle/scripts +$ unzip -q -c ojdbc7_g.jar META-INF/MANIFEST.MF +... +Specification-Vendor: Sun Microsystems Inc. +Specification-Title: JDBC +Specification-Version: 4.0 +... +---- ++ +Edit the bind file to match the JDBC driver version: ++ + +[source, console] +---- +$ more sqljdbc4.bnd +... +version=4.0 +Export-Package: *;version=${version} +Bundle-Name: Oracle JDBC Driver 4.0 for SQL Server +Bundle-SymbolicName: Oracle JDBC Driver 4.0 for SQL Server +Bundle-Version: ${version} +---- + +.. Download the `bnd` Java archive file (link:https://repo1.maven.org/maven2/biz/aQute/bnd/1.50.0/bnd-1.50.0.jar[bnd-1.50.0.jar, window=\_top]) that enables you to create OSGi bundles. For more information about `bnd`, see link:http://www.aqute.biz/Bnd/Bnd[http://www.aqute.biz/Bnd/Bnd, window=\_top]. ++ +Place the `bnd` Java archive file in the same directory as the JDBC driver, and the bind file: ++ + +[source, console] +---- +$ ls /path/to/openidm/db/oracle/scripts +bnd-1.50.0.jar ojdbc7_g.jar openidm.sql sqljdbc4.bnd +---- + +.. Change to the directory in which the script files are located and run the following command to create the OSGi bundle: ++ + +[source, console] +---- +$ cd /path/to/openidm/db/oracle/scripts +$ java -jar bnd-1.50.0.jar wrap -properties sqljdbc4.bnd ojdbc7_g.jar +Dec 10, 2013 9:53:28 AM java.util.prefs.FileSystemPreferences$1 run +INFO: Created user preferences directory. +ojdbc7_g.jar 984 0 +---- ++ +A new `.bar` file has now been created: ++ + +[source, console] +---- +$ ls +bnd-1.50.0.jar ojdbc7_g.bar ojdbc7_g.jar openidm.sql sqljdbc4.bnd +---- + +.. Move the `.bar` file to the `openidm/bundle` directory and rename it with a `.jar` extension. The actual name of the file is unimportant: ++ + +[source, console] +---- +$ mv ojdbc7_g.bar /path/to/openidm/bundle/ojdbc7_g-osgi.jar +---- + + +. Remove the default OrientDB configuration file (`repo.orientdb.json`) from your project's configuration directory. For example: ++ + +[source, console] +---- +$ rm /path/to/openidm/my-project/conf/repo.orientdb.json +---- + +. Copy the database connection configuration file for Oracle (`datasource.jdbc-default.json`) and the database table configuration file (`repo.jdbc.json`) to your project's configuration directory. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm/ +$ cp db/oracle/conf/datasource.jdbc-default.json my-project/conf/ +$ cp db/oracle/conf/repo.jdbc.json my-project/conf/ +---- + +. Update the connection configuration in `datasource.jdbc-default.json` to reflect your Oracle database deployment. The default connection configuration is as follows: ++ + +[source, javascript] +---- +{ + "driverClass" : "oracle.jdbc.OracleDriver", + "jdbcUrl" : "jdbc:oracle:thin:@//HOSTNAME:PORT/DEFAULTCATALOG", + "databaseName" : "openidm", + "username" : "openidm", + "password" : "openidm", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp" + } +} +---- ++ +The `"jdbcUrl"` corresponds to the URL of the Oracle DB listener, including the service name, based on your configured Local Naming Parameters (tnsnames.ora). It should be whatever is appropriate for your environment. ++ +The `DEFAULTCATALOG` should match the user who "owns" the tables. If your schema owner is `openidm`, the `DEFAULTCATALOG` should also be `openidm`. This will cause OpenIDM to generate queries such as `"SELECT objectid FROM openidm.internaluser"`. ++ +The `"username"` and `"password"` corresponds to the credentials of the service user that connects from OpenIDM. + +==== +When you have set up Oracle database for use as the OpenIDM internal repository, start OpenIDM to check that the setup has been successful. On startup, a number of INFO messages are output, as the predefined queries are processed. + +After startup, you should see that `repo.jdbc` is `active`, whereas `repo.orientdb` is `unsatisfied`: + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh +Using OPENIDM_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: +-Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/conf/boot/boot.properties +.... +-> scr list + Id State Name +... +[ 2] [unsatisfied ] org.forgerock.openidm.repo.orientdb +... +[ 3] [active ] org.forgerock.openidm.repo.jdbc +... +---- + + +[#repository-postgresql] +=== To Set Up OpenIDM With PostgreSQL + +This procedure assumes that PostgreSQL (version 9.3 or later) is installed and running on the local host. + +__Before starting OpenIDM for the first time__, set up OpenIDM to use a PostgreSQL repository, as described in the following procedure: + +==== + +. OpenIDM includes a script (`path/to/openidm/db/postgresql/scripts/createuser.pgsql`) that sets up an `openidm` database and user, with a default password of `openidm`. The script also grants the appropriate permissions. ++ +Edit this script if you want to change the password of the `openidm` user, for example: ++ + +[source, console] +---- +$ more /path/to/openidm/db/postgresql/scripts/createuser.pgsql +create user openidm with password 'mypassword'; +create database openidm encoding 'utf8' owner openidm; +grant all privileges on database openidm to openidm; +---- + +. As the `postgres` user, execute the `createuser.pgsql` script as follows: ++ + +[source, console] +---- +$ psql -U postgres < /path/to/openidm/db/postgresql/scripts/createuser.pgsql +CREATE ROLE +CREATE DATABASE +GRANT +---- + +. Execute the `openidm.pgsql` script as the new `openidm` user that you created in the first step: ++ + +[source, console] +---- +$ psql -U openidm < /path/to/openidm/db/postgresql/scripts/openidm.pgsql + +CREATE SCHEMA +CREATE TABLE +CREATE TABLE +CREATE TABLE +CREATE INDEX +CREATE INDEX +... +START TRANSACTION +INSERT 0 1 +INSERT 0 1 +COMMIT +CREATE INDEX +CREATE INDEX +---- ++ +Your database has now been initialized. + +. Run the three scripts that set up the tables required by the Activiti workflow engine: ++ + +[source, console] +---- +$ psql -d openidm -U openidm < /path/to/openidm/db/postgresql/scripts/activiti.postgres.create.engine.sql +$ psql -d openidm -U openidm < /path/to/openidm/db/postgresql/scripts/activiti.postgres.create.history.sql +$ psql -d openidm -U openidm < /path/to/openidm/db/postgresql/scripts/activiti.postgres.create.identity.sql +---- + +. Remove the OrientDB repository configuration file (`repo.orientdb.json`) from your project's configuration directory. For example: ++ + +[source, console] +---- +$ rm /path/to/openidm/my-project/conf/repo.orientdb.json +---- + +. Copy the database connection configuration file for PostgreSQL (`datasource.jdbc-default.json`) and the database table file (`repo.jdbc.json`) to your project's configuration directory. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ cp db/postgres/conf/datasource.jdbc-default.json my-project/conf/ +$ cp db/postgres/conf/repo.jdbc.json my-project/conf/ +---- + +. If you changed the password in step 1 of this procedure, edit the `datasource.jdbc-default.json` file to set the value for the `"password"` field to whatever password you set for the `openidm` user. For example, if you changed the connection password to `mypassword`, edit the file as follows: ++ + +[source, console] +---- +$ more conf/datasource.jdbc-default.json +{ + "driverClass" : "org.postgresql.Driver", + "jdbcUrl" : "jdbc:postgresql://localhost:5432/openidm", + "databaseName" : "openidm", + "username" : "openidm", + "password" : "mypassword", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp" + } +} +---- + +. PostgreSQL is now set up for use as the OpenIDM internal repository. ++ +Start OpenIDM to check that the setup has been successful. After startup, you should see that `repo.jdbc` is `active`, whereas `repo.orientdb` is `unsatisfied`: ++ + +[source, console] +---- +-> OpenIDM ready +scr list +Id State Name +... +[ 4] [unsatisfied ] org.forgerock.openidm.repo.orientdb +... +[ 3] [active ] org.forgerock.openidm.repo.jdbc +... +-> +---- + +. If you are using the default project configuration, run the `default_schema_optimization.pgsql` script to create the required indexes. This script must be executed by a user with SUPERUSER privileges, so that the extension can be created. By default, this is the `postgres` user. ++ +The file includes extensive comments on the indexes that are being created: ++ + +[source, console] +---- +$ psql -U postgres openidm < /path/to/openidm/db/postgresql/scripts/default_schema_optimization.pgsql +CREATE INDEX +CREATE INDEX +CREATE INDEX +CREATE INDEX +CREATE INDEX +CREATE INDEX +---- + +==== + + +[#repository-db2] +=== To Set Up OpenIDM With IBM DB2 + +This section makes the following assumptions about the DB2 environment. If these assumptions do not match your DB2 environment, adapt the subsequent instructions accordingly. + +* DB2 is running on the localhost, and is listening on the default port (50000). + +* The user `db2inst1` is configured as the DB2 instance owner, and has the password `Passw0rd1`. + +This section assumes that you will use basic username/password authentication. For instructions on configuring Kerberos authentication with a DB2 repository, see xref:#db2-kerberos-auth["Configuring OpenIDM for Kerberos Authentication With a DB2 Repository"]. + +==== +Before you start, make sure that OpenIDM is stopped. + +[source, console] +---- +$ cd /path/to/openidm/ +$ ./shutdown.sh +OpenIDM is not running, not stopping. +---- +Set up OpenIDM to use the DB2 repository, as described in the following steps. + +. Create a bundled DB2 JDBC driver, and copy it to the `openidm/bundle` directory, as follows: ++ + +.. Download the DB2 JDBC driver for your database version from the link:http://www-01.ibm.com/support/docview.wss?uid=swg21363866[IBM download site, window=\_blank] and place it in the `openidm/db/db2/scripts` directory. ++ +Use either the `db2jcc.jar` or `db2jcc4.jar`, depending on your DB2 version. For more information, see the link:http://www-01.ibm.com/support/docview.wss?uid=swg21363866[DB2 JDBC Driver Versions, window=\_top]. ++ + +[source, console] +---- +$ ls /path/to/db/db2/scripts/ +db2jcc.jar openidm.sql +---- + +.. Create a bind file and edit it to match the version information for your JDBC driver. ++ +You can use the sample bind file located in `openidm/db/mssql/scripts`. Copy the bind file to the same location as the JDBC driver. ++ + +[source, console] +---- +$ cd /path/to/openidm/db +$ cp mssql/scripts/sqljdbc4.bnd db2/scripts/ +$ ls db2/scripts +db2jcc.jar openidm.sql sqljdbc4.bnd +---- ++ +The JDBC driver version information for your driver is located in the `Specification-Version` property in the MANIFEST file of the driver. ++ + +[source, console] +---- +$ cd /path/to/openidm/db/db2/scripts +$ unzip -q -c db2jcc.jar META-INF/MANIFEST.MF +Manifest-Version: 1.0 +Created-By: 1.4.2 (IBM Corporation) +---- ++ +Edit the bind file to match the JDBC driver version. ++ + +[source, console] +---- +$ more sqljdbc4.bnd +... +version=1.0 +Export-Package: *;version=${version} +Bundle-Name: IBM JDBC DB2 Driver +Bundle-SymbolicName: com.ibm.db2.jcc.db2driver +Bundle-Version: ${version} +---- + +.. Download the `bnd` Java archive file (link:https://repo1.maven.org/maven2/biz/aQute/bnd/1.50.0/bnd-1.50.0.jar[bnd-1.50.0.jar, window=\_top]) that enables you to create OSGi bundles. For more information about `bnd`, see link:http://www.aqute.biz/Bnd/Bnd[http://www.aqute.biz/Bnd/Bnd, window=\_top]. ++ +Place the `bnd` Java archive file in the same directory as the JDBC driver, and the bind file. ++ + +[source, console] +---- +$ ls /path/to/openidm/db/db2/scripts +bnd-1.50.0.jar db2jcc.jar openidm.sql sqljdbc4.bnd +---- + +.. Change to the directory in which the script files are located and run the following command to create the OSGi bundle. ++ + +[source, console] +---- +$ cd /path/to/openidm/db/db2/scripts +$ java -jar bnd-1.50.0.jar wrap -properties sqljdbc4.bnd db2jcc.jar +Oct 01, 2015 11:50:56 PM java.util.prefs.FileSystemPreferences$1 run +INFO: Created user preferences directory. +db2jcc 1149 0 +---- ++ +A new `.bar` file has now been created. ++ + +[source, console] +---- +$ ls +bnd-1.50.0.jar db2jcc.bar db2jcc.jar openidm.sql sqljdbc4.bnd +---- + +.. Move the `.bar` file to the `openidm/bundle` directory and rename it with a `.jar` extension. The actual name of the file is unimportant. ++ + +[source, console] +---- +$ mv db2jcc.bar /path/to/openidm/bundle/db2jcc-osgi.jar +---- + + +. Remove the default OrientDB configuration file (`repo.orientdb.json`) from your project's configuration directory. For example: ++ + +[source, console] +---- +$ rm /path/to/openidm/my-project/conf/repo.orientdb.json +---- + +. Copy the database connection configuration file for DB2 (`datasource.jdbc-default.json`) and the database table configuration file (`repo.jdbc.json`) to your project's configuration directory. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm/ +$ cp db/db2/conf/datasource.jdbc-default.json my-project/conf/ +$ cp db/db2/conf/repo.jdbc.json my-project/conf/ +---- + +. Edit the `connection` property in the repository configuration file to match your DB2 environment. ++ +Update the connection configuration in `datasource.jdbc-default.json` to reflect your DB2 deployment. The default connection configuration is as follows: ++ + +[source, javascript] +---- +{ + "driverClass" : "com.ibm.db2.jcc.DB2Driver", + "jdbcUrl" : "jdbc:db2://HOSTNAME:PORT/dopenidm:retrieveMessagesFromServerOnGetMessage=true;", + "databaseName" : "sopenidm", + "username" : "openidm", + "password" : "openidm", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp" + } +} +---- + +. Create a user database for OpenIDM (`dopenidm`). ++ + +[source, console] +---- +$ db2 create database dopenidm +---- + +. Import the data definition language script for OpenIDM into your DB2 instance. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ db2 -i -tf db/db2/scripts/openidm.sql +---- ++ +The database schema is defined in the `SOPENIDM` database. + +. You can show the list of tables in the repository, using the `db2 list` command, as follows: ++ + +[source, console] +---- +$ db2 LIST TABLES for all + + Table/View Schema Type Creation time +------------------------------- --------------- ----- -------------------------- +AUDITACCESS SOPENIDM T 2015-10-01-11.58.04.313685 +AUDITACTIVITY SOPENIDM T 2015-10-01-11.58.03.671342 +AUDITAUTHENTICATION SOPENIDM T 2015-10-01-11.58.02.159573 +AUDITCONFIG SOPENIDM T 2015-10-01-11.58.03.307248 +AUDITRECON SOPENIDM T 2015-10-01-11.58.02.526214 +AUDITSYNC SOPENIDM T 2015-10-01-11.58.02.936434 +CLUSTEROBJECTPROPERTIES SOPENIDM T 2015-10-01-11.58.05.968933 +CLUSTEROBJECTS SOPENIDM T 2015-10-01-11.58.05.607075 +CONFIGOBJECTPROPERTIES SOPENIDM T 2015-10-01-11.58.01.039999 +CONFIGOBJECTS SOPENIDM T 2015-10-01-11.58.00.570231 +GENERICOBJECTPROPERTIES SOPENIDM T 2015-10-01-11.57.59.583530 +GENERICOBJECTS SOPENIDM T 2015-10-01-11.57.59.152221 +INTERNALUSER SOPENIDM T 2015-10-01-11.58.04.060990 +LINKS SOPENIDM T 2015-10-01-11.58.01.349194 +MANAGEDOBJECTPROPERTIES SOPENIDM T 2015-10-01-11.58.00.261556 +MANAGEDOBJECTS SOPENIDM T 2015-10-01-11.57.59.890152 +... +---- ++ +The table names are similar to those used with OrientDB. + +. Connect to the openidm database, then run the three scripts that set up the tables required by the Activiti workflow engine: ++ + +[source, console] +---- +$ db2 connect to dopenidm +$ db2 -i -tf /path/to/openidm/db/db2/scripts/activiti.db2.create.engine.sql +$ db2 -i -tf /path/to/openidm/db/db2/scripts/activiti.db2.create.history.sql +$ db2 -i -tf /path/to/openidm/db/db2/scripts/activiti.db2.create.identity.sql +---- + +==== +When you have set up DB2 for use as the OpenIDM internal repository, start OpenIDM to check that the setup has been successful. After startup, you should see that `repo.jdbc` is `active`, whereas `repo.orientdb` is `unsatisfied`. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh +Using OPENIDM_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: +-Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> scr list + +Id State Name +[ 19] [active ] org.forgerock.openidm.config.starter +[ 23] [active ] org.forgerock.openidm.taskscanner +[ 8] [active ] org.forgerock.openidm.external.rest +[ 12] [active ] org.forgerock.openidm.provisioner.openicf.connectorinfoprovider +[ 15] [active ] org.forgerock.openidm.ui.simple +[ 1] [active ] org.forgerock.openidm.router +[ 22] [active ] org.forgerock.openidm.scheduler +[ 14] [active ] org.forgerock.openidm.restlet +[ 7] [unsatisfied ] org.forgerock.openidm.external.email +[ 18] [unsatisfied ] org.forgerock.openidm.repo.orientdb +[ 6] [active ] org.forgerock.openidm.sync +[ 3] [active ] org.forgerock.openidm.script +[ 5] [active ] org.forgerock.openidm.recon +[ 2] [active ] org.forgerock.openidm.scope +[ 10] [active ] org.forgerock.openidm.http.contextregistrator +[ 20] [active ] org.forgerock.openidm.config +[ 0] [active ] org.forgerock.openidm.audit +[ 21] [active ] org.forgerock.openidm.schedule +[ 17] [active ] org.forgerock.openidm.repo.jdbc +[ 16] [active ] org.forgerock.openidm.workflow +[ 13] [active ] org.forgerock.openidm.provisioner.openicf +[ 4] [active ] org.forgerock.openidm.managed +[ 9] [active ] org.forgerock.openidm.authentication +[ 11] [active ] org.forgerock.openidm.provisioner +---- + +[#db2-kerberos-auth] +==== Configuring OpenIDM for Kerberos Authentication With a DB2 Repository + +By default, OpenIDM uses the username and password configured in the repository connection configuration file (`conf/datasource.jdbc-default.json`) to connect to the DB2 repository. You can configure OpenIDM to use Kerberos authentication instead. + +In this scenario, OpenIDM acts as a __client__ and requests a Kerberos ticket for a __service__, which is DB2, through the JDBC driver. + +This section assumes that you have configured DB2 for Kerberos authentication. If that is not the case, follow the instructions in the corresponding link:https://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.1.0/com.ibm.db2.luw.admin.sec.doc/doc/c0058525.html[DB2 documentation, window=\_blank] before you read this section. + +The following diagram shows how the ticket is obtained and how the keytab is referenced from OpenIDM's `jaas.conf` file. + +[#d9505e2524] +image::images/db2-kerberos.png[] + +==== +To configure OpenIDM for Kerberos authentication: + +. Create a keytab file, specifically for use by OpenIDM. ++ +A Kerberos keytab file (`krb5.keytab`) is an encrypted copy of the host's key. The keytab enables DB2 to validate the Kerberos ticket that it receives from OpenIDM. You must create a keytab file on the host that OpenIDM runs on. The keytab file must be secured in the same way that you would secure any password file. Specifically, only the user running OpenIDM should have read and write access to this file. ++ +Create a keytab for DB2 authentication, in the file `openidm/security/idm.keytab/`: ++ + +[source, console] +---- +$ kadmin -p kadmin/admin -w password +$ kadmin: ktadd -k /path/to/openidm/security/idm.keytab db2/idm.example.com +---- + +. Make sure that the DB2 user has read access to the keytab. + +. Copy the DB2 Java Authentication and Authorization Service (JAAS) configuration file to the OpenIDM `security` directory: ++ + +[source, console] +---- +$ cd path/to/openidm +$ cp db/db2/conf/jaas.conf security/ +---- ++ +By default, OpenIDM assumes that the keytab is in the file `openidm/security/idm.keytab` and that the principal identity is `db2/idm.example.com@EXAMPLE.COM`. Change the following lines in the `jaas.conf` file if you are using a different keytab: ++ + +[source] +---- +keyTab="security/idm.keytab" principal="db2/idm.example.com@EXAMPLE.COM" +---- + +. Adjust the authentication details in your DB2 connection configuration file (`conf/datasource.jdbc-default.json`). Edit that file to remove `password` field and change the username to the instance owner (`db2`). The following excerpt shows the modified file: ++ + +[source, javascript] +---- +{ + ... + "databaseName" : "sopenidm", + "username" : "db2", + "connectionTimeout" : 30000, + ... +} +---- + +. Edit your project's `conf/system.properties` file, to add the required Java options for Kerberos authentication. ++ +In particular, add the following two lines to that file: ++ + +[source] +---- +db2.jcc.securityMechanism=11 +java.security.auth.login.config=security/jaas.conf +---- + +. Restart OpenIDM. + +==== + + + diff --git a/openidm-doc/src/main/asciidoc/install-guide/chap-uninstall.adoc b/openidm-doc/src/main/asciidoc/install-guide/chap-uninstall.adoc new file mode 100644 index 000000000..7e1c6189e --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/chap-uninstall.adoc @@ -0,0 +1,80 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-uninstall] +== Removing and Moving OpenIDM Software + +This chapter shows you how to uninstall OpenIDM software and to move an existing install to a different location. + +[#d9505e2624] +.To Remove OpenIDM Software +==== + +. (Optional) Stop OpenIDM services if they are running, as described in xref:chap-install.adoc#stop-openidm["To Stop the OpenIDM Services"]. + +. Remove the file system directory where you installed OpenIDM software: ++ + +[source, console] +---- +$ rm -rf /path/to/openidm +---- + +. (Optional) If you use a JDBC database for the internal repository, you can drop the `openidm` database. + +==== + +[#d9505e2646] +.To Move OpenIDM Software +==== +If you want to move OpenIDM to a different directory, you do not have to uninstall and reinstall. To move an existing OpenIDM instance, follow these steps: + +. Shut down OpenIDM, as described in xref:chap-install.adoc#stop-openidm["To Stop the OpenIDM Services"]. + +. Remove the `felix-cache` directory: ++ + +[source, console] +---- +$ cd path/to/openidm +$ rm -rf felix-cache +---- + +. Move the files: ++ + +[source, console] +---- +$ mv path/to/openidm path/to/new-openidm +---- + +. Start OpenIDM in the new location: ++ + +[source, console] +---- +$ cd path/to/new-openidm +$ ./startup.sh +---- + +==== + diff --git a/openidm-doc/src/main/asciidoc/install-guide/chap-update.adoc b/openidm-doc/src/main/asciidoc/install-guide/chap-update.adoc new file mode 100644 index 000000000..90cd0cbfc --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/chap-update.adoc @@ -0,0 +1,1138 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-update] +== Updating OpenIDM + +The update process is largely dependent on your deployment and on the extent to which you have customized OpenIDM. Engage link:https://www.forgerock.com/support/support-services[ForgeRock Support Services, window=\_top] for help in updating an existing deployment. + +If you are updating from OpenIDM 4.0, you can take advantage of new OpenIDM update options from the command line and the Admin UI. For a generic view of the update process, see xref:#update-process["An Overview of the OpenIDM Update Process"]. + +If you are migrating a deployment from earlier versions of OpenIDM, follow the manual update process. + +* To migrate a deployment from OpenIDM 3.1.0 to OpenIDM 4.0, see link:../../../openidm/4/install-guide/#migrate-idm-314[Migrating From OpenIDM 3.1 to OpenIDM 4.0, window=\_blank] in the __OpenIDM 4 Installation Guide__. + +* To migrate a deployment from OpenIDM 3.0.0 to 3.1.0, see link:../../../openidm/3.1/install-guide/#chap-upgrade[Migrating to OpenIDM 3.1.0, window=\_blank] in the __OpenIDM 3.1.0 Installation Guide__. + +* To migrate a deployment from OpenIDM 2.1.0 to 3.0.0, see link:../../../openidm/3/install-guide/#chap-upgrade[Migrating to OpenIDM 3.0.0, window=\_blank] in the __OpenIDM 3.0.0 Installation Guide__. + + +[#update-process] +=== An Overview of the OpenIDM Update Process + +This section provides background information on the OpenIDM update process. If you want to jump to the steps required to update OpenIDM from version 4.0 to 4.5, read xref:#migrate-idm-40-45["Updating From OpenIDM 4.0 to OpenIDM 4.5"]. + +OpenIDM must be running when you launch an update (using the UI or the CLI). This section details the capabilities of the OpenIDM update process, in the following sub-sections: + +* xref:#install-update-process["The OpenIDM Update Process in Labels"] + +* xref:#update-process-checksums["OpenIDM Update Checksums"] + +* xref:#openidm-update-repos["Repository Scripts and the OpenIDM Update Process"] + +* xref:#update-idm-cli["Command Line Update Options"] + +* xref:#update-file-states["OpenIDM Update File States"] + + +[WARNING] +==== +Before starting the update process, back up your existing deployment by archiving the `openidm` directory, as well as the contents of your supported repository. As there is no "undo" option available during the OpenIDM update process, this backup can be used to restore your deployment or restart the update process if something goes wrong. + +Save any customized `*.json` configuration files, typically located in your project's `conf/` subdirectory. You'll need to make judgements on how to apply these customizations to your OpenIDM 4.5 deployment after the update is complete. +==== + +[#install-update-process] +==== The OpenIDM Update Process in Labels + +When you start an update, OpenIDM goes through a process to determine the files to be updated, to facilitate repository updates, to move OpenIDM into maintenance mode before implementing an update, and then to restart OpenIDM after the update is complete. Most of these steps happen automatically when you run the update from the UI or CLI. The following basic steps include labels that you might see in log files, if there are problems. + +* `PREVIEW_ARCHIVE`: Preview the archive using appropriate checksums, to define the files to be updated. + +* `ACCEPT_LICENSE`: Present the appropriate license for the update. If the license is not accepted, the update stops, and you retain all files in your current OpenIDM installation. + +* `LIST_REPO_UPDATES`: Identify and save scripts to update the current repository. + +* `PAUSING_SCHEDULER`: Pause the scheduler, to prevent new jobs from starting during the update process. + +* `WAIT_FOR_JOBS_TO_COMPLETE`: Wait for any currently running jobs to complete. + +* `ENTER_MAINTENANCE_MODE`: Set OpenIDM in maintenance mode, which disables non-essential OpenIDM services. + +* `INSTALL_ARCHIVE`: Install the update archive into OpenIDM. + +* `WAIT_FOR_INSTALL_DONE`: Wait until the installation of the update archive is complete. + +* `MARK_REPO_UPDATES_COMPLETE`: Note status when repository updates are complete. + +* `EXIT_MAINTENANCE_MODE`: Exit from maintenance mode. + +* `ENABLE_SCHEDULER`: Enable the scheduler. OpenIDM will re-establish any schedules that were previously in place. + +* `FORCE_RESTART` Restart OpenIDM to implement all changes that require it. + + + +[#update-process-checksums] +==== OpenIDM Update Checksums + +OpenIDM 4.0 and above includes MD5 checksums for each file. When updating, these checksums allow OpenIDM to verify file changes. + +The update process compares the current checksum of every file with: + +* The checksum of the file as originally installed. + +* The checksum of any file included in the update or patch. + +As described in xref:#migrate-idm-40-45["Updating From OpenIDM 4.0 to OpenIDM 4.5"], updates work in the OpenIDM `project-dir`, either in the default directory, `/path/to/openidm`, or outside of the OpenIDM directory tree, such as `/home/project`. If that `project-dir` is outside of the OpenIDM directory tree, there is no MD5 checksum for that file. Based on that information, the update mechanism takes the following actions: +-- + +Files in `openidm/bundle`:: +Because of the nature of Java archives in the `openidm/bundle` directory, OpenIDM updates all files in that directory, even if they have not changed. + ++ +New files are written to the target directory. + ++ +Existing files are saved with a .old-__unix_time__ extension. + +Files in `openidm/bin` and `openidm/ui/*/default`:: +New files are written to the target directory. + ++ +If the original file was customized, it is saved with a .old-__unix_time__ extension. + +`project-dir/script/access.js`:: +The update process writes `access.js` files with a `.new-unix_time` extension in the same directory. You __must__ merge any changes from the latest version of the `.new` file into your customized script file. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. + +Files in the `project-dir/conf` directory, outside of the `/path/to/openidm` directory tree:: +Project configuration files (JSON): Files related to your project are patched with the content of the corresponding 4.5 configuration file, regardless of whether they have been customized. ++ + +[WARNING] +====== +If you have a customized version of standard OpenIDM 4.0 JSON configuration files, be careful. If you overwrite a new OpenIDM 4.5 version of that file, you might overwrite new features. As a best practice, apply and test each custom change to the new OpenIDM 4.5 configuration file. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. +====== ++ +System configuration files: (`boot.properties`, `system.properties`, `config.properties`, and `jetty.xml`) are written with a `.-new-unix_time` extension in the same directory, regardless of whether they have been customized. You __must__ merge any changes from the latest version of the `.new` file into your existing configuration file. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. Currently, the `logging.properties` file is not addressed by the update process, as the default version of this file has not changed since OpenIDM 4.0. + +Files in the default `project-dir/conf` directory, where `project-dir` is `/path/to/openidm`:: +Project configuration files (JSON): Files related to your project are patched with the content of the corresponding 4.5 configuration file, regardless of whether they have been customized. ++ + +[WARNING] +====== +If you have a customized version of standard OpenIDM 4.0 JSON configuration files in your project directory, be careful. If you overwrite a new OpenIDM 4.5 version of that file, you might overwrite new features. As a best practice, apply and test each custom change to the new OpenIDM 4.5 configuration file. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. +====== ++ +System configuration files: (`boot.properties`, `system.properties`, `config.properties`, and `jetty.xml`) are not patched if they have been customized. Instead, the update process creates configuration files with a `.new-unix_time` extension in the same directory. You __must__ merge any changes from these `.new-` files into your customized configuration files. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. If you have not customized these files, the update process replaces the existing configuration file with the corresponding 4.5 file. Currently, the `logging.properties` file is not addressed by the update process, as the default version of this file has not changed since OpenIDM 4.0. + +Files in any other directory:: +Existing files are overwritten and no backup files are created. + +Configuration in the repository:: +OpenIDM configuration information is stored in your supported repository. The update process overwrites information in that data store. + +-- + +[NOTE] +==== +The `unix_time` is the number of seconds since the `Unix Epoch` of January 1, 1970. +==== +For a list of checksums, review the `openidm/.checksums.csv` file. It contains a list of checksums for every original file in your `openidm/` directory. + +You need to copy update archives, in zip format, to the `openidm/bin/update` directory. OpenIDM creates that directory during the start process. + + +[#openidm-update-repos] +==== Repository Scripts and the OpenIDM Update Process + +If there are update scripts for your OpenIDM repository, you may want to get Database Administrator (DBA) help and approval for those updates. + +Review applicable repository update scripts from the OpenIDM update binary. You can find these scripts in the following directory: `/path/to/openidm/db/repo/scripts/updates`. + +Apply the repository update scripts, while OpenIDM is not running, or is in maintenance mode. You'll need to apply these scripts in __numeric__ order. For example, if you see the following list: + +[source, console] +---- +v3_add_indices_for_roles.sql +v2_shorten_link_columns.sql +v1_increase_changedfields_size.sql +---- +Apply the `v1_*` script first, followed by the `v2_*` script, and so on. The update process will ask you to confirm that you've applied the required updates. + + +[#update-idm-cli] +==== Command Line Update Options + +As noted in xref:#migrate-idm-40-45["Updating From OpenIDM 4.0 to OpenIDM 4.5"], you can update OpenIDM 4.0 to OpenIDM 4.5 via the UNIX/Linux CLI. You'll find detailed information on the `cli.sh update` option in this section. For general information on `cli.sh` and `cli.bat`, see xref:../integrators-guide/chap-cli.adoc#chap-cli["OpenIDM Command-Line Interface"] in the __Integrator's Guide__. + +The following command updates the local system with the `openidm-new.zip` binary: + +[source, console] +---- +$ cd /path/to/openidm +$ ./cli.sh update \ +--acceptLicense \ +--user openidm-admin:openidm-admin \ +--url http://localhost:8080/openidm \ +openidm-new.zip +---- +-- +The `update` subcommand takes the following options: + +`-u` or `--user` USER[:PASSWORD]:: +Allows you to specify the server user and password. Specifying a username is mandatory. If you do not specify a username, the following error is shown in the OSGi console: `Remote operation failed: Unauthorized`. If you do not specify a password, you are prompted for one. This option is used by all three subcommands. + +`--url` URL:: +The URL of the OpenIDM REST service. The default URL is `\http://localhost:8080/openidm/`. This can be used to import configuration files from a remote running instance of OpenIDM. This option is used by all three subcommands. + +`-P` or `--port` PORT:: +The port number associated with the OpenIDM REST service. If specified, this option overrides any port number specified with the `--url` option. The default port is 8080. This option is used by all three subcommands. + +`--acceptLicense`:: +Automatically accept the license shown in `/path/to/openidm/legal-notices/Forgerock_License.txt`. If you omit this option, the update process prompts you to accept or decline the license. + +`--skipRepoUpdatePreview`:: +Bypasses a preview of repository updates. Suitable if you have already downloaded and approved changes to your repository. ++ + +[WARNING] +====== +Do not use the `--skipRepoUpdatePreview` option until you (or your DBA) has reviewed repository update scripts. +====== + +`--maxJobsFinishWaitTimeMs` TIME:: +The maximum time, in milliseconds, that the command should wait for scheduled jobs to finish before proceeding with the update. + ++ +Default: `-1`, (the process exits immediately if any jobs are running) + +`--maxUpdateWaitTimeMs` TIME:: +The maximum time, in milliseconds, that the server should wait for the update process to complete. + ++ +Default: `30000` ms + +`-l` or `--log` LOG_FILE:: +Path to the log file. + ++ +Default: `logs/update.log` + +`-Q` or `--quiet`:: +Use quiet mode to minimize messages at the console; messages are still available in the log file defined by `--log`. ++ + +[NOTE] +====== +If you use `--quiet` mode for updates, include the `--acceptLicense` option. +====== + +-- +If you do not run the command in quiet mode, messages similar to the following are displayed in the console window where you launched the command: + +[source, console] +---- +Executing ./cli.sh... +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Pausing the Scheduler +Scheduler has been paused. +Waiting for running jobs to finish. +All running jobs have finished. +Entering into maintenance mode... +Now in maintenance mode. +Installing the update archive openidm-new.zip +Update procedure is still processing... +Update procedure is still processing... +Update procedure is still processing... +Update procedure is still processing... +Update procedure is still processing... +The update process is complete with a status of COMPLETE +Restarting OpenIDM. +Restart request completed. +---- + + +[#update-file-states] +==== OpenIDM Update File States + +During the update process, you may see status information for each file, during three stages of an update: + +* xref:#update-file-preview["Preview of File Updates"] + +* xref:#update-file-during["Update Status Message"] + +* xref:#update-file-after["Updated Files: What Happened"] + + +[#update-file-preview] +.Preview of File Updates +[cols="20%,80%"] +|=== +|Status |Description + +a|UNEXPECTED +a|Existing file is not in the list of known files for the original distribution. + +a|NONEXISTENT +a|A file in the new installation that does not exist in the original distribution. This is always the status for __versioned__ files, such as the `openidm-*.jar` files in the `openidm/bundle/` directory. + +a|DELETED +a|The file should exist in the current installation but does not; OpenIDM installs the file during the update. + +a|DIFFERS +a|The file, located in a read-only directory, has changed, since the original deployment. + +a|UNCHANGED +a|The file is not changed from the original distribution. +|=== + +[#update-file-during] +.Update Status Message +[cols="40%,60%"] +|=== +|Status |Description + +a|IN_PROGRESS +a|Update has started, not yet complete + +a|PENDING_REPO_UPDATES +a|OpenIDM update is complete; however, repository updates are pending + +a|COMPLETE +a|Update is complete + +a|FAILED +a|Update failed +|=== + +[#update-file-after] +.Updated Files: What Happened +[cols="25%,75%"] +|=== +|Status |Description + +a|REPLACED +a|Original file replaced; if the original file was changed by a user, it is saved with a `.old` extension. + +a|PRESERVED +a|Original file saved with changes made by a user. New file saved with a `.new` extension. + +a|APPLIED +a|The update changed the file. + +a|REMOVED +a|The update removed the file. +|=== + + + +[#migrate-idm-40-45] +=== Updating From OpenIDM 4.0 to OpenIDM 4.5 + +The release of OpenIDM 4.5 includes additional automation in the update service for deployments installed on UNIX/Linux systems, including repository updates. + +If you've installed OpenIDM on Microsoft Windows, you'll have to migrate your systems manually. For the procedure, see xref:#migrate-idm-4045-windows["Migrating From OpenIDM 4.0 to OpenIDM 4.5 on Windows"]. + +[NOTE] +==== +The update process works for an OpenIDM project directory in the following locations: + +* The default OpenIDM project directory, `/path/to/openidm` + +* Outside of the OpenIDM directory tree, such as `/home/project` or `/other/hard_drive/idm` ++ +If you configure an OpenIDM project directory such as `/home/project`, do start OpenIDM with the full path to that project, with a command such as the following: ++ + +[source, console] +---- +$ ./startup.sh -p /home/project +---- + +The update process does not support changes to any project directory when configured as a subdirectory of `/path/to/openidm`. That includes the samples listed in the `/path/to/openidm/samples` directory. For more information on the samples, see xref:../samples-guide/chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"] in the __Samples Guide__. + +OpenIDM documentation represents the project directory as `project-dir`. + +It is an OpenIDM best practice to copy the default project or sample to an external installation `project-dir` directory, such as `/path/to/project`. If needed, this is an opportunity to move the `project-dir` to such a location, to facilitate the OpenIDM update process. +==== + +[WARNING] +==== +Before you start, back up your OpenIDM 4.0 systems, including your OpenIDM database. As OpenIDM updates are a one-way process, you should have a backup in case of problems. If needed, you must restart the update process from that backup. + +Updating nodes from a cluster is not presently supported. As a general practice, do not apply the update process to more than one node in a cluster. if you're updating a cluster, follow these steps: + +* Redirect client traffic to a different OpenIDM system or cluster. + +* Shut down every node in the cluster. + +* Update one node. + +* Clone the first node to the other OpenIDM instances in that cluster. + +If you have integrated OpenIDM with OpenAM, you should first disable the `OPENAM_SESSION` module, as described in xref:../samples-guide/chap-fullstack-sample.adoc#configure-fullstack-sample["Configuring OpenIDM for the Full Stack Sample"] in the __Samples Guide__. You can re-enable the `OPENAM_SESSION` module after the update is complete. + +Make sure you've saved any customized `*.json` configuration files, typically in your project's `conf/` subdirectory. You'll need these files after the update process is complete. + +If your OpenIDM project directory is located on a read-only volume, mount that directory in read-write mode before starting the update process. +==== +Because of the transition between the OpenIDM 4.0 and OpenIDM 4.5 update services, updating from OpenIDM 4.0 is a multi-stage process that requires two update patches in addition to the OpenIDM 4.5 binary. This section starts with an overview, with links to detailed subsections: + +* Download the update binaries. ++ +To update from OpenIDM 4.0 to OpenIDM 4.5, navigate to ForgeRock's link:https://backstage.forgerock.com/[BackStage, window=\_blank] site and download the following binaries: ++ + +** Update Patch 1 (`openidm-4.0.0-1.zip`) + +** Update Patch 2 (`openidm-4.0.0-2.zip`) + +** OpenIDM 4.5 (`openidm-4.5.0.zip`) + ++ +Access to the update binaries is restricted to ForgeRock customers. + +* Before starting the update process, extract and apply the repository update scripts from the OpenIDM 4.5 binary. You may want to share them with your Database Administrator (DBA). For more information, see xref:#update-4045-repo["Updating OpenIDM 4.0, Repository Scripts"]. + +* Before starting the update process, identify files in custom directories not known to OpenIDM. Save them, and apply them to your OpenIDM deployment after all stages of the update process are complete. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. ++ + +[WARNING] +==== +If you use anything but the standard OpenIDM Admin and Self-Service UIs, this issue related to custom directories applies to you. If you followed the procedure described in xref:../integrators-guide/chap-ui.adoc#ui-customizing["Customizing the UI"] in the __Integrator's Guide__, you'll have custom files in the `openidm/ui/admin/extension` and `openidm/ui/selfservice/extension` directories. +OpenIDM 4.5 includes significant UI improvements. The update process does not copy those improvements to the noted `extension/` subdirectories. +==== + +* When you're ready to start the first stage of the update process, see xref:#update-4045-stage1["Updating OpenIDM 4.0, Stage One"]. + +* When you're ready to start the second stage of the update process, see xref:#update-4001-4002["Updating OpenIDM 4.0.0, Stage Two"]. + +* When you're ready for the main part of the update, see xref:#update-4002-45["Updating OpenIDM 4.0, Stage Three"]. + +* Once the update is complete, you may have additional work before putting your system back into production. Start with files that include `.new-unix_time` extensions. For more information, see xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. + + +[#update-4045-repo] +==== Updating OpenIDM 4.0, Repository Scripts + +Review repository changes between OpenIDM 4.0 and OpenIDM 4.5. You can find update scripts in an unpacked OpenIDM 4.5 binary, in the `openidm/db/repo/scripts/updates` directory, where __repo__ is the subdirectory for your supported repository. + +Each supported repository includes the following scripts: + +[source, console] +---- +v1_increase_changedfields_size.sql +v2_shorten_link_columns.sql +---- +If you're running PostgreSQL, OpenIDM 4.5 includes two additional scripts: + +[source, console] +---- +v3_add_indices_for_roles.sql +v4_modify_indices_for_relationships.sql +---- +You can extract repository files individually; for example, to extract `v1_increase_changedfields_size.sql` for MySQL, run the following command: + +[source, console] +---- +$ unzip -p openidm-4.5.0.zip \ +openidm/db/mysql/scripts/updates/v1_increase_changedfields_size.sql \ +> v1_increase_changedfields_size.sql +---- +If you need DBA approval to update the OpenIDM repository, share these scripts with your DBA. Before updating from OpenIDM 4.0 to OpenIDM 4.5, apply these scripts now, in numeric order. In other words, apply the script that starts with `v1_*` first, followed by `v2_*` and so on. + +Once you have approval, you can shut down OpenIDM and apply these scripts immediately; for example, the following commands apply these scripts to a MySQL repository: + +[source, console] +---- +$ mysql -u root -p < /path/to/openidm/db/mysql/scripts/updates/v1_increase_changedfields_size.sql +$ mysql -u root -p < /path/to/openidm/db/mysql/scripts/updates/v2_shorten_link_columns.sql +---- + +[NOTE] +==== +The OpenIDM repository update scripts address the differences between the OpenIDM 4.0 and OpenIDM 4.5 supported repositories. They may not address any custom schema, columns, or tables that you have implemented in production. + +As OrientDB is not supported in production, ForgeRock does not support updates of deployments with that repository, and OpenIDM 4.5 does not include OrientDB update scripts. +==== + + +[#update-4045-stage1] +==== Updating OpenIDM 4.0, Stage One + +Now you're ready to start the first part of the update process, where you will use the OpenIDM 4.0 update facility to include several OpenIDM 4.5 bundles. + +[#update-to-4001-linux] +.Updating OpenIDM 4.0, Stage One for UNIX/Linux Systems +==== + +. Start the OpenIDM 4.0 system that you want to update: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p /path/to/project-dir +---- ++ + +[NOTE] +====== +OpenIDM must be running before you can execute all three stages of the update procedure. If you're running OpenIDM with an external `project-dir`, specify the full path to that directory. +====== + +. Run the following REST call to patch the configuration of your repository. This will speed up this first part of the update process, and minimize the risks of timeout-related issues. ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--request PATCH \ +--data '[ + { + "operation":"replace", + "field":"/resourceMapping/genericMapping/updates/searchableDefault", + "value": false + }, + { + "operation":"add", + "field":"/resourceMapping/genericMapping/updates/properties", + "value": { + "/startDate" : { + "searchable" : true + } + } + } +]' \ +http://localhost:8080/openidm/config/repo.jdbc +---- ++ +The output from this command includes the revised contents of your repository configuration. ++ +If you see a 404 error from this REST call, you might not have configured a supported JDBC repository, as described in xref:chap-repository.adoc#chap-repository["Installing a Repository For Production"]. + +. Copy the first update binary, `openidm-4.0.0-1.zip` , to the noted directory: ++ + +[source, console] +---- +$ cd /path/to/Downloads +$ cp openidm-4.0.0-1.zip /path/to/openidm/bin/update/ +---- + +. You can run the next step(s) either from the CLI or the Admin UI. ++ + +* `CLI`: Run the first part of the update from the command line: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./cli.sh update \ +--user openidm-admin:openidm-admin \ +--url http://localhost:8080/openidm \ +openidm-4.0.0-1.zip +---- ++ + +[NOTE] +====== +If you are using a port other than `8080`, include `--port number` in the `./cli.sh update` command. +====== ++ +You'll be prompted to accept a license. If you're scripting the update, you can add an `--acceptLicense` switch to the command. ++ +You'll see a series of messages that end with: ++ + +[source, console] +---- +Restart request completed. +---- ++ +A short time later, you'll see the following messages in the OpenIDM console: ++ + +[source, console] +---- +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> OpenIDM ready +---- ++ + +[NOTE] +====== +If you want to set up a script for this process, note the delay between the `Restart request completed` and `OpenIDM Ready` messages. +====== + +* `Admin UI`: You can also run the first part of the update from the Admin UI at `\http://localhost:8080/admin`. ++ +Navigate to Configure > System Preferences > Update. The instructions in the UI are intuitive. You should see an `Installation Preview` screen with a list of affected files, in the categories described in xref:#update-file-preview["Preview of File Updates"]. Afterwards, you'll also see an `Installing Update` screen with a list of files that have been updated. ++ +Scroll to the bottom of the Admin UI. After refreshing your browser, you should see the updated version of OpenIDM (4.0.0-1) in the footer of the web page. You can also see the updated version by navigating to Configure > System Preferences > Update. ++ + +[NOTE] +====== +If you see a pop-up window to log into the OSGi Management Console, select Cancel. +====== + + +==== + + +[#update-4001-4002] +==== Updating OpenIDM 4.0.0, Stage Two + +Now you're ready for stage two, which will install additional enhancements to the update process. + +[#update-to-4002] +==== + +. To prevent conflicts, remove the first update binary from the `/path/to/openidm/bin/update` directory: ++ + +[source, console] +---- +$ rm /path/to/openidm/bin/update/openidm-4.0.0-1.zip +---- + +. Copy the second update binary, `openidm-4.0.0-2.zip`, to the noted directory: ++ + +[source, console] +---- +$ cp openidm-4.0.0-2.zip /path/to/openidm/bin/update/ +---- + +. You can run the next steps either from the CLI or the Admin UI: ++ + +* `CLI`: Run the second part of the update from the command line: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./cli.sh update \ +--acceptLicense \ +--user openidm-admin:openidm-admin \ +--url http://localhost:8080/openidm \ +openidm-4.0.0-2.zip +---- ++ + +[NOTE] +====== +If you are using a port other than `8080`, specify the port number. Include `--port number` in the `./cli.sh update` command. +====== ++ +The process takes longer than xref:#update-4045-stage1["Updating OpenIDM 4.0, Stage One"]. You'll see a series of messages that include: ++ + +[source, console] +---- +... +Pausing the Scheduler +Scheduler has been paused. +Waiting for running jobs to finish. +All running jobs have finished. +Entering into maintenance mode... +Now in maintenance mode. +Installing the update archive openidm-4.0.0-2.zip +Update procedure is still processing... +Update procedure is still processing... +... +Update procedure is still processing... +The update process is complete with a status of COMPLETE +Restarting OpenIDM. +Restart request completed. +---- ++ +A short time later, you'll see the following messages in the OpenIDM console: ++ + +[source, console] +---- +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> OpenIDM ready +---- ++ + +[NOTE] +====== +If you want to set up a script for this process, note the delay between the `Restart request completed` and `OpenIDM Ready` messages. +====== + +* `Admin UI`: Alternatively, you can run the second part of the update from the Admin UI at `\http://localhost:8080/admin`. ++ +Navigate to Configure > System Preferences > Update. The instructions in the UI are intuitive. You should see an `Installation Preview` screen with a list of affected files, in the categories described in xref:#update-file-preview["Preview of File Updates"]. Afterwards, you'll also see an `Installing Update` screen with a list of files that have been updated. ++ + +[IMPORTANT] +====== +Clear your browser cache and cookies __after__ this update is complete. +====== ++ +Scroll to the bottom of the Admin UI. After refreshing your browser, you should see the updated version of OpenIDM (4.0.0-2) in the footer of the web page. You can also see the updated version number by navigating to Configure > System Preferences > Update. + + +==== + + +[#update-4002-45] +==== Updating OpenIDM 4.0, Stage Three + +Now your OpenIDM system is ready for a full update to OpenIDM 4.5. Given the details, this section includes different procedures for updates at the command line and from the Admin UI. However, the first two steps are the same: + +[#d9505e3937] +.Common Steps +==== + +. To prevent conflicts, remove the second update binary: ++ + +[source, console] +---- +$ rm /path/to/openidm/bin/update/openidm-4.0.0-2.zip +---- + +. Copy the third update binary, `openidm-4.5.0.zip` , to the noted directory: ++ + +[source, console] +---- +$ cd /path/to/Downloads +$ cp openidm-4.5.0.zip /path/to/openidm/bin/update/ +---- + +==== + +[#update-to-45-cli] +.Updating to OpenIDM 4.5 Through the CLI +==== + +. Start this part of the update from the command line: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./cli.sh update \ +--skipRepoUpdatePreview \ +--acceptLicense \ +--user openidm-admin:openidm-admin \ +--url http://localhost:8080/openidm \ +openidm-4.5.0.zip +---- ++ + +[NOTE] +====== +If you are using a port other than `8080`, specify the port number. Include `--port number` in the `./cli.sh update` command. +====== ++ +You should have already applied repository update scripts, as described in xref:#update-4045-repo["Updating OpenIDM 4.0, Repository Scripts"]. If not, leave out the `--skipRepoUpdatePreview` option. + +. The update process for this stage may be longer than xref:#update-to-4002[""]. During the process, you should see the following messages: ++ + +[source, console] +---- +License was accepted via command line argument. +Repository update preview was skipped. +Pausing the Scheduler +Scheduler has been paused. +Waiting for running jobs to finish. +All running jobs have finished. +Entering into maintenance mode... +Now in maintenance mode. +Installing the update archive openidm-4.5.0.zip +Update procedure is still processing... +... +Update procedure is still processing... +The update process is complete with a status of PENDING_REPO_UPDATES +Run repository update scripts now, and then enter 'yes' to complete the OpenIDM +update process. +---- ++ + +[WARNING] +====== +Updating the repository is your responsibility. You should have already done so in xref:#update-4045-repo["Updating OpenIDM 4.0, Repository Scripts"]. Assuming that is true, enter `yes`, and you should see the following messages: +====== ++ + +[source, console] +---- +Repo Updates status: COMPLETE +Restarting OpenIDM. +Restart request completed. +---- ++ +You should also see the following messages in the OpenIDM console ++ + +[source, console] +---- +OpenIDM version "4.5.0" +... +OpenIDM ready +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> OpenIDM ready +---- ++ + +[IMPORTANT] +====== +Clear your browser cache and cookies __after__ this update is complete. If you do not do this, you might see an error similar to the following in the OpenIDM console: + +[source, console] +---- +SEVERE: RuntimeException caught java.lang.ClassCastException: +Cannot cast org.forgerock.json.jose.jwe.EncryptedJwt +---- +====== ++ + +[NOTE] +====== +If you want to set up a script for this process, note the delay between the `Restart request completed` and the final `-> OpenIDM ready` messages. +====== + +==== + +[#update-to-45-ui] +.Updating to OpenIDM 4.5 Through the Admin UI +==== + +. Log into the Admin UI at `\http://localhost:8080/admin`. + +. +[WARNING] +====== +Updating the repository is your responsibility. You should have already done so in xref:#update-4045-repo["Updating OpenIDM 4.0, Repository Scripts"]. Assuming that is true, confirm this when the Admin UI prompts you to download and acknowledge that you've run these scripts. +====== + +. After you first select `Install Update`, you'll see a __Repository Update Script Preview__ screen where you'll get a chance to download these pre-configured scripts for review. Assuming you have already applied these scripts, click Continue to start the update process. + +. When you see the screen with `Repository Updates`, assuming you've applied these scripts, click Mark Complete. + +. When the update is complete, refresh the browser. Scroll to the bottom of the Admin UI. You should see the updated version of OpenIDM in the footer of the web page. You can also see the updated version by navigating to Configure > System Preferences > Update. ++ + +[IMPORTANT] +====== +Clear your browser cache and cookies __after__ this update is complete. If you do not do this, you might see an error similar to the following in the OpenIDM console: + +[source, console] +---- +SEVERE: RuntimeException caught java.lang.ClassCastException: + Cannot cast org.forgerock.json.jose.jwe.EncryptedJwt +---- +====== + +==== +In either case the process may not be complete. You may find files with the `.new-unix_time` extension. If they now exist, you may have additional work to do, as described in xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. + +If you see errors in the console after OpenIDM restarts, they could be related to updated files, as discussed in xref:#update-afterwards["What You Should Do After Updating to OpenIDM 4.5"]. + + +[#update-afterwards] +==== What You Should Do After Updating to OpenIDM 4.5 + +If you've customized OpenIDM 4.0, you may find files with the following extensions: `.old` and `.new`. For more information, see xref:#update-process["An Overview of the OpenIDM Update Process"]. + +On Linux/UNIX systems, you can find __some__ of these files with the following commands: + +[source, console] +---- +$ cd /path/to/openidm +$ find . -type f -name "*.old*" +$ find . -type f -name "*.new*" +---- + +* Files with the `.old-unix_time` extension are saved from the configuration you had when starting this update process. + +* Files with the `.new-unix_time` extension are files from OpenIDM 4.5 that have not been incorporated into your updated installation. For example, if you find a `system.properties.new-unix_time` file in your `project-dir` directory, OpenIDM is still using your pre-update version of this file, which would still be named `system.properties`. ++ +To take full advantage of OpenIDM 4.5, you will want to incorporate the new features from files with the `.new-unix_time` extension in your deployment. If you have files with multiple `.new-unix_time` extensions, use the file with the latest __unix_time__. ++ +Pay particular attention to your connector configuration files (`provisioner.openicf-connector-name.json`). The update removes outdated connector versions so you __must__ make sure that the `bundleVersion` in your connector configuration matches the version of the connector in `/path/to/openidm/connectors`, or specifies a range that includes the connector version, for example `[1.1.0.0,1.4.0.0]`. For more information, see xref:../integrators-guide/chap-resource-conf.adoc#connector-reference["Setting the Connector Reference Properties"] in the __Integrator's Guide__. + + +[#update-logging-properties] +===== Updating logging.properties + +Recent security fixes prevent Jetty from logging sensitive data, such as passwords. Verify that your `conf/logging.properties` file includes the following excerpt (and add the excerpt if necessary) to prevent unnecessary data from being logged: + +[source] +---- +# Logs the output from Jetty + # Sets the following Jetty classes to INFO level by default because if logging is set to FINE or higher, + # sensitive information can be leaked into the logs + org.eclipse.jetty.server.HttpChannel.level=INFO + org.eclipse.jetty.server.HttpConnection.level=INFO + org.eclipse.jetty.server.HttpInput.level=INFO + org.eclipse.jetty.http.HttpParser.level=INFO + org.eclipse.jetty.io.ssl.SslConnection.level=INFO +---- +This configuration logs request data at `INFO` level, preventing data such as password changes from being logged. In situations where you __need__ to log all data (for example, if you are debugging an issue in a test environment) change the settings here to `FINE` or `FINEST`. For example: + +[source, console] +---- +org.eclipse.jetty.server.HttpConnection.level=FINE +---- + + +[#update-afterwards-json] +===== What You Should Do With Your JSON Files After Updating to OpenIDM 4.5 + +The OpenIDM update process does not account for any changes that you made to existing standard JSON files such as `sync.json` and `managed.json`. In fact, the update process overwrites these files with the standard OpenIDM 4.5 versions of those files. + +Do not overwrite these OpenIDM 4.5 JSON files. Instead, analyze the custom settings from your original JSON files. Apply each custom setting to the files now in your OpenIDM 4.5 deployment, and test the results, to make sure they still work as intended. + + +[#update-afterwards-ui] +===== What You Should Do With Your UI After Updating to OpenIDM 4.5 + +If you have a custom OpenIDM Admin or Self-Service UI, you need to take a few extra steps. + +This assumes that you followed the instructions shown in the introduction shown in xref:#migrate-idm-40-45["Updating From OpenIDM 4.0 to OpenIDM 4.5"], and have saved any custom UI configuration files that you set up in the `openidm/ui/admin/extension` and `openidm/ui/selfservice/extension` subdirectories. + +You will need the updated UI files from the `openidm/ui/admin/default` and `openidm/ui/selfservice/default` directories. So you'll have to delete some files first. + +[WARNING] +==== +Make sure you've saved any custom files from the `openidm/ui/admin/extension` and `openidm/ui/selfservice/extension` subdirectories, as described in the introduction to xref:#migrate-idm-40-45["Updating From OpenIDM 4.0 to OpenIDM 4.5"], and then follow these steps: +==== + +[#update-afterwards-ui-procedure] +==== + +. Delete the existing `openidm/ui/admin/extension` and `openidm/ui/selfservice/extension` subdirectories. + +. Copy files from the `openidm/ui/admin/default` and `openidm/ui/selfservice/default` subdirectories with the following commands: ++ + +[source, console] +---- +$ cd /path/to/openidm/ui +$ cp -r selfservice/default/. selfservice/extension +$ cp -r admin/default/. admin/extension +---- + +. Review your UI custom files. Compare them against the OpenIDM 4.5 version of these files. + +. Apply your custom changes to each new OpenIDM 4.5 UI file in the `openidm/ui/admin/extension` and `openidm/ui/selfservice/extension` subdirectories. + +==== + + + +[#migrate-idm-4045-windows] +==== Migrating From OpenIDM 4.0 to OpenIDM 4.5 on Windows + +The steps outlined in this section will help you take advantage of the new functionality offered in OpenIDM 4.5, while preserving your custom configuration where possible. Some of these changes might affect your existing deployment. + +[NOTE] +==== +Updates from OpenIDM 4.0 to OpenIDM 4.5 on Microsoft Windows are still a manual process. +==== + +==== +To perform a migration from OpenIDM 4.0 to OpenIDM 4.5 on Windows, follow these steps. For the purposes of this procedure, the path to the existing instance of OpenIDM 4.0 is defined as `\path\to\openidm-4.0`. In contrast, the path to the OpenIDM 4.5 is defined as `\path\to\openidm-4.5`: + +. Download and extract the OpenIDM 4.5 `.zip` file. + +. Stop your existing OpenIDM 4.0 server, if it is running. Access the Java console where it is running and enter the `shutdown` command at the OSGi console: ++ + +[source, console] +---- +-> OpenIDM ready +-> shutdown +---- + +. Backup: Save your current deployment. Archive the `openidm` directory. + +. Boot properties: On the OpenIDM 4.5 server, edit the `conf\boot\boot.properties` file to match any customizations that you made on your OpenIDM 4.0 server. Specifically, check the following elements: ++ + +* The HTTP, HTTPS, and mutual authentication ports are specified in the `conf\boot\boot.properties` file. If you changed the default ports in your OpenIDM 4.0 deployment, make sure that the corresponding ports are specified in this file. + +* Check that the keystore and truststore passwords match the current passwords for the keystore and truststore of your OpenIDM 4.0 deployment. + ++ +Depending on the level of customization you have made in your current deployment, it might be simpler to start with your OpenIDM 4.0 `boot.properties` file, and copy all new settings from that file to the version associated with OpenIDM 4.5. However, as a best practice, you should keep all configuration customizations (including new properties and changed settings) in a single location. You can then copy and paste these changes as appropriate. + +. Security files: Copy the contents of your OpenIDM 4.0 `security\` folder to the OpenIDM 4.5 instance. ++ +Examine the following excerpt from the `boot.properties` file. OpenIDM automatically prepends the locations of the `keystore.jceks` and `truststore` files with the installation directory. ++ + +[source] +---- +... +openidm.keystore.type=JCEKS +openidm.truststore.type=JKS +openidm.keystore.provider= +openidm.keystore.location=security/keystore.jceks +openidm.truststore.location=security/truststore +---- + +. Scripts: Migrate any custom scripts or default scripts that you have modified to the scripts directory of your OpenIDM 4.5 instance. In general, custom and customized scripts should be located in the `openidm-4.0\script` directory on the OpenIDM 4.0 deployment: ++ + +* If you modified an existing OpenIDM 4.0 script, compare the default versions of the OpenIDM 4.0 and OpenIDM 4.5 scripts. If you're confident that your changes will work as intended, then copy the customized scripts to the new `openidm-4.5\script` directory. For example: ++ + +[source, console] +---- +PS C:\> cd \path\to\openidm-4.5 +PS C:\> cp \path\to\openidm-4.0\script\policy.js .\script\ +---- + +* If a default script has changed since the 4.0 release, copy the modified script to the `openidm-4.5\script` directory. For example: ++ + +[source, console] +---- +PS C:\> cd \path\to\openidm-4.5 +PS C:\> cp bin\default\script\linkedView.js .\script +---- ++ +Check that your customizations work as expected, then port the changes for OpenIDM 4.5 to the new script in the `openidm-4.5\script` directory. + + +. Provisioner files: Modify any customized provisioner configurations in your existing project to point to the connectors that are provided with OpenIDM-4.5. Specifically, make sure that the `"connectorRef"` properties reflect the new connectors, where applicable. For example: ++ + +[source, javascript] +---- +"connectorRef" : { + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "bundleVersion": "[1.4.0.0,2.0.0.0)", + "connectorName": "org.identityconnectors.ldap.LdapConnector" +}, +---- ++ +Alternatively, copy the connector .jars from your existing installation into the `openidm\connectors\` folder of the new installation. + +. Complete the OpenIDM 4.5 installation, as described in xref:chap-install.adoc#chap-install["Installing OpenIDM Services"]. + +. As there is no automated way to migrate a customized configuration to OpenIDM 4.5, we recommend the following strategy: ++ + +* Start with the default 4.0 configuration. + +* For each configuration file that you have customized, use a file comparison tool such as the Windows `fc.exe` utility to assess the differences between your customized file and the OpenIDM 4.5 file. + +* Based on the results of the `fc.exe` review, use either your existing file as a base and port the OpenIDM 4.5 changes to that file, or vice versa. Ultimately, you want to preserve your customizations but ensure that you are up to date with the latest default configuration. All files should end up in the `openidm-4.5/conf` directory. + +* OpenIDM 4.5 includes scripts to reflect repository changes. You can apply them directly, as described in xref:#update-4045-repo["Updating OpenIDM 4.0, Repository Scripts"]. + + +. If you are using the UI, clear your browser cache after the migration. The browser cache contains files from the previous OpenIDM release, that might not be refreshed when you log in to the new UI. + +. Start OpenIDM 4.5: ++ + +[source, console] +---- +PS C:\> cd \path\to\openidm-4.5 +PS C:\> .\startup.bat +---- + +. Test that your existing clients and scripts are working as intended. + +==== + + + +[#maintenance-mode] +=== Placing an OpenIDM Instance in Maintenance Mode + +OpenIDM 4.0 and above supports a Maintenance Service that disables non-essential services of a running OpenIDM instance, in preparation for an update to a later version. When maintenance mode is enabled, services such as recon, sync, scheduling, and workflow are disabled. The complete list of disabled services is output to the OpenIDM log file. + +The router remains functional and requests to the `maintenance` endpoint continue to be serviced. Requests to endpoints that are serviced by a disabled component return the following response: + +[source, console] +---- +404 Resource endpoint-name not found +---- +Before you enable maintenance mode, you should temporarily suspend all scheduled tasks. For more information, see xref:../integrators-guide/chap-scheduler-conf.adoc#schedules-pausing-current-tasks["Pausing Scheduled Tasks"] in the __Integrator's Guide__. + +You can enable and disable maintenance mode over the REST interface. + +To enable maintenance mode, run the following command: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/maintenance?_action=enable" +{ + "maintenanceEnabled": true +} +---- +When it starts the update process, OpenIDM should enable maintenance mode automatically. Before the update process is complete, it should disable maintenance mode. You can disable it over the REST interface with the following command: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/maintenance?_action=disable" +{ + "maintenanceEnabled": false +} +---- +To check whether OpenIDM is in maintenance mode, run the following command: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/maintenance?_action=status" +{ + "maintenanceEnabled": false +} +---- +If the system is in maintenance mode, the command returns `"maintenanceEnabled": true`, otherwise it returns `"maintenanceEnabled": false`. + + diff --git a/openidm-doc/src/main/asciidoc/install-guide/index.adoc b/openidm-doc/src/main/asciidoc/install-guide/index.adoc new file mode 100644 index 000000000..9dfbefb91 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/index.adoc @@ -0,0 +1,38 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + += Installation Guide +:doctype: book +:toc: +:authors: Mark Craig, Lana Frost, Paul Bryan, Andi Egloff, Laszlo Hordos, Matthias Tristl, Mike Jang +:copyright: Copyright 2011-2017 ForgeRock AS. +:copyright: Portions Copyright 2024 3A Systems LLC. + +:imagesdir: ../ +:figure-caption!: +:example-caption!: +:table-caption!: +[abstract] +Guide to installing and evaluating OpenIDM. OpenIDM offers flexible, open source services for automating management of the identity life cycle. + +include::./preface.adoc[] +include::./chap-install.adoc[] +include::./chap-repository.adoc[] +include::./chap-uninstall.adoc[] +include::./chap-update.adoc[] +include::./appendix-ro-install.adoc[] +include::./openidm-glossary.adoc[] diff --git a/openidm-doc/src/main/asciidoc/install-guide/openidm-glossary.adoc b/openidm-doc/src/main/asciidoc/install-guide/openidm-glossary.adoc new file mode 100644 index 000000000..d8bba1dff --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/openidm-glossary.adoc @@ -0,0 +1,80 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[glossary] +[#openidm-glossary] +== OpenIDM Glossary + + +correlation query:: +A correlation query specifies an expression that matches existing entries in a source repository to one or more entries on a target repository. While a correlation query may be built with a script, it is __not__ a correlation script. + ++ +As noted in xref:../integrators-guide/chap-synchronization.adoc#correlation["Correlating Source Objects With Existing Target Objects"] in the __Integrator's Guide__, you can set up a query definition, such as`_queryId`,`_queryFilter`, or`_queryExpression`, possibly with the help of a`linkQualifier`. + +correlation script:: +A correlation script matches existing entries in a source repository, and returns the IDs of one or more matching entries on a target repository. While it skips the intermediate step associated with a`correlation query`, a correlation script can be relatively complex, based on the operations of the script. + +entitlement:: +An entitlement is a collection of attributes that can be added to a user entry via roles. As such, it is a specialized type of `assignment`. A user or device with an entitlement gets access rights to specified resources. An entitlement is a property of a managed object. + +JSON:: +JavaScript Object Notation, a lightweight data interchange format based on a subset of JavaScript syntax. For more information, see the link:http://www.json.org[JSON, window=\_blank] site. + +JWT:: +JSON Web Token. As noted in the link:http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html[JSON Web Token draft IETF Memo, window=\_blank], "JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties." For OpenIDM, the JWT is associated with the `JWT_SESSION` authentication module. + +managed object:: +An object that represents the identity-related data managed by OpenIDM. Managed objects are configurable, JSON-based data structures that OpenIDM stores in its pluggable repository. The default configuration of a managed object is that of a user, but you can define any kind of managed object, for example, groups or roles. + +mapping:: +A policy that is defined between a source object and a target object during reconciliation or synchronization. A mapping can also define a trigger for validation, customization, filtering, and transformation of source and target objects. + +OSGi:: +A module system and service platform for the Java programming language that implements a complete and dynamic component model. For a good introduction, see the link:https://www.osgi.org//developer/benefits-of-using-osgi[OSGi, window=\_blank] site. While OpenIDM services are designed to run in any OSGi container, currently only link:http://felix.apache.org/[Apache Felix, window=\_blank] is supported. + +reconciliation:: +During reconciliation, comparisons are made between managed objects and objects on source or target systems. Reconciliation can result in one or more specified actions, including, but not limited to, synchronization. + +resource:: +An external system, database, directory server, or other source of identity data to be managed and audited by the identity management system. + +[#gloss-rest] +REST:: +Representational State Transfer. A software architecture style for exposing resources, using the technologies and protocols of the World Wide Web. REST describes how distributed data objects, or resources, can be defined and addressed. + +role:: +OpenIDM includes two different types of provisioning roles and authorization roles. For more information, see xref:../integrators-guide/chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"] in the __Integrator's Guide__. + +source object:: +In the context of reconciliation, a source object is a data object on the source system, that OpenIDM scans before attempting to find a corresponding object on the target system. Depending on the defined mapping, OpenIDM then adjusts the object on the target system (target object). + +synchronization:: +The synchronization process creates, updates, or deletes objects on a target system, based on the defined mappings from the source system. Synchronization can be scheduled or on demand. + +system object:: +A pluggable representation of an object on an external system. For example, a user entry that is stored in an external LDAP directory is represented as a system object in OpenIDM for the period during which OpenIDM requires access to that entry.System objects follow the same RESTful resource-based design principles as managed objects. + +target object:: +In the context of reconciliation, a target object is a data object on the target system, that OpenIDM scans after locating its corresponding object on the source system. Depending on the defined mapping, OpenIDM then adjusts the target object to match the corresponding source object. + + diff --git a/openidm-doc/src/main/asciidoc/install-guide/preface.adoc b/openidm-doc/src/main/asciidoc/install-guide/preface.adoc new file mode 100644 index 000000000..6433d01e2 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/install-guide/preface.adoc @@ -0,0 +1,46 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[preface] +[#preface] +== Preface + +This guide shows you how to install core OpenIDM services for identity management, provisioning, and compliance. + +[#d9505e213] +=== Who Should Use This Guide + +This guide is written for anyone installing OpenIDM to manage identities, and to ensure compliance with identity management regulations. + +It covers the install and removal (uninstall) procedures that you theoretically perform only once per version. It aims to provide you with at least some idea of what happens behind the scenes when you perform the steps. + +You do not need to be an OpenIDM wizard to learn something from this guide, though a background in identity management and maintaining web application software can help. You do need some background in managing services on your operating systems and in your application servers. You can nevertheless get started with this guide, and then learn more as you go along. + + + +include::../partials/sec-formatting-conventions.adoc[] + +include::../partials/sec-accessing-doc-online.adoc[] + +include::../partials/sec-joining-the-community.adoc[] + +include::../partials/sec-support-contact.adoc[] diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-audit.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-audit.adoc new file mode 100644 index 000000000..a6f7bc35a --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-audit.adoc @@ -0,0 +1,936 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-audit] +== Additional Audit Details + +From OpenIDM 4.5.1-20 onwards, the audit service supports audit event handlers that connect to third-party tools. As described in xref:chap-auditing.adoc#configuring-topic-handlers["Configuring Audit Event Handlers"], an audit event handler manages audit events, sends audit output to a defined location, and controls their format. + +As we do not endorse or support the use of any third-party tools, we present the handlers for such tools in this separate appendix. For the purpose of this appendix, we assume that the third-party tool is configured on the same system as your instance of OpenIDM. We realize that you may prefer to set up a third-party tool on a remote system. + +If you have configured a third-party tool on a remote system, the reliability of audit data may vary, depending on the reliability of your network connection. However, you can limit the risks with appropriate buffer settings, which can mitigate issues related to your network connection, free space on your system, and related resources such as RAM. (This is not an exhaustive list.) + +[#appendix-elastic] +=== Elasticsearch Audit Event Handler + +Starting with OpenIDM 4.5.0, you can configure the Elasticsearch audit event handler. It allows you to log OpenIDM events in file formats compatible with the Elasticsearch search server. + +[#elastic-install] +==== Installing and Configuring Elasticsearch + +This appendix assumes that you are installing Elasticsearch on the same system as OpenIDM. For Elasticsearch downloads and installation instructions, see the Elasticsearch link:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html[Getting Started, window=\_blank] document. + +You can set up Elasticsearch Shield with basic authentication to help protect your audit logs. To do so, read the following Elasticsearch document on link:https://www.elastic.co/guide/en/shield/current/getting-started.html[Getting Started with Shield, window=\_blank]. Follow up with the following Elasticsearch document on how you can link:https://www.elastic.co/guide/en/shield/current/enable-basic-auth.html[Control Access with Basic Authentication, window=\_blank]. + +You can configure SSL for Elasticsearch Shield. For more information, see the following Elasticsearch document: link:https://www.elastic.co/guide/en/shield/current/ssl-tls.html[Setting Up SSL/TLS on a Cluster, window=\_blank]. + +Import the certificate that you use for Elasticsearch into OpenIDM's truststore, with the following command: + +[source, console] +---- +$ keytool \ +-import \ +-trustcacerts \ +-alias elasticsearch \ +-file /path/to/cacert.pem \ +-keystore /path/to/openidm/security/truststore +---- +Once imported, you can activate the `useSSL` option in the `audit.json` file. If you created an Elasticsearch Shield username and password, you can also associate that information with the `username` and `password` entries in that same `audit.json` file. + + +[#elastic-audit-index] +==== Creating an Audit Index for Elasticsearch + +If you want to create an audit index for Elasticsearch, you must set it up __before__ starting OpenIDM, for the audit event topics described in this section: xref:chap-auditing.adoc#default-audit-topics["OpenIDM Audit Event Topics"]. + +To do so, execute the REST call shown in the following link:../resources/audit-index.sh[audit index, window=\_blank] file. Note the properties that are `not_analyzed`. Such fields are not indexed within Elasticsearch. + +The REST call in the audit index file includes the following URL: + +[source, console] +---- +http://myUsername:myPassword@localhost:9200/audit +---- +That URL assumes that your Elasticsearch deployment is on the localhost system, accessible on default port 9200, configured with an `indexName` of `audit`. + +It also assumes that you have configured basic authentication on Elasticsearch Shield, with a username of `myUsername` and a password of `myPassword`. + +If any part of your Elasticsearch deployment is different, revise the URL accordingly. + +[WARNING] +==== +Do not transmit usernames and passwords over an insecure connection. Enable the `useSSL` option, as described in xref:#elastic-config["Configuring the Elasticsearch Audit Event Handler"]. +==== + + +[#elastic-config] +==== Configuring the Elasticsearch Audit Event Handler + +xref:#elastic-config-ui["Configuring the Elasticsearch Audit Event Handler via the Admin UI"] and xref:#elastic-config-file["Configuring the Elasticsearch Audit Event Handler in audit.json"] illustrate how you can configure the Elasticsearch Audit Event Handler. + +If you activate the Elasticsearch audit event handler, we recommend that you enable buffering for optimal performance, by setting: + +[source, javascript] +---- +"enabled" : true, +---- +The `buffering` settings shown are not recommendations for any specific environment. If performance and audit data integrity are important in your environment, you may need to adjust these numbers. + +If you choose to protect your Elasticsearch deployment with the plugin known as link:https://www.elastic.co/products/shield[Shield, window=\_blank], and configure the ability to link:https://www.elastic.co/guide/en/shield/current/enable-basic-auth.html[Control Access with Basic Authentication, window=\_blank], you can substitute your Elasticsearch Shield `admin` or `power_user` credentials for `myUsername` and `myPassword`. + +If you activate the `useSSL` option, install the SSL certificate that you use for Elasticsearch into the OpenIDM keystore. For more information, see the following section: xref:chap-security.adoc#security-management-service["Accessing the Security Management Service"]. + +[#elastic-config-ui] +===== Configuring the Elasticsearch Audit Event Handler via the Admin UI + +To configure this event handler through the Admin UI, click Configure > System Preferences > Audit. Select `ElasticsearchAuditEventHandler` from the drop-down text box, click Add Event Handler, and configure it in the window that appears. + +image::images/elastic-audit.png[] +For a list of properties, see xref:#audit-event-prop["Common Audit Event Handler Property Configuration"]. + + +[#elastic-config-file] +===== Configuring the Elasticsearch Audit Event Handler in audit.json + +Alternatively, you can configure the Elasticsearch audit event handler in the `audit.json` file for your project. + +The following code is an excerpt from the `audit.json` file, with Elasticsearch configured as the handler for audit queries: + +[source, javascript] +---- +{ + "auditServiceConfig" : { + "handlerForQueries" : "elasticsearch", + "availableAuditEventHandlers" : [ + "org.forgerock.audit.handlers.elasticsearch.ElasticsearchAuditEventHandler", + "org.forgerock.audit.handlers.csv.CsvAuditEventHandler", + "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "org.forgerock.openidm.audit.impl.RouterAuditEventHandler" + ], +---- +You should also set up configuration for the Elasticsearch event handler. The entries shown are defaults, and can be configured. In fact, if you have set up Elasticsearch Shield, with or without SSL/TLS, as described in xref:#elastic-install["Installing and Configuring Elasticsearch"], you should change some of these defaults. + +[source, javascript] +---- +"eventHandlers" : [ + { + "name" : "elasticsearch" + "class" : "org.forgerock.audit.handlers.elasticsearch.ElasticsearchAuditEventHandler", + "config" : { + "connection" : { + "useSSL" : false, + "host" : "localhost", + "port" : "9200" + }, + "indexMapping" : { + "indexName" : "audit" + }, + "buffering" : { + "enabled" : false, + "maxSize" : 20000, + "writeInterval" : "1 second", + "maxBatchedEvents" : "500" + } + "topics" : [ + "access", + "activity", + "recon", + "sync", + "authentication", + "config" + ] + } + } +], +---- +If you set `useSSL` to true, add the following properties to the `connection` code block: + +[source, javascript] +---- +"username" : "myUsername", + "password" : "myPassword", +---- +For more information on the other options shown in `audit.json`, see xref:#audit-event-prop["Common Audit Event Handler Property Configuration"]. + + + +[#elastic-verify-data] +==== Querying and Reading Elasticsearch Audit Events + +By default, Elasticsearch uses pagination. As noted in the following Elasticsearch document on link:https://www.elastic.co/guide/en/elasticsearch/guide/current/pagination.html[Pagination, window=\_blank], queries are limited to the first 10 results. + +For example, the following query is limited to the first 10 results: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--request GET \ +"http://localhost:8080/openidm/audit/access?_queryFilter=true" +---- +To override the limit of 10 results, follow the guidance shown in xref:chap-data.adoc#paging-query-results["Paging and Counting Query Results"] for `pageSize`. + +To set up a `queryFilter` that uses a "starts with" `sw` or "equals" `eq` comparison expression, you will need to set it up as a `not_analyzed` string field, as described in the following Elasticsearch document on link:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html[Term Query., window=\_blank]. You should also review the section on xref:chap-data.adoc#query-comp-expression["Comparison Expressions"]. If you haven't already done so, you may need to modify and rerun the REST call described in xref:#elastic-audit-index["Creating an Audit Index for Elasticsearch"]. + +The `queryFilter` output should include UUIDs as `id` values for each audit event. To read audit data for that event, include that UUID in the URL. For example, the following REST call specifies an access event, which includes data on the client: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--request GET +"http://localhost:8080/openidm/audit/access/75ca07f5-836c-4e7b-beaa-ae968325a529-622" +---- + + + +[#appendix-audit-schema] +=== Audit Configuration Schema + +The following tables depict schema for the six audit event topics used by OpenIDM. Each topic is associated with the following files that you can find in the `openidm/audit` directory: + +* `access.csv`: see xref:#access-event-prop["Access Event Topic Properties"] + +* `activity.csv`: see xref:#activity-event-prop["Activity Event Topic Properties"] + +* `authentication.csv`: see xref:#auth-event-prop["Authentication Event Topic Properties"] + +* `config.csv`: see xref:#config-event-prop["Configuration Event Topic Properties"] + +* `recon.csv`: see xref:#recon-event-prop["Reconciliation Event Topic Properties"] + +* `sync.csv`: see xref:#sync-event-prop["Synchronization Event Topic Properties"] + +If you open the CSV files from that directory in a spreadsheet application, those files can help you read through the tables shown in this appendix. + +[#openidm-audit-event-topics] +==== OpenIDM Specific Audit Event Topics + + +[#recon-event-prop] +.Reconciliation Event Topic Properties +[cols="33%,67%"] +|=== +|Event Property |Description + +a|`_id` +a|UUID for the message object, such as `"0419d364-1b3d-4e4f-b769-555c3ca098b0"` + +a|`transactionId` +a|The UUID of the transaction; you may see the same ID in different audit event topics. + +a|`timestamp` +a|The time that OpenIDM logged the message, in UTC format; for example `"2015-05-18T08:48:00.160Z"` + +a|`eventName` +a|The name of the audit event: `recon` for this log + +a|`userId` +a|User ID + +a|`trackingIds` +a|A unique value for an object being tracked + +a|`action` +a|Reconciliation action, depicted as a CREST action. For more information, see xref:chap-synchronization.adoc#sync-actions["Synchronization Actions"] + +a|`exception` +a|The stack trace of the exception + +a|`linkQualifier` +a|The link qualifier applied to the action; For more information, see xref:chap-synchronization.adoc#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"] + +a|`mapping` +a|The name of the mapping used for the synchronization operation, defined in `conf/sync.json`. + +a|`message` +a|Description of the synchronization action + +a|`messageDetail` +a|Details from the synchronization run, shown as CREST output + +a|`situation` +a|The synchronization situation described in xref:chap-synchronization.adoc#sync-situations["Synchronization Situations"] + +a|`sourceObjectId` +a|The object ID on the source system, such as `managed/user/jdoe` + +a|`status` +a|Reconciliation result status, such as SUCCESS or FAILURE + +a|`targetObjectId` +a|The object ID on the target system, such as `system/xmlfile/account/bjensen` + +a|`reconciling` +a|What OpenIDM is reconciling, `source` for the first phase, `target` for the second phase. + +a|`ambiguousTargetObjectIds` +a|When the `situation` is AMBIGUOUS or UNQUALIFIED, and OpenIDM cannot distinguish between more than one target object, OpenIDM logs the object IDs, to help figure out what was ambiguous. + +a|`reconAction` +a|The reconciliation action, typically `recon` or `null` + +a|`entryType` +a|The type of reconciliation log entry, such as `start`, `entry`, or `summary`. + +a|`reconId` +a|UUID for the reconciliation operation +|=== + +[#sync-event-prop] +.Synchronization Event Topic Properties +[cols="33%,67%"] +|=== +|Event Property |Description + +a|`_id` +a|UUID for the message object, such as `"0419d364-1b3d-4e4f-b769-555c3ca098b0"` + +a|`transactionId` +a|The UUID of the transaction; you may see the same ID in different audit event topics. + +a|`timestamp` +a|The time that OpenIDM logged the message, in UTC format; for example `"2015-05-18T08:48:00.160Z"` + +a|`eventName` +a|The name of the audit event: `sync` for this log + +a|`userId` +a|User ID + +a|`trackingIds` +a|A unique value for an object being tracked + +a|`action` +a|Synchronization action, depicted as a CREST action. For more information, see xref:chap-synchronization.adoc#sync-actions["Synchronization Actions"] + +a|`exception` +a|The stack trace of the exception + +a|`linkQualifier` +a|The link qualifier applied to the action; For more information, see xref:chap-synchronization.adoc#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"] + +a|`mapping` +a|The name of the mapping used for the synchronization operation, defined in `conf/sync.json`. + +a|`message` +a|Description of the synchronization action + +a|`messageDetail` +a|Details from the reconciliation run, shown as CREST output + +a|`situation` +a|The synchronization situation described in xref:chap-synchronization.adoc#sync-situations["Synchronization Situations"] + +a|`sourceObjectId` +a|The object ID on the source system, such as `managed/user/jdoe` + +a|`status` +a|Reconciliation result status, such as SUCCESS or FAILURE + +a|`targetObjectId` +a|The object ID on the target system, such as `uid=jdoe,ou=People,dc=example,dc=com` +|=== + + +[#section-caud-events] +==== Commons Audit Event Topics + + +[#access-event-prop] +.Access Event Topic Properties +[cols="33%,67%"] +|=== +|Event Property |Description + +a|`_id` +a|UUID for the message object, such as `"0419d364-1b3d-4e4f-b769-555c3ca098b0"` + +a|`timestamp` +a|The time that OpenIDM logged the message, in UTC format; for example `"2015-05-18T08:48:00.160Z"` + +a|`eventName` +a|The name of the audit event: `access` for this log + +a|`transactionId` +a|The UUID of the transaction; you may see the same transaction for the same event in different audit event topics + +a|`userId` +a|User ID + +a|`trackingIds` +a|A unique value for an object being tracked + +a|`server.ip` +a|IP address of the OpenIDM server + +a|`server.port` +a|Port number used by the OpenIDM server + +a|`client.ip` +a|Client IP address + +a|`client.port` +a|Client port number + +a|`request.protocol` +a|Protocol for request, typically CREST + +a|`request.operation` +a|Typically a CREST operation + +a|`request.detail` +a|Typically details for an ACTION request + +a|`http.request.secure` +a|Boolean for request security + +a|`http.request.method` +a|HTTP method requested by the client + +a|`http.request.path` +a|Path of the HTTP request + +a|`http.request.queryParameters` +a|Parameters sent in the HTTP request, such as a key/value pair + +a|`http.request.headers` +a|HTTP headers for the request (optional) + +a|`http.request.cookies` +a|HTTP cookies for the request (optional) + +a|`http.response.headers` +a|HTTP response headers (optional) + +a|`response.status` +a|Normally, SUCCESSFUL, FAILED, or null + +a|`response.statusCode` +a|SUCCESS in `response.status` leads to a null `response.statusCode`; FAILURE leads to a 400-level error + +a|`response.detail` +a|Message associated with `response.statusCode`, such as Not Found or Internal Server Error + +a|`response.elapsedTime` +a|Time to execute the access event + +a|`response.elapsedTimeUnits` +a|Units for response time + +a|`roles` +a|OpenIDM roles associated with the request +|=== + +[#activity-event-prop] +.Activity Event Topic Properties +[cols="33%,67%"] +|=== +|Event Property |Description + +a|`_id` +a|UUID for the message object, such as `"0419d364-1b3d-4e4f-b769-555c3ca098b0"` + +a|`timestamp` +a|The time that OpenIDM logged the message, in UTC format; for example `"2015-05-18T08:48:00.160Z"` + +a|`eventName` +a|The name of the audit event: `activity` for this log + +a|`transactionId` +a|The UUID of the transaction; you may see the same transaction for the same event in different audit event topics. + +a|`userId` +a|User ID + +a|`trackingIds` +a|A unique value for the object being tracked + +a|`runAs` +a|User to run the activity as; may be used in delegated administration + +a|`objectId` +a|Object identifier, such as `/managed/user/jdoe` + +a|`operation` +a|Typically a CREST operation + +a|`before` +a|JSON representation of the object prior to the activity + +a|`after` +a|JSON representation of the object after the activity + +a|`changedFields` +a|Fields that were changed, based on xref:chap-auditing.adoc#audit-watched-fields["Watched Fields: Defining Fields to Monitor"] + +a|`revision` +a|Object revision number + +a|`status` +a|Result, such as SUCCESS + +a|`message` +a|Human readable text about the action + +a|`passwordChanged` +a|True/False entry on changes to the password +|=== + +[#auth-event-prop] +.Authentication Event Topic Properties +[cols="33%,67%"] +|=== +|Event Property |Description + +a|`_id` +a|UUID for the message object, such as `"0419d364-1b3d-4e4f-b769-555c3ca098b0"` + +a|`timestamp` +a|The time that OpenIDM logged the message, in UTC format; for example `"2015-05-18T08:48:00.160Z"` + +a|`eventName` +a|The name of the audit event: `authentication` for this log + +a|`transactionId` +a|The UUID of the transaction; you may see the same transaction for the same event in different audit event topics. + +a|`userId` +a|User ID + +a|`trackingIds` +a|A unique value for an object being tracked + +a|`result` +a|The result of the transaction, either "SUCCESSFUL", or "FAILED" + +a|`principal` +a|An array of the accounts used to authenticate, such as [ "openidm-admin" ] + +a|`context` +a|The complete security context of the authentication operation, including the authenticating ID, the targeted endpoint, the roles applied, and the IP address from which the authentication request was made. + +a|`entries` +a|The JSON representation of the authentication session +|=== + +[#config-event-prop] +.Configuration Event Topic Properties +[cols="33%,67%"] +|=== +|Event Property |Description + +a|`_id` +a|UUID for the message object, such as `"0419d364-1b3d-4e4f-b769-555c3ca098b0"` + +a|`timestamp` +a|The time that OpenIDM logged the message, in UTC format; for example `"2015-05-18T08:48:00.160Z"` + +a|`eventName` +a|The name of the audit event: `config` for this log + +a|`transactionId` +a|The UUID of the transaction; you may see the same transaction for the same event in different audit event topics. + +a|`userId` +a|User ID + +a|`trackingIds` +a|A unique value for an object being tracked + +a|`runAs` +a|User to run the activity as; may be used in delegated administration + +a|`objectId` +a|Object identifier, such as `ui` + +a|`operation` +a|Typically a CREST operation + +a|`before` +a|JSON representation of the object prior to the activity + +a|`after` +a|JSON representation of the object after to the activity + +a|`changedFields` +a|Fields that were changed, based on xref:chap-auditing.adoc#audit-watched-fields["Watched Fields: Defining Fields to Monitor"] + +a|`revision` +a|Object revision number +|=== + + + +[#section-audit-event-config] +=== Audit Event Handler Configuration + +When you set up an audit event handler, you can configure several properties. Most of the properties in the following table are used by the CSV audit event handler, and may be configured in the audit configuration file for your project: `project-dir/conf/audit.json`. + +In several cases, the following table does not include an entry for `description`, as the UI Label / Text is sufficient. + +If you're reviewing this from the OpenIDM Admin UI, click Configure > System Preferences > Audit, and click the edit icon associated with your event handler. + +The tables shown in this section reflect the order in which properties are shown in the Admin UI. That order differs when you review the properties in your project's `audit.json` file. + +[#audit-event-prop] +.Common Audit Event Handler Property Configuration +[cols="33%,33%,34%"] +|=== +|UI Label / Text |audit.json File Label |Description + +a|Name +a|`name` +a|`config` sub-property. Given name of the audit event handler + +a|Audit Events +a|`topics` +a|`config` sub-property; may include events such as `access`, `activity`, and `config` + +a|Use for Queries +a|`handlerForQueries` +a|Audit Event Handler to use for Queries + +a|Enabled +a|`enabled` +a|`config` sub-property + +a|n/a +a|`config` +a|The JSON object used to configure the handler; includes several sub-properties + +a|Shown only in `audit.json` +a|`class` +a|The class name in the Java file(s) used to build the handler +|=== +Two properties shown only in the `audit.json` file for your project are: + +* The class name used to build the handler, which may shown as one of the `availableAuditEventHandlers`, as shown in this excerpt from the `audit.json` file: ++ + +[source, javascript] +---- +"availableAuditEventHandlers" : [ + "org.forgerock.audit.handlers.elasticsearch.ElasticsearchAuditEventHandler", + "org.forgerock.audit.handlers.csv.CsvAuditEventHandler", + "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "org.forgerock.openidm.audit.impl.RouterAuditEventHandler" +], +---- + +* The audit event handler `config` property, which comes after a second instance of the class name of that audit event handler. For an example, see the following excerpt of an `audit.json` file: ++ + +[source, javascript] +---- +"eventHandlers" : [ + { + "class" : "org.forgerock.audit.handlers.csv.CsvAuditEventHandler", + "config" : { + "name" : "csv", + "logDirectory" : "&{launcher.working.location}/audit", + "topics" : [ +---- + +The following table includes `config` properties for the CSV audit event handler. That is different from the audit event topic `config` property, a category of logging data described in xref:chap-auditing.adoc#default-audit-topics["OpenIDM Audit Event Topics"]. + +[#audit-config-prop-csv] +.CSV Audit Event Handler Unique config Properties +[cols="33%,33%,34%"] +|=== +|UI Label / Text |audit.json File Label |Description + +a|File Rotation +a|`fileRotation` +a|File rotation options + +a|rotationEnabled +a|`rotationEnabled` +a|File rotation: true or false boolean + +a|maxFileSize +a|`maxFileSize` +a|File rotation: Maximum size for an audit file, before rotation is triggered + +a|rotationFilePrefix +a|`rotationFilePrefix` +a|File rotation: Prefix to add to the start of an audit file, after rotation + +a|Rotation Times +a|`rotationTimes` +a|File rotation: Time to trigger, after midnight; may use entries such as 5 seconds, 5 minutes, 5 hours, disabled + +a|File Rotation Suffix +a|`rotationFileSuffix` +a|File rotation: Suffix appended to the end of audit file names + +a|Rotation Interval +a|`rotationInterval` +a|File rotation: Time period between log rotation; may use 5 seconds, 5 minutes, 5 hours, disabled + +a|File Retention +a|`fileRetention` +a|Specifies how long to keep an audit file + +a|Maximum Number of Historical Files +a|`maxNumberOfHistoryFiles` +a|File retention: Maximum number of backup audit files + +a|Maximum Disk Space +a|`maxDiskSpaceToUse` +a|File retention: Maximum disk space for audit files + +a|Minimum Free Space Required +a|`minFreeSpaceRequired` +a|File retention: Minimum disk space required on system with audit files + +a|rotationRetentionCheckInterval +a|`rotationRetentionCheckInterval` +a|Interval for periodically checking file rotation and retention policies + +a|Log Directory +a|`logDirectory` +a|Directory with CSV audit event handler files + +a|CSV Output Formatting +a|`formatting` +a| + +a|quoteChar +a|`quoteChar` +a|Formatting: Character used around a CSV field + +a|delimiterChar +a|`delimiterChar` +a|Formatting: Character between CSV fields + +a|End of Line Symbols +a|`endOfLineSymbols` +a|Formatting: end of line symbol, such as `\n` or `\r` + +a|Security: CSV Tamper Evident Configuration +a|`security` +a|Uses keystore-based signatures + +a|Enabled +a|`enabled` +a|CSV Tamper Evident Configuration: true or false + +a|Filename +a|`filename` +a|CSV Tamper Evident Configuration: Path to the Java keystore + +a|Password +a|`password` +a|CSV Tamper Evident Configuration: Password for the Java keystore + +a|Keystore Handler +a|`keystoreHandlerName` +a|CSV Tamper Evident Configuration: Keystore name + +a|Signature Interval +a|`signatureInterval` +a|CSV Tamper Evident Configuration: Signature generation interval. Default = 1 hour. Units described in xref:chap-auditing.adoc#audit-csv-min["Minimum Admin UI CSV Audit Handler Configuration Requirements"]. + +a|Buffering +a|`buffering` +a|Configuration for optional event buffering + +a|enabled +a|`enabled` +a|Buffering: true or false + +a|autoFlush +a|`autoFlush` +a|Buffering: avoids flushing after each event +|=== +Except for the common properties shown in xref:#audit-event-prop["Common Audit Event Handler Property Configuration"], the Repository and Router audit event handlers share one unique property: `resourcePath`: + +[source, javascript] +---- +{ + "class" : "org.forgerock.openidm.audit.impl.RouterAuditEventHandler", + "config" : { + "name" : "router", + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ], + "resourcePath" : "system/auditdb" + } + }, +---- + +[#audit-config-prop-repo] +.Repository / Router Audit Event Handler Unique config Properties +[cols="33%,33%,34%"] +|=== +|UI Label / Text |audit.json File Label |Description + +a|resourcePath +a|`resourcePath` +a|Path to the repository resource +|=== + +[NOTE] +==== +Take care when reading JMS properties in the `audit.json` file. They include the standard ForgeRock audit event topics, along with JMS-unique topics: +==== + +[#audit-config-prop-jms] +.JMS Audit Event Handler Unique config Properties +[cols="33%,33%,34%"] +|=== +|UI Label / Text |audit.json File Label |Description + +a|Delivery Mode +a|`deliveryMode` +a|For messages from a JMS provider; may be `PERSISTENT` or `NON_PERSISTENT` + +a|Session Mode +a|`sessionMode` +a|Acknowledgement mode, in sessions without transactions. May be `AUTO`, `CLIENT`, or `DUPS_OK`. + +a|Batch Configuration Settings +a|`batchConfiguration` +a|Options when batch messaging is enabled + +a|Batch Enabled +a|`batchEnabled` +a|Boolean for batch delivery of audit events + +a|Capacity +a|`capacity` +a|Maximum event count in the batch queue; additional events are dropped + +a|Thread Count +a|`threadCount` +a|Number of concurrent threads that pull events from the batch queue + +a|Maximum Batched Events +a|`maxBatchedEvents` +a|Maximum number of events per batch + +a|Insert Timeout (Seconds) +a|`insertTimeoutSec` +a|Waiting period (seconds) for available capacity, when a new event enters the queue + +a|Polling Timeout (Seconds) +a|`pollTimeoutSec` +a|Worker thread waiting period (seconds) for the next event, before going idle + +a|Shutdown Timeout (Seconds) +a|`shutdownTimeoutSec` +a|Application waiting period (seconds) for worker thread termination + +a|JNDI Configuration +a|`jndiConfiguration` +a|Java Naming and Directory Interface (JNDI) Configuration Settings + +a|JNDI Context Properties +a|`contextProperties` +a|Settings to populate the JNDI initial context with + +a|JNDI Context Factory +a|`java.naming.factory.initial` +a|Initial JNDI context factory, such as `com.tibco.tibjms.naming.TibjmsInitialContextFactory` + +a|JNDI Provider URL +a|`java.naming.provider.url` +a|Depends on provider; options include `tcp://localhost:61616` and `tibjmsnaming://192.168.1.133:7222` + +a|JNDI Topic +a|`topic.audit` +a|Relevant JNDI topic; default=audit + +a|JNDI Topic Name +a|`topicName` +a|JNDI lookup name for the JMS topic + +a|Connection Factory +a|`connectionFactoryName` +a|JNDI lookup name for the JMS connection factory +|=== +The Elasticsearch audit event handler is relatively complex, with `config` subcategories for `connection`, `indexMapping`, `buffering`, and `topics`. + +[#audit-config-prop-elastic] +.Elasticsearch Audit Event Handler Unique config Properties +[cols="33%,33%,34%"] +|=== +|UI Label / Text |audit.json File Label |Description + +a|Connection +a|`connection` +a|Elasticsearch audit event handler + +a|useSSL +a|`useSSL` +a|Connection: Use SSL/TLS to connect to Elasticsearch + +a|host +a|`host` +a|Connection: Hostname or IP address of Elasticsearch (default: localhost) + +a|port +a|`port` +a|Connection: Port used by Elasticsearch (default: 9200) + +a|username +a|`username` +a|Connection: Username when Basic Authentication is enabled via Elasticsearch Shield + +a|password +a|`password` +a|Connection: Password when Basic Authentication is enabled via Elasticsearch Shield + +a|Index Mapping +a|`indexMapping` +a|Defines how an audit event and its fields are stored and indexed + +a|indexName +a|`indexName` +a|Index Mapping: Index Name (default=audit). Change if 'audit' conflicts with an existing Elasticsearch index + +a|Buffering +a|`buffering` +a|Configuration for buffering events and batch writes (increases write-throughput) + +a|enabled +a|`enabled` +a|Buffering: recommended + +a|maxSize +a|`maxSize` +a|Buffering: Fixed maximum number of events that can be buffered (default: 10000) + +a|Write Interval +a|`writeInterval` +a|Buffering: Interval (default: 1 s) at which buffered events are written to Elasticsearch (units of 'ms' or 's' are recommended) + +a|maxBatchedEvents +a|`maxBatchedEvents` +a|Buffering: Maximum number of events per batch-write to Elasticsearch for each Write Interval (default: 500) +|=== + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-auth-modules.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-auth-modules.adoc new file mode 100644 index 000000000..c139ef4ba --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-auth-modules.adoc @@ -0,0 +1,294 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-auth-modules] +== Authentication and Session Module Configuration Details + +This appendix includes configuration details for authentication modules described here: xref:chap-auth.adoc#supported-auth-session-modules["Supported Authentication and Session Modules"]. + +Authentication modules, as configured in the `authentication.json` file, include a number of properties. Except for the xref:#openam-module-details["OPENAM_SESSION Module Configuration Options"], Those properties are listed in the following tables: + +[#session-module-prop] +.Session Module +[cols="28%,29%,43%"] +|=== +|Authentication Property |Property as Listed in the Admin UI |Description + +a|`keyAlias` +a|(not shown) +a|Used by the Jetty Web server to service SSL requests. + +a|`privateKeyPassword` +a|(not shown) +a|Defaults to `openidm.keystore.password` in `boot.properties`. + +a|`keystoreType` +a|(not shown) +a|Defaults to `openidm.keystore.type` in `boot.properties`. + +a|`keystoreFile` +a|(not shown) +a|Defaults to `openidm.keystore.location` in `boot.properties`. + +a|`keystorePassword` +a|(not shown) +a|Defaults to `openidm.keystore.password` in `boot.properties` + +a|`maxTokenLifeMinutes` +a|Max Token Life (in seconds) +a|Maximum time before a session is cancelled. Note the different units for the property and the UI. + +a|`tokenIdleTimeMinutes` +a|Token Idle Time (in seconds) +a|Maximum time before an idle session is cancelled. Note the different units for the property and the UI. + +a|`sessionOnly` +a|Session Only +a|Whether the session continues after browser restarts. +|=== + +[#static-module-prop] +.Static User Module +[cols="28%,29%,43%"] +|=== +|Authentication Property |Property as Listed in the Admin UI |Description + +a|`enabled` +a|Module Enabled +a|Does OpenIDM use the module + +a|`queryOnResource` +a|Query on Resource +a|Endpoint hard coded to user `anonymous` + +a|`username` +a|Static User Name +a|Default for the static user, `anonymous` + +a|`password` +a|Static User Password +a|Default for the static user, `anonymous` + +a|`defaultUserRoles` +a|Static User Role +a|Normally set to `openidm-reg` for self-registration +|=== +The following table applies to several authentication modules: +[none] +* `Managed User` +* `Internal User` +* `Client Cert` +* `Passthrough` +* `IWA` +The IWA module includes several Kerberos-related properties listed at the end of the table. + +[#managed-module-prop] +.Common Module Properties +[cols="28%,29%,43%"] +|=== +|Authentication Property |Property as Listed in the Admin UI |Description + +a|`enabled` +a|Module Enabled +a|Does OpenIDM use the module + +a|`queryOnResource` +a|Query on Resource +a|Endpoint to query + +a|`queryId` +a|Use Query ID +a|A defined `queryId` searches against the `queryOnResource` endpoint. An undefined `queryId` against `queryOnResource` with `action=reauthenticate` + +a|`defaultUserRoles` +a|Default User Roles +a|Normally blank for managed users + +a|`authenticationId` +a|Authentication ID +a|Defines how account credentials are derived from a `queryOnResource` endpoint + +a|`userCredential` +a|User Credential +a|Defines how account credentials are derived from a `queryOnResource` endpoint + +a|`userRoles` +a|User Roles +a|Defines how account roles are derived from a `queryOnResource` endpoint + +a|`groupMembership` +a|Group Membership +a|Provides more information for calculated roles + +a|`groupRoleMapping` +a|Group Role Mapping +a|Provides more information for calculated roles + +a|`groupComparisonMethod` +a|Group Comparison Method +a|Provides more information for calculated roles + +a|`managedUserLink` +a|Managed User Link +a|Applicable mapping (Passthrough module only) + +a|`augmentSecurityContext` +a|Augment Security Context +a|Includes a script that is executed only after a successful authentication request. + +a|`servicePrincipal` +a|Kerberos Service Principal +a|(IWA only) For more information, see xref:chap-auth.adoc#openidm-auth-kerberos["Configuring IWA Authentication"] + +a|`keytabFileName` +a|Keytab File Name +a|(IWA only) For more information, see xref:chap-auth.adoc#openidm-auth-kerberos["Configuring IWA Authentication"] + +a|`kerberosRealm` +a|Kerberos Realm +a|(IWA only) For more information, see xref:chap-auth.adoc#openidm-auth-kerberos["Configuring IWA Authentication"] + +a|`kerberosServerName` +a|Kerberos Server Name +a|(IWA only) For more information, see xref:chap-auth.adoc#openidm-auth-kerberos["Configuring IWA Authentication"] +|=== + +[#openam-module-details] +=== OPENAM_SESSION Module Configuration Options + +The `OPENAM_SESSION` module uses OpenAM authentication to protect an OpenIDM deployment. + +The options shown in the screen are subdivided into basic and advanced properties. You may need to click Advanced Properties to review those details. + +[#openam-basic-prop] +image::images/openam-auth-basic.png[] +The following table describes the label that you see in the Admin UI, the default value (if any), a brief description, and the associated configuration file. If you need the property name, look at the configuration file. + +The default values shown depict what you see if you use the `OPENAM_SESSION` module with the Full Stack Sample. For more information, see xref:../samples-guide/chap-fullstack-sample.adoc#chap-fullstack-sample["Full Stack Sample - Using OpenIDM in the ForgeRock Identity Platform"] in the __Samples Guide__. + +[#table-openam-basic] +.OPENAM_SESSION Module Basic Properties +[cols="18%,27%,27%,28%"] +|=== +|Admin UI Label |Default |Description |Configuration File + +a|Module Enabled +a|false +a|Whether to enable the module +a|authentication.json + +a|Route to OpenAM User Datastore +a|system/ldap/account +a|External repository with OpenAM Data Store Information +a|authentication.json + +a|OpenAM Deployment URL +a|blank +a|FQDN of the deployed instance of OpenAM +a|authentication.json + +a|Require OpenAM Authentication +a|false +a|Whether to make the OpenIDM UI redirect users to OpenAM for authentication +a|ui-configuration.json +|=== + +[#table-openam-advanced] +.OPENAM_SESSION Module Advanced Properties +[cols="18%,27%,27%,28%"] +|=== +|Admin UI Label |Default |Description |Configuration File + +a|OpenAM Login URL +a|http://example.com:8081/XUI/#login/ +a|FQDN of the login endpoint of the deployed instance of OpenAM +a|ui-configuration.json + +a|OpenAM Login Link Text +a|Login with OpenAM +a|UI text that links to OpenAM +a|ui-configuration.json + +a|Default User Roles +a|openidm-authorized +a|OpenIDM assigns such roles to the security context of a user +a|authentication.json + +a|OpenAM User Attribute +a|uid +a|User identifier for the OpenAM data store +a|authentication.json + +a|Authentication ID +a|uid +a|User identifier +a|authentication.json + +a|User Credential +a|blank +a|Credential, sometimes a password +a|authentication.json + +a|User Roles or Group Membership +a|Select an option +a|For an explanation, see xref:#managed-module-prop["Common Module Properties"]. +a|authentication.json + +a|Group Membership (if selected) +a|ldapGroups +a|Group Membership +a|authentication.json + +a|Role Name +a|openidm-admin +a|Default role for the user, normally a group role mapping +a|authentication.json + +a|Group Mappings +a|cn=idmAdmins,ou=Groups,dc=example,dc=com +a|Mapping from a user to a LDAP entry +a|authentication.json + +a|TruststorePath Property Name +a|truststorePath +a|File path to the OpenIDM truststore +a|authentication.json + +a|TruststorePath Property Type +a|security/truststore +a|Truststore file location, relative to /path/to/openidm +a|authentication.json (from boot.properties) + +a|Augment Security Context +a|Javascript +a|Supports Javascript or Groovy +a|authentication.json + +a|File Path +a|auth/populateAsManagedUser.js +a|Path to security context script, in the `/path/to/openidm/bin/defaults/script` subdirectory +a|authentication.json +|=== +In general, if you add a custom property, the Admin UI writes changes to the `authentication.json` or `ui-configuration.json` files. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-file-layout.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-file-layout.adoc new file mode 100644 index 000000000..a40b0a320 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-file-layout.adoc @@ -0,0 +1,290 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-file-layout] +== File Layout + +-- +When you unpack and start OpenIDM 4.5, you create the following files and directories. Note that the precise paths will depend on the install, project, and working directories that you have selected during startup. For more information, see xref:chap-services.adoc#startup-configuration["Specifying the OpenIDM Startup Configuration"]. + +`openidm/audit/`:: +OpenIDM audit log directory default location, created at run time, as configured in `openidm/conf/audit.json` + +`openidm/audit/access.csv`:: +Default OpenIDM access audit log + +`openidm/audit/activity.csv`:: +Default OpenIDM activity audit log + +`openidm/audit/authentication.csv`:: +Default OpenIDM authentication audit log + +`openidm/audit/config.csv`:: +Default OpenIDM configuration audit log + +`openidm/audit/recon.csv`:: +Default OpenIDM reconciliation audit log + +`openidm/audit/sync.csv`:: +Default OpenIDM synchronization audit log + +`openidm/bin/`:: +OpenIDM core libraries and scripts + +`openidm/bin/create-openidm-rc.sh`:: +Script to create an `openidm` resource definition file for inclusion under `/etc/init.d/` + +`openidm/bin/defaults/script`:: +Default scripts required to run specific services. In general, you should not modify these scripts. Instead, add customized scripts to your project's `script/` directory. + +`openidm/bin/defaults/script/audit/*.js`:: +Scripts related to the audit logging service, described in xref:chap-auditing.adoc#chap-auditing["Using Audit Logs"]. + +`openidm/bin/defaults/script/auth/*.js`:: +Scripts related to the authentication mechanism, described in xref:chap-auth.adoc#openidm-authentication["OpenIDM Authentication"]. + +`openidm/bin/defaults/script/compensate.js`:: +Script that provides the compensation functionality to assure or roll back reconciliation operations. For more information, see xref:chap-synchronization.adoc#sync-failure-compensation["Configuring Synchronization Failure Compensation"]. + +`openidm/bin/defaults/script/info/login.js`:: +Provides information about the current OpenIDM session. + +`openidm/bin/defaults/script/info/ping.js`:: +Provides basic information about the health of an OpenIDM system. + +`openidm/bin/defaults/script/info/version.js`:: +Provides information about the current OpenIDM version. + +`openidm/bin/defaults/script/lib/*`:: +Internal libraries required by certain OpenIDM javascripts. + +`openidm/bin/defaults/script/linkedView.js`:: +A script that returns all the records linked to a specific resource, used in reconciliation. + +`openidm/bin/defaults/script/policy.js`:: +Defines each policy and specifies how policy validation is performed + +`openidm/bin/defaults/script/policyFilter.js`:: +Enforces policy validation + +`openidm/bin/defaults/script/roles/*.js`:: +Scripts to provide the default roles functionality. For more information, see xref:chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"]. + +`openidm/bin/defaults/script/router-authz.js`:: +Provides the functions that enforce access rules + +`openidm/bin/defaults/script/ui/*`:: +Scripts required by the UI + +`openidm/bin/defaults/script/workflow/*`:: +Default workflow scripts + +`openidm/bin/felix.jar`,`openidm/bin/openidm.jar`,`openidm/bin/org.apache.felix.gogo.runtime-0.10.0.jar`,`openidm/bin/org.apache.felix.gogo.shell-0.10.0.jar`:: +Files relating to the Apache Felix OSGi framework + +`openidm/bin/launcher.bat`,`openidm/bin/launcher.jar`,`openidm/bin/launcher.json`:: +Files relating to the startup configuration + +`openidm/bin/LICENSE.TXT`,`openidm/bin/NOTICE.TXT`:: +Files relating to the Apache Software License + +`openidm/bin/install-service.bat`,`openidm/bin/MonitorService.bat`,`openidm/bin/prunmgr.exe`,`openidm/bin/amd64/prunsrv.exe`,`openidm/bin/i386/prunsrv.exe`,`openidm/bin/ia64/prunsrv.exe`:: +Files required by the user interface to monitor and configure installed services + +`openidm/bin/startup/`,`openidm/bin/startup/OS X - Run OpenIDM In Background.command`,`openidm/bin/startup/OS X - Run OpenIDM In Terminal Window.command`,`openidm/bin/startup/OS X - Stop OpenIDM.command`:: +Clickable commands for Mac OS X + +`openidm/bin/update`:: +Empty directory into which update archives must be copied. For more information, see xref:../install-guide/chap-update.adoc#update-process["An Overview of the OpenIDM Update Process"] in the __Installation Guide__. + +`openidm/bundle/`:: +OSGi bundles and modules required by OpenIDM. Upgrade can install new and upgraded bundles here. + +`openidm/cli.bat`,`openidm/cli.sh`:: +Management commands for operations such as validating configuration files + +`openidm/conf/`:: +OpenIDM configuration files, including .properties files and JSON files. You can also access JSON views through the REST interface. + +`openidm/conf/audit.json`:: +Audit event publisher configuration file + +`openidm/conf/authentication.json`:: +Authentication configuration file for access to the REST API + +`openidm/conf/boot/boot.properties`:: +OpenIDM bootstrap properties + +`openidm/conf/cluster.json`:: +Configuration file to enable use of this OpenIDM instance in a cluster + +`openidm/conf/config.properties`:: +Felix and OSGi bundle configuration properties + +`openidm/conf/endpoint-*.json`:: +Endpoint configuration files required by the UI for the default workflows + +`openidm/conf/info-*.json`:: +Configuration files for the health check service, described in xref:chap-services.adoc#system-healthcheck["Monitoring the Basic Health of an OpenIDM System"]. + +`openidm/conf/jetty.xml`:: +Jetty configuration controlling access to the REST interface + +`openidm/conf/logging.properties`:: +OpenIDM log configuration properties + +`openidm/conf/managed.json`:: +Managed object configuration file + +`openidm/conf/policy.json`:: +Default policy configuration + +`openidm/conf/process-access.json`:: +Workflow access configuration + +`openidm/conf/repo.orientdb.json`:: +OrientDB internal repository configuration file + +`openidm/conf/router.json`:: +Router service configuration file + +`openidm/conf/scheduler.json`:: +Scheduler service configuration + +`openidm/conf/script.json`:: +Script configuration file with default script directories. + +`openidm/conf/selfservice.kba.json`:: +Configuration file for knowledge-based access in the self-service UI. For more information, see xref:chap-ui.adoc#self-service-questions["Configuring Self-Service Questions"]. + +`openidm/conf/servletfilter-*.json`:: +Sample servlet filter configuration, described in xref:appendix-jetty.adoc#registering-servlet-filters["Registering Additional Servlet Filters"]. + +`openidm/conf/system.properties`:: +System configuration properties used when starting OpenIDM services + +`openidm/conf/ui-configuration.json`:: +Main configuration file for the browser-based user interface + +`openidm/conf/ui-countries.json`:: +Configurable list of countries available when registering users in the user interface + +`openidm/conf/ui-dashboard.json`:: +Configuration file for Self-Service and Admin UI dashboard pages + +`openidm/conf/ui-themeconfig.json`:: +Customizable UI theme configuration file + +`openidm/conf/ui.context-*.json`:: +Configuration files that set the context root of the Self-Service and Admin UIs. + +`openidm/conf/workflow.json`:: +Configuration of the Activiti workflow engine + +`openidm/connectors/`:: +OpenICF connector libraries. OSGi enabled connector libraries can also be stored in `openidm/bundle/`. + +`openidm/db/*`:: +Internal repository files, including OrientDB files and sample repository configurations for JDBC-based repositories. For more information, see xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. + +`openidm/felix-cache/`:: +Bundle cache directory created when the Felix framework is started + +`openidm/getting-started.*`:: +Startup scripts for the __Getting Started__ sample configuration. For more information, see xref:../getting-started/index.adoc[Getting Started]. + +`openidm/legal-notices`:: +Licence files for ForgeRock and third-party components used by OpenIDM. + +`openidm/lib`:: +Location in which third-party libraries (required, for example, by custom connectors) should be placed. + +`openidm/logs/`:: +OpenIDM service log directory + +`openidm/logs/openidm0.log.*`:: +OpenIDM service log files as configured in `openidm/conf/logging.properties` + +`openidm/update.json`:: +Facilitates autodection of the ability to update OpenIDM from a given .jar or .zip file. + +`openidm/samples/`:: +OpenIDM sample configurations + ++ +Most of the samples in this directory are described in xref:../samples-guide/index.adoc[Samples Guide]. + ++ +For information on the health check service sample (`samples/infoservice/`), see xref:chap-services.adoc#custom-health-scripts["Customizing Health Check Scripts"]. + ++ +For information on the sync failure sample (`samples/syncfailure/`), see xref:chap-synchronization.adoc#livesync-retry-strategy["Configuring the LiveSync Retry Policy"]. + ++ +For information on the scanning task sample (`samples/taskscanner/`), see xref:chap-scheduler-conf.adoc#task-scanner["Scanning Data to Trigger Tasks"]. + ++ +Sample files not covered in this guide, or in xref:../samples-guide/index.adoc[Samples Guide] include the following: ++ + +* `samples/misc/` - sample configuration files + +* `samples/provisioners/` - sample connector configuration files + +* `samples/schedules/` - sample schedule configuration files + +* `samples/security/` - sample keystore, truststore, and certificates + + +`openidm/script/`:: +OpenIDM location for script files referenced in the configuration + +`openidm/script/access.js`:: +Default authorization policy script + +`openidm/security/`:: +OpenIDM security configuration, keystore, and truststore + +`openidm/shutdown.sh`:: +Script to shutdown OpenIDM services based on the process identifier + +`openidm/startup.bat`:: +Script to start OpenIDM services on Windows + +`openidm/startup.sh`:: +Script to start OpenIDM services on UNIX + +`openidm/tools`:: +Location of the custom scripted connector bundler, described in the link:http://openicf.forgerock.org/doc/bootstrap/dev-guide/index.html#chap-custom-bundler[OpenICF Developers Guide, window=\_blank]. + +`openidm/ui/admin/*`:: +Configuration files for the Admin UI. + +`openidm/ui/selfservice/*`:: +Configuration files for the Self-Service UI. + +`openidm/workflow/`:: +OpenIDM location for BPMN 2.0 workflows and .bar files + +-- + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-interface-stability.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-interface-stability.adoc new file mode 100644 index 000000000..5e18669dd --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-interface-stability.adoc @@ -0,0 +1,31 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-interface-stability] +== Release Levels & Interface Stability + +This appendix includes Open Identity Platform definitions for product release levels and interface stability. + +include::../partials/sec-release-levels.adoc[] + +include::../partials/sec-interface-stability.adoc[] diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-jetty.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-jetty.adoc new file mode 100644 index 000000000..e8ca0dc0e --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-jetty.adoc @@ -0,0 +1,231 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-jetty] +== Embedded Jetty Configuration + +OpenIDM 4.5 includes an embedded Jetty web server. + +To configure the embedded Jetty server, edit `openidm/conf/jetty.xml`. OpenIDM delegates most of the connector configuration to `jetty.xml`. OSGi and PAX web specific settings for connector configuration therefore do not have an effect. This lets you take advantage of all Jetty capabilities, as the web server is not configured through an abstraction that might limit some of the options. + +The Jetty configuration can reference configuration properties (such as port numbers and keystore details) from OpenIDM's `boot.properties` configuration file. + +[#access-openidm-config-properties] +=== Using OpenIDM Configuration Properties in the Jetty Configuration + +OpenIDM exposes a `Param` class that you can use in `jetty.xml` to include OpenIDM configuration. The `Param` class exposes Bean properties for common Jetty settings and generic property access for other, arbitrary settings. + +[#jetty-access-bean-properties] +==== Accessing Explicit Bean Properties + +To retrieve an explicit Bean property, use the following syntax in `jetty.xml`. + +[source, xml] +---- + +---- +For example, to set a Jetty property for keystore password: + +[source, xml] +---- + + + +---- +Also see the bundled `jetty.xml` for further examples. +-- +The following explicit Bean properties are available. + +port:: +Maps to `openidm.port.http` + +port:: +Maps to `openidm.port.https` + +port:: +Maps to `openidm.port.mutualauth` + +keystoreType:: +Maps to `openidm.keystore.type` + +keystoreProvider:: +Maps to `openidm.keystore.provider` + +keystoreLocation:: +Maps to `openidm.keystore.location` + +keystorePassword:: +Maps to `openidm.keystore.password` + +keystoreKeyPassword:: +Maps to `openidm.keystore.key.password`, or the keystore password, if not set + +truststoreLocation:: +Maps to `openidm.truststore.location`, or the keystore location, if not set + +truststorePassword:: +Maps to `openidm.truststore.password`, or the keystore password, if not set + +-- + + +[#jetty-access-generic-properties] +==== Accessing Generic Properties + + +[source, xml] +---- + + org.forgerock.openidm.some.sample.property + +---- + + + +[#default-jetty-settings] +=== Jetty Default Settings + +By default the embedded Jetty server uses the following settings. + +* The HTTP, SSL, and Mutual Authentication ports defined in OpenIDM + +* The same keystore and truststore settings as OpenIDM + +* Trivial sample realm, `openidm/security/realm.properties` to add users + +The default settings are intended for evaluation only. Adjust them according to your production requirements. + + +[#registering-servlet-filters] +=== Registering Additional Servlet Filters + +You can register generic servlet filters in the embedded Jetty server to perform additional filtering tasks on requests to or responses from OpenIDM. For example, you might want to use a servlet filter to protect access to OpenIDM with an access management product. Servlet filters are configured in files named `openidm/conf/servletfilter-name.json`. These servlet filter configuration files define the filter class, required libraries, and other settings. + +A sample servlet filter configuration is provided in the `servletfilter-cors.json` file in the `/path/to/openidm/conf` directory. + +The sample servlet filter configuration file is shown below: + +[source, javascript] +---- +{ + "classPathURLs" : [ ], + "systemProperties" : { }, + "requestAttributes" : { }, + "scriptExtensions" : { }. + "initParams" : { + "allowedOrigins" : "https://localhost:8443", + "allowedMethods" : "GET,POST,PUT,DELETE,PATCH", + "allowedHeaders" : "accept,x-openidm-password,x-openidm-nosession, + x-openidm-username,content-type,origin, + x-requested-with", + "allowCredentials" : "true", + "chainPreflight" : "false" + }, + "urlPatterns" : [ + "/*" + ], + "filterClass" : "org.eclipse.jetty.servlets.CrossOriginFilter" +} +---- +The sample configuration includes the following properties: +-- + +`"classPathURLs"`:: +The URLs to any required classes or libraries that should be added to the classpath used by the servlet filter class + +`"systemProperties"`:: +Any additional Java system properties required by the filter + +`"requestAttributes"`:: +The HTTP Servlet request attributes that will be set by OpenIDM when the filter is invoked. OpenIDM expects certain request attributes to be set by any module that protects access to it, so this helps in setting these expected settings. + +`"scriptExtensions"`:: +Optional script extensions to OpenIDM. Currently only `"augmentSecurityContext"` is supported. A script that is defined in `augmentSecurityContext` is executed by OpenIDM after a successful authentication request. The script helps to populate the expected security context in OpenIDM. For example, the login module (servlet filter) might select to supply only the authenticated user name, while the associated roles and user ID can be augmented by the script. + ++ +Supported script types include `"text/javascript"` and `"groovy"`. The script can be provided inline (`"source":script source`) or in a file (`"file":filename`). The sample filter extends the filter interface with the functionality in the script `script/security/populateContext.js`. + +`"filterClass"`:: +The servlet filter that is being registered + +-- +The following additional properties can be configured for the filter: +-- + +`"httpContextId"`:: +The HTTP context under which the filter should be registered. The default is `"openidm"`. + +`"servletNames"`:: +A list of servlet names to which the filter should apply. The default is `"OpenIDM REST"`. + +`"urlPatterns"`:: +A list of URL patterns to which the filter applies. The default is `["/*"]`. + +`"initParams"`:: +Filter configuration initialization parameters that are passed to the servlet filter `init` method. For more information, see link:http://docs.oracle.com/javaee/5/api/javax/servlet/FilterConfig.html[http://docs.oracle.com/javaee/5/api/javax/servlet/FilterConfig.html, window=\_top]. + +-- + + +[#disabling-protocols] +=== Disabling and Enabling Secure Protocols + +Secure communications are important. To that end, the embedded Jetty web server enables a number of different protocols. To review the list of enabled protocols, run the following commands: + +[source, console] +---- +$ cd /path/to/openidm/logs +$ grep Enabled openidm0.log.0 + openidm0.log.0:INFO: Enabled Protocols [SSLv2Hello, TLSv1, TLSv1.1, TLSv1.2] of +[SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2] +---- +Note the difference between enabled and available protocols. Based on this particular output, `SSLv3` is missing from the list of enabled protocols. To see how this was done, open the `jetty.xml` file in the /path/to/openidm/conf directory. Note the `"ExcludeProtocols"` code block shown here: + +[source, xml] +---- +... + + + SSLv3 + + +... +---- + +[NOTE] +==== +As noted in the following link:https://www.openssl.org/~bodo/ssl-poodle.pdf[Security Advisory, window=\_blank], "SSL 3.0 [RFC6101] is an obsolete and insecure protocol." +==== +To exclude another protocol from the `Enabled` list, just add it to the `"ExcludeProtocols"` XML block. For example, if you included the following line in that XML block, your instance of Jetty would also exclude TLSv1: + +[source, xml] +---- +TLSv1 +---- +You can reverse the process by removing the protocol from the `"ExcludeProtocols"` block. + +To see if certain protocols should be included in the `"ExcludeProtocols"` block, review the current list of link:https://forgerock.org/security-advisories/[ForgeRock Security Advisories, window=\_blank] + +For more information on Jetty configuration see the following document from the developers of link:http://www.eclipse.org/jetty/documentation/current/[Jetty: The Definitive Reference, window=\_blank] + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-objects.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-objects.adoc new file mode 100644 index 000000000..14c757d4a --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-objects.adoc @@ -0,0 +1,860 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-objects] +== Data Models and Objects Reference + +OpenIDM allows you to customize a variety of objects that can be addressed via a URL or URI, and that have a common set of functions that OpenIDM can perform on them such as CRUD, query, and action. + +Depending on how you intend to use them, different objects are appropriate. + +[#table-object-types] +.OpenIDM Objects +[cols="28%,43%,29%"] +|=== +|Object Type |Intended Use |Special Functionality + +a|Managed objects +a|Serve as targets and sources for synchronization, and to build virtual identities. +a|Provide appropriate auditing, script hooks, declarative mappings and so forth in addition to the REST interface. + +a|Configuration objects +a|Ideal for look-up tables or other custom configuration, which can be configured externally like any other system configuration. +a|Adds file view, REST interface, and so forth + +a|Repository objects +a|The equivalent of arbitrary database table access. Appropriate for managing data purely through the underlying data store or repository API. +a|Persistence and API access + +a|System objects +a|Representation of target resource objects, such as accounts, but also resource objects such as groups. +a| + +a|Audit objects +a|Houses audit data in the OpenIDM internal repository. +a| + +a|Links +a|Defines a relation between two objects. +a| +|=== + +[#managed-objects] +=== Managed Objects + +A __managed object__ in OpenIDM is an object which represents the identity-related data managed by OpenIDM. Managed objects are stored by OpenIDM in its data store. All managed objects are JSON-based data structures. + +[#managed-object-schema] +==== Managed Object Schema + +OpenIDM provides a default schema for typical managed object types, such as users and roles, but does not control the structure of objects that you store in the OpenIDM repository. You can modify or extend the schema for the default object types, and you can set up a new managed object type for any item that can be collected in a data set. + +The `_rev` property of a managed object is reserved by OpenIDM for internal use, and is not explicitly part of its schema. This property specifies the revision of the object in the repository. This is the same value that is exposed as the object's ETag through the REST API. The content of this attribute is not defined. No consumer should make any assumptions of its content beyond equivalence comparison. This attribute may be provided by the underlying data store. + +Schema validation is performed by the policy service and can be configured according to the requirements of your deployment. For more information, see xref:chap-policies.adoc#chap-policies["Using Policies to Validate Data"]. + +Properties can be defined to be strictly derived from other properties within the object. This allows computed and composite values to be created in the object. Such properties are named __virtual properties__. The value of a virtual property is computed only when that property is retrieved. + + +[#managed-object-data-consistency] +==== Data Consistency + +Single-object operations are consistent within the scope of the operation performed, limited by the capabilities of the underlying data store. Bulk operations have no consistency guarantees. OpenIDM does not expose any transactional semantics in the managed object access API. + +All access through the REST API uses the ETag and associated conditional headers: `If-Match`, `If-None-Match`. In operations that modify objects, if no conditional header is provided, the default `If-Match: "*"` is applied. This header indicates that the call explicitly accepts overwriting other potential changes on the object. + + +[#managed-object-triggers] +==== Managed Object Triggers + +__Triggers__ are user-definable functions that validate or modify object or property state. + +[#managed-object-state-triggers] +===== State Triggers + +Managed objects are resource-oriented. A set of triggers is defined to intercept the supported request methods on managed objects. Such triggers are intended to perform authorization, redact, or modify objects before the action is performed. The object being operated on is in scope for each trigger, meaning that the object is retrieved by the data store before the trigger is fired. + +If retrieval of the object fails, the failure occurs before any trigger is called. Triggers are executed before any optimistic concurrency mechanisms are invoked. The reason for this is to prevent a potential attacker from getting information about an object (including its presence in the data store) before authorization is applied. +-- + +onCreate:: +Called upon a request to create a new object. Throwing an exception causes the create to fail. + +postCreate:: +Called after the creation of a new object is complete. + +onRead:: +Called upon a request to retrieve a whole object or portion of an object. Throwing an exception causes the object to not be included in the result. This method is also called when lists of objects are retrieved via requests to its container object; in this case, only the requested properties are included in the object. Allows for uniform access control for retrieval of objects, regardless of the method in which they were requested. + +onUpdate:: +Called upon a request to store an object. The `oldObject` and `newObject` variables are in-scope for the trigger. The `oldObject` represents a complete object, as retrieved from the data store. The trigger can elect to change `newObject` properties. If, as a result of the trigger, the values of the `oldObject` and `newObject` are identical (that is, update is reverted), the update ends prematurely, but successfully. Throwing an exception causes the update to fail. + +postUpdate:: +Called after an update request is complete. + +onDelete:: +Called upon a request to delete an object. Throwing an exception causes the deletion to fail. + +postDelete:: +Called after an object is deleted. + +onSync:: +Called when a managed object is changed, and the change triggers an implicit synchronization operation. The implicit synchronization operation is triggered by calling the sync service, which attempts to to go through all the configured managed-system mappings, defined in `sync.json`. The sync service returns either a response or an error. For both the response and the error, script that is referenced by the `onSync` hook is called. + ++ +You can use this hook to inject business logic when the sync service either fails or succeeds to synchronize all applicable mappings. For an example of how the `onSync` hook is used to revert partial successful synchronization operations, see xref:chap-synchronization.adoc#sync-failure-compensation["Configuring Synchronization Failure Compensation"]. + +-- + + +[#managed-object-storage-triggers] +===== Object Storage Triggers + +-- +An object-scoped trigger applies to an entire object. Unless otherwise specified, the object itself is in scope for the trigger. + +onValidate:: +Validates an object prior to its storage in the data store. If an exception is thrown, the validation fails and the object is not stored. + +onStore:: +Called just prior to when an object is stored in the data store. Typically used to transform an object just prior to its storage (for example, encryption). + +-- + + +[#managed-object-property-storage-triggers] +===== Property Storage Triggers + +A property-scoped trigger applies to a specific property within an object. Only the property itself is in scope for the trigger. No other properties in the object should be accessed during execution of the trigger. Unless otherwise specified, the order of execution of property-scoped triggers is intentionally left undefined. +-- + +onValidate:: +Validates a given property value after its retrieval from and prior to its storage in the data store. If an exception is thrown, the validation fails and the property is not stored. + +onRetrieve:: +Called in the result of a query request. Executed only when the `executeOnRetrieve` condition shows a full managed object. + +onStore:: +Called prior to when an object is stored in the data store. Typically used to transform a given property prior to its object's storage. + +-- + + +[#managed-object-storage-trigger-sequences] +===== Storage Trigger Sequences + +Triggers are executed in the following order: +Object Retrieval Sequence + +. Retrieve the raw object from the data store + +. The `executeOnRetrieve` boolean is used to see if a full managed object is returned. The sequence continues if the boolean is set to `true`. + +. Call object `onRetrieve` trigger + +. Per-property within the object, call property `onRetrieve` trigger + +Object Storage Sequence + +. Per-property within the object: ++ + +* Call property `onValidate` trigger + +* Call object `onValidate` trigger + + +. Per-property trigger within the object: ++ + +* Call property `onStore` trigger + +* Call object `onStore` trigger + +* Store the object with any resulting changes to the data store + + + + + +[#managed-object-encryption] +==== Managed Object Encryption + +Sensitive object properties can be encrypted prior to storage, typically through the property `onStore` trigger. The trigger has access to configuration data, which can include arbitrary attributes that you define, such as a symmetric encryption key. Such attributes can be decrypted during retrieval from the data store through the property `onRetrieve` trigger. + + +[#managed-object-configuration] +==== Managed Object Configuration + +Configuration of managed objects is provided through an array of managed object configuration objects. + +[source, javascript] +---- +{ + "objects": [ managed-object-config object, ... ] +} +---- +-- + +objects:: +array of managed-object-config objects, required + ++ +Specifies the objects that the managed object service manages. + +-- +[#managed-object-config-object-properties] +.Managed-Object-Config Object Properties +-- +Specifies the configuration of each managed object. + +[source, javascript] +---- +{ + "name" : string, + "schema" : { + json-schema object, + "properties": { property-configuration objects }, + } + "onCreate" : script object, + "postCreate": script object, + "onRead" : script object, + "onUpdate" : script object, + "postUpdate": script object, + "onDelete" : script object, + "postDelete": script object, + "onValidate": script object, + "onRetrieve": script object, + "onStore" : script object, + "onSync" : script object +} +---- + +name:: +string, required + ++ +The name of the managed object. Used to identify the managed object in URIs and identifiers. + +schema:: +json-schema object, optional + ++ +The schema to use to validate the structure and content of the managed object. The schema-object format is specified by the JSON Schema specification. + +properties:: +list of property-config objects, optional + ++ +A list of property specifications. + +onCreate:: +script object, optional + ++ +A script object to trigger when the creation of an object is being requested. The object to be created is provided in the root scope as an `object` property. The script can change the object. If an exception is thrown, the create aborts with an exception. + +postCreate:: +script object, optional + ++ +A script object to trigger after an object is created, but before any targets are synchronized. + +onRead:: +script object, optional + ++ +A script object to trigger when the read of an object is being requested. The object being read is provided in the root scope as an `object` property. The script can change the object. If an exception is thrown, the read aborts with an exception. + +onUpdate:: +script object, optional + ++ +A script object to trigger when an update to an object is requested. The old value of the object being updated is provided in the root scope as an `oldObject` property. The new value of the object being updated is provided in the root scope as a `newObject` property. The script can change the `newObject`. If an exception is thrown, the update aborts with an exception. + +postUpdate:: +script object, optional + ++ +A script object to trigger after an update to an object is complete, but before any targets are synchronized. The value of the object before the update is provided in the root scope as an `oldObject` property. The value of the object after the update is provided in the root scope as a `newObject` property. + +onDelete:: +script object, optional + ++ +A script object to trigger when the deletion of an object is being requested. The object being deleted is provided in the root scope as an `object` property. If an exception is thrown, the deletion aborts with an exception. + +postDelete:: +script object, optional + ++ +A script object to trigger after a delete of an object is complete, but before any further synchronization. The value of the deleted object is provided in the root scope as an `oldObject` property. + +onValidate:: +script object, optional + ++ +A script object to trigger when the object requires validation. The object to be validated is provided in the root scope as an `object` property. If an exception is thrown, the validation fails. + +onRetrieve:: +script object, optional + ++ +A script object to trigger when an object is retrieved from the repository. The object that was retrieved is provided in the root scope as an `object` property. The script can change the object. If an exception is thrown, then object retrieval fails. + +onStore:: +script object, optional + ++ +A script object to trigger when an object is about to be stored in the repository. The object to be stored is provided in the root scope as an `object` property. The script can change the object. If an exception is thrown, then object storage fails. + +onSync:: +script object, optional + ++ +A script object to trigger when a change to a managed object triggers an implicit synchronization operation. The script has access to the `syncResults` object, the `request` object, the state of the object before the change (`oldObject`) and the state of the object after the change (`newObject`). The script can change the object. + +-- +[#managed-object-script-object-properties] +.Script Object Properties +-- + +[source, javascript] +---- +{ + "type" : "text/javascript", + "source": string +} +---- + +type:: +string, required + ++ +Specifies the type of script to be executed. Supported types include `"text/javascript"` and `"groovy"`. + +source, file:: +string, required (only one, source or file is required) + ++ +Specifies the source code of the script to be executed (if the keyword is "source"), or a pointer to the file that contains the script (if the keyword is "file"). + +-- +[#managed-object-property-config-properties] +.Property Config Properties +-- + +[source, javascript] +---- +{ + "property-name" : string, + "onValidate" : script object, + "onRetrieve" : script object, + "onStore" : script object, + "encryption" : property-encryption object, + "secureHash" : property-hash object, + "scope" : string, + "title" : string, + "viewable" : boolean true/false, + "type" : data type, + "searchable" : boolean true/false, + "userEditable" : boolean true/false, + "minLength" : positive integer, + "pattern" : string, + "policies" : policy object, + "required" : boolean true/false, + "isVirtual" : boolean true/false, + "returnByDefault" : boolean true/false +} +---- + +property-name:: +string, required + ++ +The name of the property being configured. + +onValidate:: +script object, optional + ++ +A script object to trigger when the property requires validation. The value of the property to be validated is provided in the root scope as the `property` property. If an exception is thrown, validation fails. + +onRetrieve:: +script object, optional + ++ +A script object to trigger once a property is retrieved from the repository. That property may be one of two related variables: `property` and `propertyName`. The property that was retrieved is provided in the root scope as the `propertyName` variable; its value is provided as the `property` variable. If an exception is thrown, then object retrieval fails. + +onStore:: +script object, optional + ++ +A script object to trigger when a property is about to be stored in the repository. That property may be one of two related variables: `property` and `propertyName`. The property that was retrieved is provided in the root scope as the `propertyName` variable; its value is provided as the `property` variable. If an exception is thrown, then object storage fails. + +encryption:: +property-encryption object, optional + ++ +Specifies the configuration for encryption of the property in the repository. If omitted or null, the property is not encrypted. + +secureHash:: +property-hash object, optional + ++ +Specifies the configuration for hashing of the property value in the repository. If omitted or null, the property is not hashed. + +scope:: +string, optional + ++ +Specifies whether the property should be filtered from HTTP/external calls. The value can be either `"public"` or `"private"`. `"private"` indicates that the property should be filtered, `"public"` indicates no filtering. If no value is set, the property is assumed to be public and thus not filtered. + +title:: +string, required + ++ +A human-readable string, used to display the property in the UI. + +viewable:: +boolean, true/false + ++ +Specifies whether this property is viewable in the object's profile in the UI. True by default. + +type:: +data type, required + ++ +The data type for the property value; can be String, Array, Boolean, Integer, Number, Object, or Resource Collection. + +searchable:: +boolean, true/false + ++ +Specifies whether this property can be used in a search query on the managed object. A searchable property is visible within the Managed Object data grid in the Self-Service UI. False by default. + +userEditable:: +boolean, true/false + ++ +Specifies whether users can edit the property value in the UI. This property applies in the context of the self-service UI, in which users are able to edit certain properties of their own accounts. False by default. + +minLength:: +positive integer, optional + ++ +The minimum number of characters that the value of this property must have. + +pattern:: +string, optional + ++ +Any specific pattern to which the value of the property must adhere. For example, a property whose value is a date might require a specific date format. Patterns specified here must follow regular expression syntax. + +policies:: +policy object, optional + ++ +Any policy validation that must be applied to the property. + +required:: +boolean, true/false + ++ +Specifies whether or the property must be supplied when an object of this type is created. + +isVirtual:: +boolean, true/false + ++ +Specifies whether the property takes a static value, or whether its value is calculated "on the fly" as the result of a script. + ++ +The most recently calculated value of a virtual property is persisted by default. The persistence of virtual property values allows OpenIDM to compare the new value of the property against the last calculated value, and therefore to detect change events during synchronization. + ++ +Virtual property values are not persisted by default if you are using an explicit mapping. + +returnByDefault:: +boolean, true/false + ++ +For virtual properties, specifies whether the property will be returned in the results of a query on an object of this type if it is not explicitly requested. Virtual attributes are not returned by default. + +-- +[#managed-object-property-encryption-properties] +.Property Encryption Object +-- + +[source, javascript] +---- +{ + "cipher": string, + "key" : string +} +---- + +cipher:: +string, optional + ++ +The cipher transformation used to encrypt the property. If omitted or null, the default cipher of `"AES/CBC/PKCS5Padding"` is used. + +key:: +string, required + ++ +The alias of the key in the OpenIDM cryptography service keystore used to encrypt the property. + +-- +[#managed-object-property-hash-properties] +.Property Hash Object +-- + +[source, javascript] +---- +{ + "algorithm" : "string", + "type" : "string" +} +---- + +algorithm:: +string, required + ++ +The algorithm that should be used to hash the value. The following hash algorithms are supported: `MD5`, `SHA-1`, `SHA-256`, `SHA-384`, `SHA-512`. + +type:: +string, optional + ++ +The type of hashing. Currently only salted hash is supported. If this property is omitted or null, the default `"salted-hash"` is used. + +-- + + +[#custom-managed-objects] +==== Custom Managed Objects + +Managed objects in OpenIDM are inherently fully user definable and customizable. Like all OpenIDM objects, managed objects can maintain relationships to each other in the form of links. Managed objects are intended for use as targets and sources for synchronization operations to represent domain objects, and to build up virtual identities. The name comes from the intention that OpenIDM stores and manages these objects, as opposed to system objects that are present in external systems. + +OpenIDM can synchronize and map directly between external systems (system objects), without storing intermediate managed objects. Managed objects are appropriate, however, as a way to cache the data—for example, when mapping to multiple target systems, or when decoupling the availability of systems—to more fully report and audit on all object changes during reconciliation, and to build up views that are different from the original source, such as transformed and combined or virtual views. Managed objects can also be allowed to act as an authoritative source if no other appropriate source is available. + +Other object types exist for other settings that should be available to a script, such as configuration or look-up tables that do not need audit logging. + +[#managed-objects-setup] +===== Setting Up a Managed Object Type + +To set up a managed object, you declare the object in the `conf/managed.json` file where OpenIDM is installed. The following example adds a simple `foobar` object declaration after the user object type. + +[source, javascript] +---- +{ + "objects": [ + { + "name": "user" + }, + { + "name": "foobar" + } + ] +} +---- + + +[#managed-objects-declarative] +===== Manipulating Managed Objects Declaratively + +By mapping an object to another object, either an external system object or another internal managed object, you automatically tie the object life cycle and property settings to the other object. For more information, see xref:chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"]. + + +[#managed-objects-programmatic] +===== Manipulating Managed Objects Programmatically + +You can address managed objects as resources using URLs or URIs with the `managed/` prefix. This works whether you address the managed object internally as a script running in OpenIDM or externally through the REST interface. + +You can use all resource API functions in script objects for create, read, update, delete operations, and also for arbitrary queries on the object set, but not currently for arbitrary actions. For more information, see xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"]. + +OpenIDM supports concurrency through a multi version concurrency control (MVCC) mechanism. In other words, each time an object changes, OpenIDM assigns it a new revision. + +Objects can be arbitrarily complex as long as they use supported types, such as maps, lists, numbers, strings, and booleans as defined in link:http://www.json.org[JSON, window=\_blank]. + +[#managed-objects-programmatic-create] +====== Creating Objects + +The following script example creates an object type. + +[source, javascript] +---- +openidm.create("managed/foobar", "myidentifier", mymap) +---- + + +[#managed-objects-programmatic-update] +====== Updating Objects + +The following script example updates an object type. + +[source, javascript] +---- +var expectedRev = origMap._rev +openidm.update("managed/foobar/myidentifier", expectedRev, mymap) +---- +The MVCC mechanism requires that `expectedRev` be set to the expected revision of the object to update. You obtain the revision from the object's `_rev` property. If something else changes the object concurrently, OpenIDM rejects the update, and you must either retry or inspect the concurrent modification. + + +[#managed-objects-programmatic-patch] +====== Patching Objects + +You can partially update a managed or system object using the patch method, which changes only the specified properties of the object. + +The following script example updates an object type. + +[source, javascript] +---- +openidm.patch("managed/foobar/myidentifier", rev, value) +---- +The patch method supports a revision of `"null"`, which effectively disables the MVCC mechanism, that is, changes are applied, regardless of revision. In the REST interface, this matches the `If-Match: "*"` condition supported by patch. Alternatively, you can omit the "If-Match: *" header. + +For managed objects, the API supports patch by query, so the caller does not need to know the identifier of the object to change. + +[source, console] +---- +$ 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":"/password", + "value":"Passw0rd" + }]' \ + "https://localhost:8443/openidm/managed/user?_action=patch&_queryId=for-userName&uid=DDOE" +---- +For the syntax on how to formulate the query `_queryId=for-userName&uid=DDOE` see xref:#managed-objects-programmatic-query["Querying Object Sets"]. + + +[#managed-objects-programmatic-delete] +====== Deleting Objects + +The following script example deletes an object type. + +[source, javascript] +---- +var expectedRev = origMap._rev +openidm.delete("managed/foobar/myidentifier", expectedRev) +---- +The MVCC mechanism requires that `expectedRev` be set to the expected revision of the object to update. You obtain the revision from the object's `_rev` property. If something else changes the object concurrently, OpenIDM rejects deletion, and you must either retry or inspect the concurrent modification. + + +[#managed-objects-programmatic-read] +====== Reading Objects + +The following script example reads an object type. + +[source, javascript] +---- +val = openidm.read("managed/foobar/myidentifier") +---- + + +[#managed-objects-programmatic-query] +====== Querying Object Sets + +You can query managed objects using common query filter syntax, or by configuring predefined queries in your repository configuration. The following script example queries managed user objects whose userName is Smith. + +[source, javascript] +---- +var qry = { + "_queryFilter" : "/userName eq \"smith\"" +}; +val = openidm.query("managed/user", qry); +---- +For more information, see xref:chap-data.adoc#queries["Defining and Calling Queries"]. + + + + +[#managed-objects-access-rest] +==== Accessing Managed Objects Through the REST API + +OpenIDM exposes all managed object functionality through the REST API unless you configure a policy to prevent such access. In addition to the common REST functionality of create, read, update, delete, patch, and query, the REST API also supports patch by query. For more information, see xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + +OpenIDM requires authentication to access the REST API. The authentication configuration is provided in your project's `conf/authentication.json` file. The default authorization filter script is `openidm/bin/defaults/script/router-authz.js`. For more information, see xref:chap-auth.adoc#openidm-authentication["OpenIDM Authentication"]. + + + +[#configuration-objects-reference] +=== Configuration Objects + +OpenIDM provides an extensible configuration to allow you to leverage regular configuration mechanisms. + +Unlike native OpenIDM configuration, which OpenIDM interprets automatically and can start new services, OpenIDM stores custom configuration objects and makes them available to your code through the API. + +For an introduction to the standard configuration objects, see xref:chap-configuration.adoc#chap-configuration["Configuring OpenIDM"]. + +[#configuration-objects-when-to-use] +==== When To Use Custom Configuration Objects + +Configuration objects are ideal for metadata and settings that need not be included in the data to reconcile. In other words, use configuration objects for data that does not require audit log, and does not serve directly as a target or source for mappings. + +Although you can set and manipulate configuration objects both programmatically and manually, configuration objects are expected to change slowly, perhaps through a mix of both manual file updates and programmatic updates. To store temporary values that can change frequently and that you do not expect to be updated by configuration file changes, custom repository objects might be more appropriate. + + +[#configuration-objects-naming] +==== Custom Configuration Object Naming Conventions + +By convention custom configuration objects are added under the reserved context, `config/custom`. + +You can choose any name under `config/context`. Be sure, however, to choose a value for __context__ that does not clash with future OpenIDM configuration names. + + +[#configuration-objects-file-mapping] +==== Mapping Configuration Objects To Configuration Files + +If you have not disabled the file based view for configuration, you can view and edit all configuration including custom configuration in `openidm/conf/*.json` files. The configuration maps to a file named `context-config-name.json`, where __context__ for custom configuration objects is `custom` by convention, and __config-name__ is the configuration object name. A configuration object named `escalation` thus maps to a file named `conf/custom-escalation.json`. + +OpenIDM detects and automatically picks up changes to the file. + +OpenIDM also applies changes made through APIs to the file. + +By default, OpenIDM stores configuration objects in the repository. The file view is an added convenience aimed to help you in the development phase of your project. + + +[#configuration-objects-formats] +==== Configuration Objects File & REST Payload Formats + +By default, OpenIDM maps configuration objects to JSON representations. + +OpenIDM represents objects internally in plain, native types like maps, lists, strings, numbers, booleans, null. OpenIDM constrains the object model to simple types so that mapping objects to external representations is trivial. + +The following example shows a representation of a configuration object with a look-up map. + +[source, javascript] +---- +{ + "CODE123" : "ALERT", + "CODE889" : "IGNORE" +} +---- +In the JSON representation, maps are represented with braces (`{ }`), and lists are represented with brackets (`[ ]`). Objects can be arbitrarily complex, as in the following example. + +[source, javascript] +---- +{ + "CODE123" : { + "email" : ["sample@sample.com", "john.doe@somedomain.com"], + "sms" : ["555666777"] + } + "CODE889" : "IGNORE" +} +---- + + +[#configuration-objects-access-rest] +==== Accessing Configuration Objects Through the REST API + +You can list all available configuration objects, including system and custom configurations, using an HTTP GET on `/openidm/config`. + +The `_id` property in the configuration object provides the link to the configuration details with an HTTP GET on `/openidm/config/id-value`. By convention, the __id-value__ for a custom configuration object called `escalation` is `custom/escalation`. + +OpenIDM supports REST mappings for create, read, update, query, and delete of configuration objects. Currently OpenIDM does not support patch operations for configuration objects. + + +[#configuration-objects-access-programmatic] +==== Accessing Configuration Objects Programmatically + +You can address configuration objects as resources using the URL or URI `config/` prefix both internally and also through the REST interface. The resource API provides script object functions for create, read, update, query, and delete operations. + +OpenIDM supports concurrency through a multi version concurrency control mechanism. In other words, each time an object changes, OpenIDM assigns it a new revision. + +Objects can be arbitrarily complex as long as they use supported types, such as maps, lists, numbers, strings, and booleans. + + +[#configuration-objects-programmatic-create] +==== Creating Objects + +The following script example creates an object type. + +[source, javascript] +---- +openidm.create("config/custom", "myconfig", mymap) +---- + + +[#configuration-objects-programmatic-update] +==== Updating Objects + +The following script example updates a custom configuration object type. + +[source, javascript] +---- +openidm.update("config/custom/myconfig", mymap) +---- + + +[#configuration-objects-programmatic-delete] +==== Deleting Objects + +The following script example deletes a custom configuration object type. + +[source, javascript] +---- +openidm.delete("config/custom/myconfig") +---- + + +[#configuration-objects-programmatic-read] +==== Reading Objects + +The following script example reads an object type. + +[source, javascript] +---- +val = openidm.read("config/custom/myconfig") +---- + + + +[#system-objects] +=== System Objects + +__System objects__ are pluggable representations of objects on external systems. They follow the same RESTful resource based design principles as managed objects. There is a default implementation for the OpenICF framework, which allows any connector object to be represented as a system object. + + +[#audit-objects] +=== Audit Objects + +Audit objects house audit data selected for local storage in the OpenIDM repository. For details, see xref:chap-auditing.adoc#chap-auditing["Using Audit Logs"]. + + +[#links] +=== Links + +Link objects define relations between source objects and target objects, usually relations between managed objects and system objects. The link relationship is established by provisioning activity that either results in a new account on a target system, or a reconciliation or synchronization scenario that takes a `LINK` action. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-ports-used.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-ports-used.adoc new file mode 100644 index 000000000..b2b4b711e --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-ports-used.adoc @@ -0,0 +1,44 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-ports-used] +== Ports Used + +-- +By default, OpenIDM 4.5 listens on the following ports (specified in the file `/path/to/openidm/conf/boot/boot.properties`): + +`8080`:: ++ +HTTP access to the REST API, requiring OpenIDM authentication. This port is not secure, exposing clear text passwords and all data that is not encrypted. This port is therefore not suitable for production use. + +`8443`:: ++ +HTTPS access to the REST API, requiring OpenIDM authentication + +`8444`:: ++ +HTTPS access to the REST API, requiring SSL mutual authentication. Clients that present certificates found in the truststore under `openidm/security/` are granted access to the system. + +-- +The Jetty configuration (in `openidm/conf/jetty.xml`) references the ports that are specified in the `boot.properties` file. + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc new file mode 100644 index 000000000..4eee53b12 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc @@ -0,0 +1,1278 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-rest] +== REST API Reference + +Representational State Transfer (REST) is a software architecture style for exposing resources, using the technologies and protocols of the World Wide Web. REST describes how distributed data objects, or resources, can be defined and addressed. OpenIDM provides a RESTful API for accessing managed objects, system objects, workflows, and some elements of the system configuration. + +The ForgeRock implementation of REST, known as commons REST (CREST), defines an API intended for common use across all ForgeRock products. CREST is a framework used to access various web resources, and for writing to RESTful resource providers (servers). + +CREST is intended to support the following types of operations, described in detail in xref:#rest-supported-operations["Supported Operations"]: `Create`, `Read`, `Update`, `Delete`, `Action`, and `Query`. + +[NOTE] +==== +The examples in this chapter show REST requests to OpenIDM over the regular (http) port. +==== +ForgeRock defines a JSON Resource core library, as a common framework to implement RESTful APIs. That core library includes two components: +-- + +`json-resource`:: +A Maven module that provides core interfaces such as `Connections`, `Requests`, and `Request Handlers`. + +`json-resource-servlet`:: +Provides J2EE servlet integration. Defines a common HTTP-based REST API for interacting with JSON resources. + +-- + +[NOTE] +==== +You can examine the libraries associated with ForgeRock REST at http://commons.forgerock.org/forgerock-rest. +==== + +[#rest-uri-scheme] +=== URI Scheme + +The URI scheme for accessing a managed object follows this convention, assuming the OpenIDM web application was deployed at `/openidm`. + +[source] +---- +/openidm/managed/type/id +---- +Similar schemes exist for URIs associated with all but system objects. For more information, see xref:chap-auth.adoc#access-js["Understanding the Access Configuration Script (access.js)"]. + +The URI scheme for accessing a system object follows this convention: + +[source] +---- +/openidm/system/resource-name/type/id +---- +An example of a system object in an LDAP repository might be: + +[source] +---- +/openidm/system/ldap/account/07b46858-56eb-457c-b935-cfe6ddf769c7 +---- +Note that for LDAP resources, you should not map the LDAP `dn` to the OpenIDM `uidAttribute` (`_id`). The attribute that is used for the `_id` should be immutable. You should therefore map the LDAP `entryUUID` operational attribute to the OpenIDM `_id`, as shown in the following excerpt of the provisioner configuration file: + +[source] +---- +... +"uidAttribute" : "entryUUID", +... +---- + + +[#rest-object-identifier] +=== Object Identifiers + +Every managed and system object has an identifier (expressed as __id__ in the URI scheme) that is used to address the object through the REST API. The REST API allows for client-generated and server-generated identifiers, through PUT and POST methods. The default server-generated identifier type is a UUID. If you create an object by using `POST`, a server-assigned ID is generated in the form of a UUID. If you create an object by using PUT, the client assigns the ID in whatever format you specify. + +Most of the examples in this guide use client-assigned IDs, as it makes the examples easier to read. + +For more information on whether to use PUT or POST to create managed objects, see xref:#put-post-managed-objects[Should You Use PUT or POST to Create a Managed Object?]. + + +[#rest-content-negotiation] +=== Content Negotiation + +The REST API fully supports negotiation of content representation through the `Accept` HTTP header. Currently, the supported content type is JSON. When you send a JSON payload, you must include the following header: + +[source] +---- +Accept: application/json +---- +In a REST call (using the `curl` command, for example), you would include the following option to specify the noted header: + +[source] +---- +--header "Content-Type: application/json" +---- +You can also specify the default UTF-8 character set as follows: + +[source] +---- +--header "Content-Type: application/json;charset=utf-8" +---- +The `application/json` content type is not needed when the REST call does not send a JSON payload. + + +[#rest-supported-operations] +=== Supported Operations + +CREST supports several types of operations for communication with web servers. + +The following request parameters can be used in conjunction with the supported operations. +-- + +`_fields`:: +The `_fields` parameter can be used to return multiple common attributes. + ++ +For example, you can use `GET` to read specific attributes for a user as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET + "http://localhost:8080/openidm/managed/user/james?_fields=userName,mail" + { + "mail": "james@example.com", + "userName": "james" + } +---- + +`_prettyPrint=[true,false]`:: +If `_prettyPrint=true`, the `HttpServlet` formats the response, in a fashion similar to the JSON parser known as link:http://stedolan.github.io/jq/[jq, window=\_top]. + ++ +For example, adding `_prettyPrint=true` to the end of a `query-all-ids` request formats the output in the following manner: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids&_prettyPrint=true" +{ + "result" : [ { + "_id" : "bjensen", + "_rev" : "0" + }, { + "_id" : "scarter", + "_rev" : "0" + }, { + "_id" : "jberg", + "_rev" : "0" + } ], + "resultCount" : 3, + "pagedResultsCookie" : null, + "remainingPagedResults" : -1 +} +---- ++ +Note that most command-line examples in this guide do not show this parameter, although the output in the examples is formatted for readability. + +-- + +[#rest-supported-create] +==== Creating an Object + +Objects can be created with two different HTTP operations: `POST` and `PUT`. + +To create an object with a server-assigned ID, use the `POST` operation with the `create` action. For example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "userName":"mike", + "sn":"Smith", + "givenName":"Mike", + "mail": "mike@example.com", + "telephoneNumber": "082082082", + "password":"Passw0rd" + }' + "http://localhost:8080/openidm/managed/user?_action=create" +{ + "userName": "mike", + ... + "_rev": "1", + "_id": "a5bed4d7-99d4-41c4-8d64-49493b48a920", + ... +} +---- +To create an object with a client-assigned ID, use a `PUT` request, with the `If-None-Match: *` header. Specify the ID as part of the URL, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-None-Match: *" \ + --request PUT \ + --data '{ + "userName":"james", + "sn":"Berg", + "givenName":"James", + "mail": "james@example.com", + "telephoneNumber": "082082082", + "password":"Passw0rd" + }' \ + "http://localhost:8080/openidm/managed/user/james" +{ + "userName": "james", + ... + "_rev": "1", + ... + "_id": "james", + ... +} +---- + + +[#rest-supported-read] +==== Reading an Object + +To read the contents of an object, use the `GET` operation, specifying the object ID. For example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account/fc252fd9-b982-3ed6-b42a-c76d2546312c" +{ + "givenName": "Barbara", + "telephoneNumber": "1-360-229-7105", + "dn": "uid=bjensen,ou=People,dc=example,dc=com", + "description": "Created for OpenIDM", + "mail": "bjensen@example.com", + "ldapGroups": [ + "cn=openidm2,ou=Groups,dc=example,dc=com" + ], + "cn": "Barbara Jensen", + "uid": "bjensen", + "sn": "Jensen", + "_id": "fc252fd9-b982-3ed6-b42a-c76d2546312c" +} +---- + + +[#rest-supported-update] +==== Updating an Object + +An update replaces some or all of the contents of an existing object. Any object can be updated over REST with a PUT request. Managed objects and some system objects can also be updated with a `PATCH` request. + +To update a managed or system object with a PUT request, specify the object ID in the URL. For managed objects, you must include the complete object in the JSON payload. You can also include an optional `If-Match` conditional header. If no conditional header is specified, a default of `If-Match: "*"` is applied. + +The following example updates Joe Smith's telephone number, and supplies his complete managed user object, with the updated value, in the JSON payload: + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "userName":"joe", + "givenName":"joe", + "sn":"smith", + "mail":"joe@example.com", + "telephoneNumber":"555-123-457", + "password":"Passw0rd", + "description":"This is Joe Smith's description" + }' \ + "http://localhost:8080/openidm/managed/user/07b46858-56eb-457c-b935-cfe6ddf769c7" +---- +A PATCH request can add, remove, replace, or increment an attribute value. A `replace` operation replaces an existing value, or adds a value if no value exists. + +When you update a managed or system object with a PATCH request, you can include the optional `If-Match` conditional header. If no conditional header is specified, a default of `If-Match: "*"` is applied. + +The following example shows a patch request that updates a multi-valued attribute by adding a new value. Note the dash `-` character appended to the field name, which specifies that the value provided should be added to the existing values. If the dash character is omitted, the provided value replaces the existing values of that field. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PATCH \ + --data '[ + { + "operation": "add", + "field": "/roles/-", + "value": "managed/role/ldap" + + } +]' \ + "http://localhost:8080/openidm/managed/user/bjensen" +---- + + +[#rest-supported-delete] +==== Deleting an Object + +A delete request is similar to an update request, and can optionally include the HTTP `If-Match` header. To delete an object, specify its ID in the request, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/system/ldap/account/e81c7f15-2e6d-4c3c-8005-890101070dd9" +{ + "_id": "e81c7f15-2e6d-4c3c-8005-890101070dd9" +} +---- + + +[#rest-supported-query] +==== Querying Resources + +Resources can be queried using the `GET` method, with one of the following query parameters: +For queries on managed objects: + +* `_queryId` for arbitrary predefined, parameterized queries + +* `_queryFilter` for arbitrary filters, in common filter notation + +* `_queryExpression` for client-supplied queries, in native query format + +For queries on system objects: + +* `_queryId=query-all-ids` (the only supported predefined query) + +* `_queryFilter` for arbitrary filters, in common filter notation + +For more information on queries, see xref:chap-data.adoc#constructing-queries["Constructing Queries"]. + + + +[#rest-conditional-operations] +=== Conditional Operations + +The REST API supports conditional operations through the use of the `ETag`, `If-Match` and `If-None-Match` HTTP headers. The use of HTTP conditional operations is the basis of OpenIDM's optimistic concurrency control system. Clients should make requests conditional in order to prevent inadvertent modification of the wrong version of an object. If no conditional header is specified, a default of `If-Match: *` is applied. + + +[#rest-supported-methods] +=== Supported Methods + +The managed object API uses standard HTTP methods to access managed objects. +-- + +GET:: +Retrieves a managed object in OpenIDM. + ++ +Example Request ++ + +[source, httprequest] +---- +GET /openidm/managed/user/bdd793f8 +... +---- ++ +Example Response ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Type: application/json;charset=UTF-8 +Cache-Control: no-cache +Vary: Accept-Encoding, User-Agent +Set-Cookie: session-jwt=2sadf... afd5;Path=/ +Expires: Thu, 01 Jan 2015 00:00:00 GMT +Content-Length: 1230 +Server: Jetty(8.y.z-SNAPSHOT) +... + +[JSON representation of the managed object] +---- + +PUT:: +Creates or updates a managed object. ++ + +[NOTE] +====== +If you include the `If-None-Match` header, its value must be `*`. In this case, the request creates the object if it does not exist and fails if the object does exist. If you include the `If-None-Match` header with any value other than `*`, the server returns an HTTP 400 Bad Request error. For example, creating an object with `If-None-Match: revision` returns a bad request error. If you do not include `If-None-Match: *`, the request creates the object if it does not exist, and __updates__ the object if it does exist. +====== ++ +Example Request: Creating a new object ++ + +[source, httprequest] +---- +PUT /openidm/managed/user/5752c0fd9509 +Content-Type: application/json +Content-Length: 123 +If-None-Match: * +... + +[JSON representation of the managed object to create] +---- ++ +Example Response: Creating a new object (success) ++ + +[source, httprequest] +---- +HTTP/1.1 201 Created +Content-Type: application/json +Content-Length: 45 +ETag: "0" +... + +[JSON representation containing metadata (underscore-prefixed) properties] +---- ++ +Example Response: Creating or updating an object with the `If-None-Match` header set to something other than `*` ++ + +[source, httprequest] +---- +HTTP/1.1 400 "Bad Request +Content-Type: application/json +Content-Length: 83 +... + +[JSON representation of error] +---- ++ +Example Request: Updating an existing object ++ + +[source, httprequest] +---- +PUT /openidm/managed/user/5752c0fd9509 +Content-Type: application/json +Content-Length: 123 +If-Match: "1" +... + +[JSON representation of managed object to update] +---- ++ +Example Response: Updating an existing object (success) ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 45 +ETag: "2" +... + +[JSON representation of updated object] +---- ++ +Example Response: Updating an existing object when no version is supplied ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 89 +ETag: "3" +... + +[JSON representation of updated object] +---- ++ +Example Response: Updating an existing object when an invalid version is supplied ++ + +[source, httprequest] +---- +HTTP/1.1 412 Precondition Required +Content-Type: application/json +Content-Length: 89 +... + +[JSON representation of error] +---- ++ +Example Response: Updating an existing object with `If-Match: *` ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 45 +ETag: "0" +... +[JSON representation of updated object] +---- ++ + +[#put-post-managed-objects] +.Should You Use PUT or POST to Create a Managed Object? +[NOTE] +====== +You can use PUT and POST to create managed objects. To create a managed object with a PUT, you would include the `_id` in the request. If you create a managed object with a POST, the server assigns the `_id` in the form of a UUID. + +In some cases, you may want to use PUT, as POST is not idempotent. If you can specify the `_id` to assign to the object, use PUT. + +Alternatively, POST generates a server-assigned ID in the form of a UUID. In some cases, you may prefer to use UUIDs in production, as a POST can generate them easily in clustered environments. +====== + +POST:: +The POST method enables you to perform arbitrary actions on managed objects. The `_action` query parameter defines the action to be performed. + ++ +The `create` action is used to create a managed object. Because POST is neither safe nor idempotent, PUT is the preferred method of creating managed objects, and should be used if the client knows what identifier it wants to assign the object. The response contains the server-generated `_id` of the newly created managed object. + ++ +The POST method create optionally accepts an `_id` query parameter to specify the identifier to give the newly created object. If an `_id` is not provided, the server selects its own identifier. + ++ +The `patch` action updates one or more attributes of a managed object, without replacing the entire object. + ++ +Example Create Request ++ + +[source, httprequest] +---- +POST /openidm/managed/user?_action=create +Content-Type: application/json;charset=UTF-8 +Content-Length: 123 +... + +[JSON representation of the managed object to create] +---- ++ +Example Response ++ + +[source, httprequest] +---- +HTTP/1.1 201 Created +Content-Type: application/json;charset=UTF-8 +Cache-Control: no-cache +Location: https://Some_URI +... + +[JSON representation containing metadata (underscore-prefixed) properties] +---- ++ +Example Response (success) ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Type: application/json;charset=UTF-8 +Cache-Control: no-cache +Set-Cookie: session-jwt=yAiYWxnIjogI;Path=/ +... +---- ++ +Example Response: Updating an existing object when an invalid version is supplied ++ + +[source, httprequest] +---- +HTTP/1.1 412 Precondition Failed +Content-Type: application/json +Content-Length: 89 +... + +[JSON representation of error] +---- + +DELETE:: +Deletes a managed object. + ++ +Example Request ++ + +[source, httprequest] +---- +DELETE /openidm/managed/user/c3471805b60f +If-Match: "0" +... +---- ++ +Example Response (success) ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Length: 405 +Content-Type: application/json;charset=UTF-8 +Etag: "4" +... + +[JSON representation of the managed object that was deleted] +---- ++ +Example Response: Deleting an existing object when no version is supplied ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Content-Length: 405 +Content-Type: application/json;charset=UTF-8 +Etag: "4" +... + +[JSON representation of the managed object that was deleted] +---- ++ +Example Response: Deleting an existing object when an invalid version is supplied ++ + +[source, httprequest] +---- +HTTP/1.1 412 Precondition Failed +Content-Type: application/json;charset=UTF-8 +Content-Length: 89 +... + +[JSON representation of error] +---- + +PATCH:: +Performs a partial modification of a managed or system object. + ++ +Example Request ++ + +[source, httprequest] +---- +PATCH /openidm/managed/user/5752c0fd9509 +Content-Type: application/patch+json +Content-Length: 456 +If-Match: "0" +... + +[JSON representation of patch document to apply] +---- ++ +Example Response (success) ++ + +[source, httprequest] +---- +HTTP/1.1 200 OK +Set-Cookie: JSESSIONID=1kke440cyv1vivbrid6ljso7b;Path=/ +Expires: Thu, 01 Jan 1970 00:00:00 GMT +Content-Type: application/json; charset=UTF-8 +ETag: "1" +... +{"_id":"5752c0fd9509","_rev":"2"} +---- ++ +Updating an existing object when no version is supplied (version conflict) ++ + +[source, httprequest] +---- +HTTP/1.1 409 Conflict +Content-Type: application/json;charset=UTF-8 +Content-Length: 89 +... + +[JSON representation of error] +---- ++ +Example Response: Updating an existing object when an invalid version is supplied (version conflict) ++ + +[source, httprequest] +---- +HTTP/1.1 412 Precondition Required +Content-Type: application/json;charset=UTF-8 +Content-Length: 89 +... + +[JSON representation of error] +---- + +-- + + +[#sample-rest-commands] +=== REST Endpoints and Sample Commands + +This section describes the OpenIDM REST endpoints and provides a number of sample commands that show the interaction with the REST interface. + +[#rest-server-config] +==== Managing the Server Configuration Over REST + +OpenIDM stores configuration objects in the repository, and exposes them under the context path `/openidm/config`. Single instance configuration objects are exposed under `/openidm/config/object-name`. + +Multiple instance configuration objects are exposed under `/openidm/config/object-name/instance-name`. The following table outlines these configuration objects and how they can be accessed through the REST interface. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== +OpenIDM supports REST mappings for create, read, update, query, and delete of configuration objects. + +For an example that displays the current configuration, the current logging configuration, the configuration with an XML connector provisioner, and how the configuration can be modified over the router, see xref:chap-configuration.adoc#configuring-over-rest["Configuring OpenIDM Over REST"]. + +One entry is returned for each configuration object. To obtain additional information on the configuration object, include its `pid` or `_id` in the URL. The following example displays configuration information on the `sync` object, based on OpenIDM using Sample 1. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/config/sync" +{ + "mappings": [ { + "target" : "managed/user", + "correlationQuery" : { + "type" : "text/javascript", + "source" : "var query = {'_queryId' : 'for-userName', 'uid' : source.name};query;" + }, + "properties" : [ { + "target" : "_id", + "source" : "_id" + }, { + "target" : "description", + "source" : "description" + }, { + "target" : "givenName", + "source" : "firstname" + }, { + "target" : "mail", + "source" : "email" + }, { +... +---- + + +[#managing-users-REST] +==== Managing Users Over REST + +User objects are stored in the repository and are exposed under the context path `/managed/user`. Many examples of REST calls related to this context path exist throughout this document. The following table lists available functionality associated with the `/managed/user` context path. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== +The following example retrieves the JSON representation of all users stored in the internal repository. + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +---- +The following two examples perform a query on the repository for managed users for a user named `smith`. + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=userName+eq+%22smith%22" +---- +For this second example, note the use of single quotes around the URL, to avoid conflicts with the double quotes around the user named `smith`. Be aware, the `_queryFilter` requires double quotes (or the URL encoded equivalent, `%22`,) around the search term. + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +'http://localhost:8080/openidm/managed/user?_queryFilter=userName+eq+"smith"' +---- +The following example retrieves the JSON representation of a specified user. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/user_id" +---- +To add a user without a specified ID, see xref:../samples-guide/chap-xml-samples.adoc#sample-adding-users-rest["Adding Users Over REST"] in the __Samples Guide__. + +The following example adds a user with a specific user ID. + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "If-None-Match: *" \ + --request PUT \ + --data '{ + "userName":"james", + "sn":"Berg", + "givenName":"James", + "mail": "james@example.com", + "telephoneNumber": "082082082", + "password":"Passw0rd" + }' \ +"http://localhost:8080/openidm/managed/user/james" +---- +The following example checks whether a user exists, then updates the user entry. The command replaces the telephone number with the new data provided in the request body. + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '[{ + "operation":"replace", + "field":"/telephoneNumber", + "value":"1234567" + }]' \ + "http://localhost:8080/openidm/managed/user?_action=patch&_queryId=for-userName&uid=id" +---- + + +[#managing-system-objects-REST] +==== Managing System Objects Over REST + +System objects, that is, objects that are stored in remote systems, are exposed under the `/openidm/system` context. OpenIDM provides access to system objects over REST, as listed in the following table. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== + +[NOTE] +==== +When you create a system object with a PUT request (that is, specifying a client-assigned ID), you should specify the ID in the URL only and not in the JSON payload. If you specify a different ID in the URL and in the JSON payload, the request will fail, with an error similar to the following: + +[source, console] +---- +{ + "code":500, + "reason":"Internal Server Error", + "message":"The uid attribute is not single value attribute." +} +---- +A `POST` request with a `patch` action is not currently supported on system objects. To patch a system object, you must send a `PATCH` request. +==== + +[#d0e31723] +.Returning a list of the available connector configurations +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=availableConnectors" +---- +==== + +[#d0e31731] +.Returning a list of remote systems, and their status +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=test" +[ + { + "ok": true, + "displayName": "LDAP Connector", + "connectorRef": { + "bundleVersion": "[1.4.0.0,2.0.0.0)", + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "connectorName": "org.identityconnectors.ldap.LdapConnector" + }, + "objectTypes": [ + "__ALL__", + "group", + "account" + ], + "config": "config/provisioner.openicf/ldap", + "enabled": true, + "name": "ldap" + } +] +---- +==== + +[#d0e31742] +.Two options for running a liveSync operation on a specified system object +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=liveSync&source=system/ldap/account" +{ + "_rev": "1", + "_id": "SYSTEMLDAPACCOUNT", + "connectorData": { + "nativeType": "integer", + "syncToken": 0 + } +} +---- + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ldap/account?_action=liveSync" + +{ + "_rev": "2", + "_id": "SYSTEMLDAPACCOUNT", + "connectorData": { + "nativeType": "integer", + "syncToken": 0 + } +} +---- +==== + +[#d0e31761] +.Running a script on a system object +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ldap/account?_action=script&_scriptId=addUser" +---- +==== + +[#d0e31769] +.Authenticating to a system object +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ldap/account?_action=authenticate&username=bjensen&password=Passw0rd" +{ + "_id": "fc252fd9-b982-3ed6-b42a-c76d2546312c" +} +---- +==== + +[#d0e31780] +.Creating a new system object +==== + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --data '{ + "cn":"James Smith", + "dn":"uid=jsmith,ou=people,dc=example,dc=com", + "uid":"jsmith", + "sn":"Smith", + "givenName":"James", + "mail": "jsmith@example.com", + "description":"Created by OpenIDM REST"}' \ + --request POST \ + "http://localhost:8080/openidm/system/ldap/account?_action=create" +{ + "telephoneNumber":null, + "description":"Created by OpenIDM REST", + "mail":"jsmith@example.com", + "givenName":"James", + "cn":"James Smith", + "dn":"uid=jsmith,ou=people,dc=example,dc=com", + "uid":"jsmith", + "ldapGroups":[], + "sn":"Smith", + "_id":"07b46858-56eb-457c-b935-cfe6ddf769c7" +} +---- +==== + +[#d0e31791] +.Renaming a system object +==== +You can rename a system object simply by supplying a new naming attribute value in a PUT request. The PUT request replaces the entire object. The naming attribute depends on the external resource. + +The following example renames an object on an LDAP server, by changing the DN of the LDAP object (effectively performing a modDN operation on that object). + +The example renames the user created in the previous example. + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "If-Match: *" \ + --data '{ + "cn":"James Smith", + "dn":"uid=jimmysmith,ou=people,dc=example,dc=com", + "uid":"jimmysmith", + "sn":"Smith", + "givenName":"James", + "mail": "jsmith@example.com"}' \ + --request PUT \ + "http://localhost:8080/openidm/system/ldap/account/07b46858-56eb-457c-b935-cfe6ddf769c7" +{ + "mail":"jsmith@example.com", + "cn":"James Smith", + "sn":"Smith", + "dn":"uid=jimmysmith,ou=people,dc=example,dc=com", + "ldapGroups":[], + "telephoneNumber":null, + "description":"Created by OpenIDM REST", + "givenName":"James", + "uid":"jimmysmith", + "_id":"07b46858-56eb-457c-b935-cfe6ddf769c7" +} +---- +==== + +[#d0e31808] +.List the IDs associated with a specific system object +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 3, + "result": [ + { + "dn": "uid=jdoe,ou=People,dc=example,dc=com", + "_id": "1ff2e78f-4c4c-300c-b8f7-c2ab160061e0" + }, + { + "dn": "uid=bjensen,ou=People,dc=example,dc=com", + "_id": "fc252fd9-b982-3ed6-b42a-c76d2546312c" + }, + { + "dn": "uid=jimmysmith,ou=people,dc=example,dc=com", + "_id": "07b46858-56eb-457c-b935-cfe6ddf769c7" + } + ] +} +---- +==== + + +[#managing-workflows-over-REST] +==== Managing Workflows Over REST + +Workflow objects are exposed under the `/openidm/workflow` context. OpenIDM provides access to the workflow module over REST, as listed in the following table. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== +The following examples list the defined workflows. For a workflow to appear in this list, the corresponding workflow definition must be in the `openidm/workflow` directory. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/workflow/processdefinition?_queryId=query-all-ids" +---- +Depending on the defined workflows, the output will be something like the following: + +[source, console] +---- +{ +"result":[ { + "tenantId" : "", + "candidateStarterGroupIdExpressions" : [ ], + "candidateStarterUserIdExpressions" : [ ], + "participantProcess" : null, +... + } ], + "resultCount" : 1, + "pagedResultsCookie" : null, + "remainingPagedResults" : -1 +} +---- +The following example invokes a workflow named "myWorkflow". The `foo` parameter is given the value `bar` in the workflow invocation. + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "_key":"contractorOnboarding", + "foo":"bar" + }' \ + "http://localhost:8080/openidm/workflow/processinstance?_action=create" +---- + + +[#managing-scanned-REST] +==== Managing Scanned Tasks Over REST + +OpenIDM provides a task scanning mechanism that enables you to perform a batch scan for a specified date in OpenIDM data, on a scheduled interval, and then to execute a task when this date is reached. For more information about scanned tasks, see xref:chap-scheduler-conf.adoc#task-scanner["Scanning Data to Trigger Tasks"]. + +OpenIDM provides REST access to the task scanner, as listed in the following table. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== + + +[#accessing-log-REST] +==== Accessing Log Entries Over REST + +You can interact with the audit logs over REST, as shown in the following table. Queries on the audit endpoint must use `queryFilter` syntax. Predefined queries (invoked with the `_queryId` parameter) are not supported. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== + + +[#recon-over-REST] +==== Managing Reconciliation Operations Over REST + +You can interact with the reconciliation engine over REST, as shown in the following table. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== +The following example runs a reconciliation action, with the mapping `systemHrdb_managedUser`, defined in the `sync.json` file. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemHrdb_managedUser" +---- + + +[#security-over-REST] +==== Managing the Security Service Over REST + +You can interact with the security service over REST, as shown in the following table: + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== +For sample REST commands, see xref:chap-security.adoc#security-management-service["Accessing the Security Management Service"]. + + +[#repo-REST] +==== Managing the Repository Over REST + +You can interact with the repository engine over REST, as shown in the following table. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== +For examples of queries on the `repo/` endpoint, see xref:chap-repo.adoc#repo-over-rest["Interacting With the Repository Over REST"]. + + +[#update-REST] +==== Managing Updates Over REST + +You can interact with the updates engine over REST, as shown in the following table. + +[cols="50%,10%,40%"] +|=== +|URI |HTTP Operation |Description +|=== + +[#update-file-during] +.Update Status Message +[cols="40%,60%"] +|=== +|Status |Description + +a|IN_PROGRESS +a|Update has started, not yet complete + +a|PENDING_REPO_UPDATES +a|OpenIDM update is complete, updates to the repository are pending + +a|COMPLETE +a|Update is complete + +a|FAILED +a|Update failed, not yet reverted +|=== + + + +[#http-status-codes] +=== HTTP Status Codes + +The OpenIDM REST API returns the standard HTTP response codes, as described in the following table. + +[cols="40%,60%"] +|=== +|HTTP Status |Description +|=== + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-router.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-router.adoc new file mode 100644 index 000000000..f60924691 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-router.adoc @@ -0,0 +1,320 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-router] +== Router Service Reference + +The OpenIDM router service provides the uniform interface to all objects in OpenIDM: managed objects, system objects, configuration objects, and so on. + +[#router-configuration] +=== Configuration + +The router object as shown in `conf/router.json` defines an array of filter objects. + +[source, javascript] +---- +{ + "filters": [ filter object, ... ] +} +---- +The required filters array defines a list of filters to be processed on each router request. Filters are processed in the order in which they are specified in this array. + +[#filter-object] +==== Filter Objects + +Filter objects are defined as follows. + +[source, javascript] +---- +{ + "pattern": string, + "methods": [ string, ... ], + "condition": script object, + "onRequest": script object, + "onResponse": script object, + "onFailure": script object +} +---- +-- + +"pattern":: +string, optional + ++ +Specifies a regular expression pattern matching the JSON pointer of the object to trigger scripts. If not specified, all identifiers (including `null`) match. Pattern matching is done on the resource name, rather than on individual objects. + +"methods":: +array of strings, optional + ++ +One or more methods for which the script(s) should be triggered. Supported methods are: `"create"`, `"read"`, `"update"`, `"delete"`, `"patch"`, `"query"`, `"action"`. If not specified, all methods are matched. + +"condition":: +script object, optional + ++ +Specifies a script that is called first to determine if the script should be triggered. If the condition yields `"true"`, the other script(s) are executed. If no condition is specified, the script(s) are called unconditionally. + +"onRequest":: +script object, optional + ++ +Specifies a script to execute before the request is dispatched to the resource. If the script throws an exception, the method is not performed, and a client error response is provided. + +"onResponse":: +script object, optional + ++ +Specifies a script to execute after the request is successfully dispatched to the resource and a response is returned. Throwing an exception from this script does not undo the method already performed. + +"onFailure":: +script object, optional + ++ +Specifies a script to execute if the request resulted in an exception being thrown. Throwing an exception from this script does not undo the method already performed. + +-- + +[#script-pattern-match] +===== Pattern Matching in the router.json File + +Pattern matching can minimize overhead in the router service. For example, the default `router.json` file includes instances of the `pattern` filter object, which limits script requests to specified methods and endpoints. + +Based on the following code snippet, the router service would trigger the `policyFilter.js` script for `CREATE` and `UPDATE` calls to managed, system, and internal repository objects. + +[source, javascript] +---- +{ + "pattern" : "^(managed|system|repo/internal)($|(/.+))", + "onRequest" : { + "type" : "text/javascript", + "source" : "require('policyFilter').runFilter()" + }, + "methods" : [ + "create", + "update" + ] +}, +---- +Without the noted `pattern`, OpenIDM would apply the policy filter to additional objects such as the audit service, which may affect performance. + + + +[#script-sequence] +==== Script Execution Sequence + +All "onRequest" and "onResponse" scripts are executed in sequence. First, the "onRequest" scripts are executed from the top down, then the "onResponse" scripts are executed from the bottom up. + +[source, console] +---- +client -> filter 1 onRequest -> filter 2 onRequest -> resource +client <- filter 1 onResponse <- filter 2 onResponse <- resource +---- +The following sample `router.json` file shows the order in which the scripts would be executed: + +[source, javascript] +---- +{ + "filters" : [ + { + "onRequest" : { + "type" : "text/javascript", + "file" : "script/router-authz.js" + } + }, + { + "pattern" : "^managed/user", + "methods" : [ + "read" + ], + "onRequest" : { + "type" : "text/javascript", + "source" : "console.log('requestFilter 1');" + } + }, + { + "pattern" : "^managed/user", + "methods" : [ + "read" + ], + "onResponse" : { + "type" : "text/javascript", + "source" : "console.log('responseFilter 1');" + } + }, + { + "pattern" : "^managed/user", + "methods" : [ + "read" + ], + "onRequest" : { + "type" : "text/javascript", + "source" : "console.log('requestFilter 2');" + } + }, + { + "pattern" : "^managed/user", + "methods" : [ + "read" + ], + "onResponse" : { + "type" : "text/javascript", + "source" : "console.log('responseFilter 2');" + } + } + ] +} +---- +Will produce a log like: + +[source, console] +---- +requestFilter 1 +requestFilter 2 +responseFilter 2 +responseFilter 1 +---- + + +[#filter-script-scope] +==== Script Scope + +Scripts are provided with the following scope. + +[source, javascript] +---- +{ + "openidm": openidm-functions object, + "request": resource-request object, + "response": resource-response object, + "exception": exception object +} +---- +-- + +"openidm":: +openidm-functions object (see xref:appendix-scripting.adoc#function-ref["Function Reference"]). + ++ +Provides access to OpenIDM resources. + +"request":: +resource-request object + ++ +The resource-request context, which has one or more parent contexts. Provided in the scope of all scripts. For more information about the request context, see xref:#understanding-request-context["Understanding the Request Context Chain"]. + +"response":: +resource-response object + ++ +The response to the resource-request. Only provided in the scope of the `"onResponse"` script. + +"exception":: +exception object + ++ +The exception value that was thrown as a result of processing the request. Only provided in the scope of the `"onFailure"` script. + +-- +An exception object is defined as follows. + +[source, javascript] +---- +{ + "code": integer, + "reason": string, + "message": string, + "detail": string +} +---- +-- + +"code":: +integer + ++ +The numeric HTTP code of the exception. + +"reason":: +string + ++ +The short reason phrase of the exception. + +"message":: +string + ++ +A brief message describing the exception. + +"detail":: +(optional), string + ++ +A detailed description of the exception, in structured JSON format, suitable for programmatic evaluation. + +-- + + + +[#router-example] +=== Example + +The following example executes a script after a managed user object is created or updated. + +[source, javascript] +---- +{ + "filters": [ + { + "pattern": "^managed/user", + "methods": [ + "create", + "update" + ], + "onResponse": { + "type": "text/javascript", + "file": "scripts/afterUpdateUser.js" + } + } + ] +} +---- + + +[#understanding-request-context] +=== Understanding the Request Context Chain + +The context chain of any request is established as follows: + +. The request starts with a __root context__, associated with a specific context ID. + +. The root context is wrapped in the __security context__ that includes the authentication and authorization detail for the request. + +. The security context is further wrapped by the __HTTP context__, with the target URI. The HTTP context is associated with the normal parameters of the request, including a user agent, authorization token, and method. + +. The HTTP context is wrapped by one or more server/router context(s), with an endpoint URI. The request can have several layers of server and router contexts. + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-scripting.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-scripting.adoc new file mode 100644 index 000000000..d5e328ec9 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-scripting.adoc @@ -0,0 +1,1332 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-scripting] +== Scripting Reference + +This appendix lists the functions supported by the script engine, the locations in which scripts can be triggered, and the variables available to scripts. For more information about scripting in OpenIDM, see xref:chap-scripting.adoc#chap-scripting["Extending OpenIDM Functionality By Using Scripts"]. + +[#function-ref] +=== Function Reference + +Functions (access to managed objects, system objects, and configuration objects) within OpenIDM are accessible to scripts via the `openidm` object, which is included in the top-level scope provided to each script. + +The following sections describe the OpenIDM functions supported by the script engine. + +[#function-create] +==== openidm.create(resourceName, newResourceId, content, params, fields) + +This function creates a new resource object. +.Parameters +-- + +resourceName:: +string + ++ +The container in which the object will be created, for example, `managed/user` or `system/ldap/account`. + +newResourceId:: +string + ++ +The identifier of the object to be created, if the client is supplying the ID. If the server should generate the ID, pass null here. + +content:: +JSON object + ++ +The content of the object to be created. + +params:: +JSON object (optional) + ++ +Additional parameters that are passed to the create request. + +fields:: +JSON array (optional) + ++ +An array of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. If no fields are specified, the entire new object is returned. + +-- +.Returns +-- + +:: +The created OpenIDM resource object. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be created. + +-- +.Example +-- + +:: + +[source, javascript] +---- +openidm.create("managed/user", bjensen, JSON object); +---- + +-- + + +[#function-patch] +==== openidm.patch(resourceName, rev, value, params, fields) + +This function performs a partial modification of a managed or system object. Unlike the `update` function, only the modified attributes are provided, not the entire object. +.Parameters +-- + +resourceName:: +string + ++ +The full path to the object being updated, including the ID. + +rev:: +string + ++ +The revision of the object to be updated. Use `null` if the object is not subject to revision control, or if you want to skip the revision check and update the object, regardless of the revision. + +value:: +JSON object + ++ +The value of the modifications to be applied to the object. The patch set includes the operation type, the field to be changed, and the new values. A PATCH request can `add`, `remove`, `replace`, or `increment` an attribute value. A `replace` operation replaces an existing value, or adds a value if no value exists. + +params:: +JSON object (optional) + ++ +Additional parameters that are passed to the patch request. + +fields:: +JSON array (optional) + ++ +An array of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. If no fields are specified, the entire new object is returned. + +-- +.Returns +-- + +:: +The modified OpenIDM resource object. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be updated. + +-- +.Examples +-- + +:: +Patching an object to add a value to an array: ++ + +[source, javascript] +---- +openidm.patch("managed/role/" + role._id, null, + [{"operation":"add", "field":"/members/-", "value":[ {"_ref":"managed/user/" + user._id} ]}]); +---- ++ +Patching an object to remove an existing property: ++ + +[source, javascript] +---- +openidm.patch("managed/user/" + user._id, null, + [{"operation":"remove", "field":"marital_status", "value":"single"}]); +---- ++ +Patching an object to replace a field value: ++ + +[source, javascript] +---- +openidm.patch("managed/user/" + user._id, null, + [{"operation":"replace", "field":"/password", "value":"Passw0rd"}]); +---- ++ +Patching an object to increment an integer value: ++ + +[source, javascript] +---- +openidm.patch("managed/user/" + user._id, null, + [{"operation":"increment","field":"/age","value":1}]); +---- + +-- + + +[#function-read] +==== openidm.read(resourceName, params, fields) + +This function reads and returns an OpenIDM resource object. +.Parameters +-- + +resourceName:: +string + ++ +The full path to the object to be read, including the ID. + +params:: +JSON object (optional) + ++ +The parameters that are passed to the read request. Generally, no additional parameters are passed to a read request, but this might differ, depending on the request. If you need to specify a list of `fields` as a third parameter, and you have no additional `params` to pass, you must pass `null` here. Otherwise, you simply omit both parameters. + +fields:: +JSON array (optional) + ++ +An array of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. If no fields are specified, the entire object is returned. + +-- +.Returns +-- + +:: +The OpenIDM resource object, or `null` if not found. + +-- +.Example +-- + +:: + +[source, javascript] +---- +openidm.read("managed/user/"+userId, null, ["*", "manager"]) +---- + +-- + + +[#function-update] +==== openidm.update(resourceName, rev, value, params, fields) + +This function updates an entire resource object. +.Parameters +-- + +id:: +string + ++ +The complete path to the object to be updated, including its ID. + +rev:: +string + ++ +The revision of the object to be updated. Use `null` if the object is not subject to revision control, or if you want to skip the revision check and update the object, regardless of the revision. + +value:: +object + ++ +The complete replacement object. + +params:: +JSON object (optional) + ++ +The parameters that are passed to the update request. + +fields:: +JSON array (optional) + ++ +An array of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. If no fields are specified, the entire object is returned. + +-- +.Returns +-- + +:: +The modified OpenIDM resource object. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be updated. + +-- +.Example +-- + +:: +In this example, the managed user entry is read (with an `openidm.read`, the user entry that has been read is updated with a new description, and the entire updated object is replaced with the new value. ++ + +[source, javascript] +---- +var user_read = openidm.read('managed/user/' + source._id); +user_read['description'] = 'The entry has been updated'; +openidm.update('managed/user/' + source._id, null, user_read); +---- + +-- + + +[#function-delete] +==== openidm.delete(resourceName, rev, params, fields) + +This function deletes a resource object. +.Parameters +-- + +resourceName:: +string + ++ +The complete path to the to be deleted, including its ID. + +rev:: +string + ++ +The revision of the object to be deleted. Use `null` if the object is not subject to revision control, or if you want to skip the revision check and delete the object, regardless of the revision. + +params:: +JSON object (optional) + ++ +The parameters that are passed to the delete request. + +fields:: +JSON array (optional) + ++ +An array of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. If no fields are specified, the entire object is returned. + +-- +.Returns +-- + +:: +Returns the deleted object if successful. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be deleted. + +-- +.Example +-- + +:: + +[source, javascript] +---- +openidm.delete('managed/user/'+ user._id, user._rev) +---- + +-- + + +[#function-query] +==== openidm.query(resourceName, params, fields) + +This function performs a query on the specified OpenIDM resource object. For more information, see xref:chap-data.adoc#constructing-queries["Constructing Queries"]. +.Parameters +-- + +resourceName:: +string + ++ +The resource object on which the query should be performed, for example, `"managed/user"`, or `"system/ldap/account"`. + +params:: +JSON object + ++ +The parameters that are passed to the query, `_queryFilter`, `_queryId`, or `_queryExpression`. Additional parameters passed to the query will differ, depending on the query. + ++ +Certain common parameters can be passed to the query to restrict the query results. The following sample query passes paging parameters and sort keys to the query. ++ + +[source] +---- +reconAudit = openidm.query("audit/recon", { + "_queryFilter": queryFilter, + "_pageSize": limit, + "_pagedResultsOffset": offset, + "_pagedResultsCookie": string, + "_sortKeys": "-timestamp" +}); +---- ++ +For more information about `_queryFilter` syntax, see xref:chap-data.adoc#query-filters["Common Filter Expressions"]. For more information about paging, see xref:chap-data.adoc#paging-query-results["Paging and Counting Query Results"]. + +fields:: +list + ++ +A list of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. The following example returns only the `userName` and `_id` fields: ++ + +[source, javascript] +---- +openidm.query("managed/user", { "_queryFilter": "/userName sw \"user.1\""}, ["userName", "_id"]) +---- ++ +This parameter is particularly useful in enabling you to return the response from a query without including intermediary code to massage it into the right format. + ++ +Fields are specified as JSON pointers. + +-- +.Returns +-- + +:: +The result of the query. A query result includes the following parameters: ++ +[open] +==== + +"query-time-ms":: +The time, in milliseconds, that OpenIDM took to process the query. + +"conversion-time-ms":: +(For an OrientDB repository only) the time, in milliseconds, taken to convert the data to a JSON object. + +"result":: +The list of entries retrieved by the query. The result includes the revision (`"_rev"`) of the entry and any other properties that were requested in the query. + +==== ++ +The following example shows the result of a custom query that requests the ID, user name, and email address of managed users in the repository. For an OrientDB repository, the query would be something like `select _openidm_id, userName, email from managed_user,`. ++ + +[source, javascript] +---- +{ + "conversion-time-ms": 0, + "result": [ + { + "email": "bjensen@example.com", + "userName": "bjensen", + "_rev": "0", + "_id": "36bbb745-517f-4695-93d0-998e1e7065cf" + }, + { + "email": "scarter@example.com", + "userName": "scarter", + "_rev": "0", + "_id": "cc3bf6f0-949e-4699-9b8e-8c78ce04a287" + } + ], + "query-time-ms": 1 +} +---- + +-- +.Throws +-- + +:: +An exception is thrown if the given query could not be processed. + +-- +.Examples +-- + +:: +The following sample query uses a `_queryFilter` to query the managed user repository. ++ + +[source] +---- +openidm.query("managed/user", + {'_queryFilter': userIdPropertyName + ' eq "' + security.authenticationId + '"'}); +---- ++ +The following sample query references the `for-userName` query, defined in the repository configuration, to query the managed user repository. ++ + +[source] +---- +openidm.query("managed/user", + {"_queryId": "for-userName", "uid": request.additionalParameters.uid } ); +---- + +-- + + +[#function-action] +==== openidm.action(resource, actionName, content, params, fields) + +This function performs an action on the specified OpenIDM resource object. The `resource` and `actionName` are required. All other parameters are optional. +.Parameters +-- + +resource:: +string + ++ +The resource that the function acts upon, for example, `managed/user`. + +actionName:: +string + ++ +The action to execute. Actions are used to represent functionality that is not covered by the standard methods for a resource (create, read, update, delete, patch, or query). In general, you should not use the `openidm.action` function for create, read, update, patch, delete or query operations. Instead, use the corresponding function specific to the operation (for example, `openidm.create`). + ++ +Using the operation-specific functions enables you to benefit from the well-defined REST API, which follows the same pattern as all other standard resources in the system. Using the REST API enhances usability for your own API and enforces the established patterns described in xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + ++ +OpenIDM-defined resources support a fixed set of actions. For user-defined resources (scriptable endpoints) you can implement whatever actions you require. ++ +[open] +==== +The following list outlines the supported actions, for each OpenIDM-defined resource. The actions listed here are also supported over the REST interface, and are described in detail in xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + +Actions supported on managed resources (`managed/*`):: +patch, triggerSyncCheck + +Actions supported on system resources (`system/*`):: +availableConnectors, createCoreConfig, createFullConfig, test, testConfig, liveSync, authenticate, script + ++ +For example: ++ + +[source, javascript] +---- +openidm.action("system/ldap/account", "authenticate", {}, +{"userName" : "bjensen", "password" : "Passw0rd"}); +---- + +Actions supported on the repository (`repo`):: +command, updateDbCredentials + ++ +For example: ++ + +[source, javascript] +---- +var r, command = { + "commandId": "purge-by-recon-number-of", + "numberOf": numOfRecons, + "includeMapping" : includeMapping, + "excludeMapping" : excludeMapping +}; +r = openidm.action("repo/audit/recon", "command", {}, command); +---- + +Actions supported on the synchronization resource (`sync`):: +performAction, + ++ +For example: ++ + +[source, javascript] +---- +openidm.action('sync', 'performAction', content, params) +---- + +Actions supported on the reconciliation resource (`recon`):: +recon, cancel + ++ +For example: ++ + +[source, javascript] +---- +openidm.action("recon", "cancel", content, params); +---- + +Actions supported on the script resource (`script`):: +eval + ++ +For example: ++ + +[source, javascript] +---- +openidm.action("script", "eval", getConfig(scriptConfig), {}); +---- + +Actions supported on the policy resource (`policy`):: +validateObject, validateProperty + ++ +For example: ++ + +[source, javascript] +---- +openidm.action("policy/" + fullResourcePath, "validateObject", request.content, { "external" : "true" }); +---- + +Actions supported on the workflow resource (`workflow/*`):: +claim + ++ +For example: ++ + +[source, javascript] +---- +var params = { +"userId":"manager1" +}; +openidm.action('workflow/processinstance/15', {"_action" : "claim"}, params); +---- + +Actions supported on the task scanner resource (`taskscanner`):: +execute, cancel + +Actions supported on the external email resource (`external/email`):: +sendEmail + ++ +For example: ++ + +[source, javascript] +---- +{ + emailParams = { + "from" : 'admin@example.com', + "to" : user.mail, + "subject" : 'Password expiry notification', + "type" : 'text/plain', + "body" : 'Your password will expire soon. Please change it!' + } + openidm.action("external/email", 'sendEmail', emailParams); +} +---- + +==== + +content:: +object (optional) + ++ +Content given to the action for processing. + +params:: +object (optional) + ++ +Additional parameters passed to the script. The `params` object must be a set of simple key:value pairs, and cannot include complex values. The parameters must map directly to URL variables, which take the form `name1=val1&name2=val2&...`. + +fields:: +JSON array (optional) + ++ +An array of the fields that should be returned in the result. The list of fields can include wild cards, such as `*` or `*_ref`. If no fields are specified, the entire object is returned. + +-- +.Returns +-- + +:: +The result of the action may be `null`. + +-- +.Throws +-- + +:: +If the action cannot be executed, an exception is thrown. + +-- + + +[#function-encrypt] +==== openidm.encrypt(value, cipher, alias) + +This function encrypts a value. +.Parameters +-- + +value:: +any + ++ +The value to be encrypted. + +cipher:: +string + ++ +The cipher with which to encrypt the value, using the form "algorithm/mode/padding" or just "algorithm". Example: `AES/ECB/PKCS5Padding`. + +alias:: +string + ++ +The key alias in the keystore with which to encrypt the node. + +-- +.Returns +-- + +:: +The value, encrypted with the specified cipher and key. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be encrypted for any reason. + +-- + + +[#function-decrypt] +==== openidm.decrypt(value) + +This function decrypts a value. +.Parameters +-- + +value:: +object + ++ +The value to be decrypted. + +-- +.Returns +-- + +:: +A deep copy of the value, with any encrypted value decrypted. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be decrypted for any reason. An error is thrown if the value is passed in as a string - it must be passed in an object. + +-- + + +[#function-isencrypted] +==== openidm.isEncrypted(object) + +This function determines if a value is encrypted. +.Parameters +-- + +object to check:: +any + ++ +The object whose value should be checked to determine if it is encrypted. + +-- +.Returns +-- + +:: +Boolean, `true` if the value is encrypted, and `false` if it is not encrypted. + +-- +.Throws +-- + +:: +An exception is thrown if the server is unable to detect whether the value is encrypted, for any reason. + +-- + + +[#function-hash] +==== openidm.hash(value, algorithm) + +This function calculates a value using a salted hash algorithm. +.Parameters +-- + +value:: +any + ++ +The value to be hashed. + +algorithm:: +string (optional) + ++ +The algorithm with which to hash the value. Example: `SHA-512`. If no algorithm is provided, a `null` value must be passed, and the algorithm defaults to SHA-256. + +-- +.Returns +-- + +:: +The value, calculated with the specified hash algorithm. + +-- +.Throws +-- + +:: +An exception is thrown if the object could not be hashed for any reason. + +-- + + +[#function-ishashed] +==== openidm.isHashed(value) + +This function detects whether a value has been calculated with a salted hash algorithm. +.Parameters +-- + +value:: +any + ++ +The value to be reviewed. + +-- +.Returns +-- + +:: +Boolean, `true` if the value is hashed, and `false` otherwise. + +-- +.Throws +-- + +:: +An exception is thrown if the server is unable to detect whether the value is hashed, for any reason. + +-- + + +[#function-matches] +==== openidm.matches(string, value) + +This function detects whether a string, when hashed, matches an existing hashed value. +.Parameters +-- + +string:: +any + ++ +A string to be hashed. + +value:: +any + ++ +A hashed value to compare to the string. + +-- +.Returns +-- + +:: +Boolean, `true` if the hash of the string matches the hashed value, and `false` otherwise. + +-- +.Throws +-- + +:: +An exception is thrown if the string could not be hashed. + +-- + + +[#logger-functions] +==== Logging Functions + +OpenIDM also provides a `logger` object to access the Simple Logging Facade for Java (SLF4J) facilities. The following code shows an example of the `logger` object. + +[source, javascript] +---- +logger.info("Parameters passed in: {} {} {}", param1, param2, param3); +---- +To set the log level for JavaScript scripts, add the following properties to your project's `conf/logging.properties` file: + +[source] +---- +org.forgerock.openidm.script.javascript.JavaScript.level +---- + +[source] +---- +org.forgerock.script.javascript.JavaScript.level +---- +The level can be one of `SEVERE` (highest value), `WARNING, INFO, CONFIG, FINE, FINER`, or `FINEST` (lowest value). For example: + +[source, javascript] +---- +org.forgerock.openidm.script.javascript.JavaScript.level=WARNING +org.forgerock.script.javascript.JavaScript.level=WARNING +---- +In addition, JavaScript has a useful logging function named `console.log()`. This function provides an easy way to dump data to the OpenIDM standard output (usually the same output as the OSGi console). The function works well with the JavaScript built-in function `JSON.stringify` and provides fine-grained details about any given object. For example, the following line will print a formatted JSON structure that represents the HTTP request details to STDOUT. + +[source, javascript] +---- +console.log(JSON.stringify(context.http, null, 4)); +---- + +[NOTE] +==== +These logging functions apply only to JavaScript scripts. To use the logging functions in Groovy scripts, the following lines must be added to the Groovy scripts: + +[source] +---- +import org.slf4j.*; +logger = LoggerFactory.getLogger('logger'); +---- +==== +The following sections describe the logging functions available to the script engine. + +[#function-logger-debug] +===== logger.debug(string message, object... params) + +Logs a message at DEBUG level. +.Parameters +-- + +message:: +string + ++ +The message format to log. Params replace `{}` in your message. + +params:: +object + ++ +Arguments to include in the message. + +-- +.Returns +-- + +:: +A `null` value if successful. + +-- +.Throws +-- + +:: +An exception is thrown if the message could not be logged. + +-- + + +[#function-logger-error] +===== logger.error(string message, object... params) + +Logs a message at ERROR level. +.Parameters +-- + +message:: +string + ++ +The message format to log. Params replace `{}` in your message. + +params:: +object + ++ +Arguments to include in the message. + +-- +.Returns +-- + +:: +A `null` value if successful. + +-- +.Throws +-- + +:: +An exception is thrown if the message could not be logged. + +-- + + +[#function-logger-info] +===== logger.info(string message, object... params) + +Logs a message at INFO level. +.Parameters +-- + +message:: +string + ++ +The message format to log. Params replace `{}` in your message. + +params:: +object + ++ +Arguments to include in the message. + +-- +.Returns +-- + +:: +A `null` value if successful. + +-- +.Throws +-- + +:: +An exception is thrown if the message could not be logged. + +-- + + +[#function-logger-trace] +===== logger.trace(string message, object... params) + +Logs a message at TRACE level. +.Parameters +-- + +message:: +string + ++ +The message format to log. Params replace `{}` in your message. + +params:: +object + ++ +Arguments to include in the message. + +-- +.Returns +-- + +:: +A `null` value if successful. + +-- +.Throws +-- + +:: +An exception is thrown if the message could not be logged. + +-- + + +[#function-logger-warn] +===== logger.warn(string message, object... params) + +Logs a message at WARN level. +.Parameters +-- + +message:: +string + ++ +The message format to log. Params replace `{}` in your message. + +params:: +object + ++ +Arguments to include in the message. + +-- +.Returns +-- + +:: +A `null` value if successful. + +-- +.Throws +-- + +:: +An exception is thrown if the message could not be logged. + +-- + + + + +[#script-places] +=== Places to Trigger Scripts + +Scripts can be triggered in different places, and by different events. The following list indicates the configuration files in which scripts can be referenced, the events upon which the scripts can be triggered and the actual scripts that can be triggered on each of these files. +-- + +Scripts called in the mapping (`conf/sync.json`) file:: +[open] +==== + +Triggered by situation:: +onCreate, onUpdate, onDelete, onLink, onUnlink + +Object filter:: +validSource, validTarget + +Triggered when correlating objects:: +correlationQuery, correlationScript + +Triggered on any reconciliation:: +result + +Scripts inside properties:: +condition, transform + ++ +`sync.json` supports only one script per hook. If multiple scripts are defined for the same hook, only the last one is kept. + +==== + +Scripts called in the managed object configuration (`conf/managed.json`) file:: +onCreate, onRead, onUpdate, onDelete, onValidate, onRetrieve, onStore, onSync, postCreate, postUpdate, and postDelete + ++ +`managed.json` supports only one script per hook. If multiple scripts are defined for the same hook, only the last one is kept. + +Scripts called in the router configuration (`conf/router.json`) file:: +onRequest, onResponse, onFailure + ++ +`router.json` supports multiple scripts per hook. + +-- + + +[#script-variables] +=== Variables Available to Scripts + +The standard variables, `context`, `resourceName` and `request` are available to all scripts. Additional variables available to a script depend on the following items: + +* The trigger that launches the script + +* The configuration file in which that trigger is defined + +* The object type. For a managed object (defined in `managed.json`), the object type is either a managed object configuration object, or a managed object property. For a synchronization object (defined in `sync.json`), the object can be an object-mapping object (see xref:appendix-synchronization.adoc#sync-object-mapping["Object-Mapping Objects"]), a property object (see xref:appendix-synchronization.adoc#sync-property-objects["Property Objects"]), or a policy object (see xref:appendix-synchronization.adoc#sync-policy-objects["Policy Objects"]). + +The following tables list the available variables, based on each of these items. + +[#script-triggers-managed_json] +.Script Triggers Defined in managed.json +[cols="25%,25%,50%"] +|=== +|Object Type |Trigger |Variable + +.6+a|managed object config object +a|onCreate, postCreate +a|object, newObject + +a|onUpdate, postUpdate +a|object, oldObject, newObject + +a|onDelete, onRetrieve, onRead +a|object + +a|postDelete +a|oldObject + +a|onSync +a|request, oldObject, newObject, success (boolean) + + action (string) + + syncDetails - an array of maps, each detailing the mappings that were attempted to be synchronized + + syncResults - a map that includes all the syncDetails in one place + +a|onStore, onValidate +a|object, value (the content to be stored or validated for the object) + +.2+a|property object +a|onRetrieve, onStore +a|object, property, propertyName + +a|onValidate +a|property +|=== + +[#script-triggers-sync_json] +.Script Triggers Defined in sync.json +[cols="25%,25%,50%"] +|=== +|Object Type |Trigger |Variable + +.8+a|object-mapping object +a|correlationQuery, correlationScript +a|source, linkQualifier + +a|linkQualifiers +a|mapping - the name of the current mapping + + object - the value of the source object. During a DELETE event, that source object may not exist, and may be null. + + oldValue - The former value of the deleted source object, if any. If the source object is new, oldValue will be null. When there are deleted objects, oldValue is populated only if the source is a managed object. + + returnAll (boolean) - you must configure the script to return every valid link qualifier when returnAll is true, independent of the source object. So you might want your script first to check the value of returnAll. If returnAll is true, the script must not attempt to use the object variable, because it will be null. + +a|onCreate +a|source, target, situation, linkQualifier, context, sourceId, targetId, mappingConfig - a configuration object representing the mapping being processed + +a|onDelete, onUpdate +a|source, target, oldTarget, situation, linkQualifier, context, sourceId, targetId, mappingConfig - a configuration object representing the mapping being processed + +a|onLink, onUnlink +a|source, target, linkQualifier, context, sourceId, targetId, mappingConfig - a configuration object representing the mapping being processed + +a|result +a|source, target, global, with reconciliation results + +a|validSource +a|source, linkQualifier + +a|validTarget +a|target, linkQualifier + +.2+a|property object +a|condition +a|object, linkQualifier, target, oldTarget, oldSource - available during UPDATE and DELETE operations performed through implicit sync. With implicit synchronization, the synchronization operation is triggered by a specific change to the source object. As such, implicit sync can populate the old value within the `oldSource` variable and pass it on to the sync engine. + + During reconciliation operations `oldSource` will be undefined. A reconciliation operation cannot populate the value of the `oldSource` variable as it has no awareness of the specific change to the source object. Reconciliation simply synchronizes the static source object to the target. + +a|transform +a|source, linkQualifier + +.2+a|policy object +a|action +a|source, target, recon, sourceAction - a boolean that indicates whether the action is being processed during the source or target synchronization phase The `recon.actionParam` object contains information about the current reconciliation operation and includes the following variables: + +* `reconId` +* `mapping`+ +`systemLdapAccounts_managedUser` +* `situation` +* `action` +* `sourceId`+ +`_id` +* `linkQualifier`+ +`default` +* `ambiguousTargetIds` +* `_action`+ +`performAction` + +a|postAction +a|source, target, action, actionParam, sourceAction, linkQualifier, reconId, situation +|=== + +[#script-triggers-router_json] +.Script Triggers Defined in router.json +[cols="50%,50%"] +|=== +|Trigger |Variable + +a|onFailure +a|exception + +a|onRequest +a|request + +a|onResponse +a|response +|=== +Custom endpoint scripts always have access to the `request` and `context` variables. +-- +OpenIDM includes one additional variable used in scripts: + +identityServer:: +The `identityServer` variable can be used in several ways. The `ScriptRegistryService` described in xref:chap-scripting.adoc#script-endpoint["Validating Scripts Over REST"] binds this variable to: + +* `getProperty` ++ +Retrieves property information from configuration files. Creates a new identity environment configuration. ++ +For example, you can retrieve the value of the `openidm.config.crypto.alias` property from that file with the following code: `alias = identityServer.getProperty("openidm.config.crypto.alias", "true", true);` + +* `getInstallLocation` ++ +Retrieves the installation path for OpenIDM, such as `/path/to/openidm`. May be superseded by an absolute path. + +* `getProjectLocation` ++ +Retrieves the directory used when you started OpenIDM. That directory includes configuration and script files for your project. ++ +For more information on the project location, see xref:chap-services.adoc#startup-configuration["Specifying the OpenIDM Startup Configuration"]. + +* `getWorkingLocation` ++ +Retrieves the directory associated with database cache and audit logs. You can find `db/` and `audit/` subdirectories there. ++ +For more information on the working location, see xref:chap-services.adoc#startup-configuration["Specifying the OpenIDM Startup Configuration"]. + + +-- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-synchronization.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-synchronization.adoc new file mode 100644 index 000000000..25cac0922 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-synchronization.adoc @@ -0,0 +1,537 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[appendix] +[#appendix-synchronization] +== Synchronization Reference + +The synchronization engine is one of the core services of OpenIDM. You configure the synchronization service through a `mappings` property that specifies mappings between objects that are managed by the synchronization engine. + +[source, javascript] +---- +{ + "mappings": [ object-mapping object, ... ] +} +---- + +[#sync-object-mapping] +=== Object-Mapping Objects + +An object-mapping object specifies the configuration for a mapping of source objects to target objects. + +[source, javascript] +---- +{ + "name" : string, + "source" : string, + "target" : string, + "links" : string, + "enableSync" : boolean, + "validSource" : script object, + "validTarget" : script object, + "sourceCondition" : script object or queryFilter string, + "correlationQuery" : script object, + "correlationScript": script object, + "linkQualifier" : script object, + "properties" : [ property object, ... ], + "policies" : [ policy object, ... ], + "onCreate" : script object, + "onUpdate" : script object, + "onDelete" : script object, + "onLink" : script object, + "onUnlink" : script object, + "result" : script object +} +---- +[#mapping-object-properties] +.Mapping Object Properties +-- + +name:: +string, required + ++ +Uniquely names the object mapping. Used in the link object identifier. + +source:: +string, required + ++ +Specifies the path of the source object set. Example: `"managed/user"`. + +target:: +string, required + ++ +Specifies the path of the target object set. Example: `"system/ldap/account"`. + +links:: +string, optional + ++ +Enables reuse of the links created in another mapping. Example: `"systemLdapAccounts_managedUser"` reuses the links created by a previous mapping whose `name` is `"systemLdapAccounts_managedUser"`. + +enableSync:: +boolean, true or false + ++ +Specifies whether automatic synchronization (liveSync and implicit synchronization) should be enabled for a specific mapping. For more information, see xref:chap-synchronization.adoc#disabling-automatic-sync["Disabling Automatic Synchronization Operations"]. + ++ +Default : `true` + +validSource:: +script object, optional + ++ +A script that determines if a source object is valid to be mapped. The script yields a boolean value: `true` indicates the source object is valid; `false` can be used to defer mapping until some condition is met. In the root scope, the source object is provided in the `"source"` property. If the script is not specified, then all source objects are considered valid. + +validTarget:: +script object, optional + ++ +A script used during the target phase of reconciliation that determines if a target object is valid to be mapped. The script yields a boolean value: `true` indicates that the target object is valid; `false` indicates that the target object should not be included in reconciliation. In the root scope, the target object is provided in the `"target"` property. If the script is not specified, then all target objects are considered valid for mapping. + +sourceCondition:: +script object or `queryFilter` string, optional + ++ +A script or query filter that determines if a source object should be included in the mapping. If no `sourceCondition` element (or `validSource` script) is specified, all source objects are included in the mapping. + +correlationQuery:: +script object, optional + ++ +A script that yields a query object to query the target object set when a source object has no linked target. The syntax for writing the query depends on the target system of the correlation. For examples of correlation queries, see xref:chap-synchronization.adoc#correlation["Correlating Source Objects With Existing Target Objects"]. The source object is provided in the `"source"` property in the script scope. + +correlationScript:: +script object, optional + ++ +A script that goes beyond a `correlationQuery` of a target system. Used when you need another method to determine which records in the target system relate to the given source record. The syntax depends on the target of the correlation. For information about defining correlation scripts, see xref:chap-synchronization.adoc#correlation-scripts["Writing Correlation Scripts"]. + +properties:: +array of property-mapping objects, optional + ++ +Specifies mappings between source object properties and target object properties, with optional transformation scripts. + +policies:: +array of policy objects, optional + ++ +Specifies a set of link conditions and associated actions to take in response. + +onCreate:: +script object, optional + ++ +A script to execute when a target object is to be created, after property mappings have been applied. In the root scope, the source object is provided in the `"source"` property, the projected target object in the `"target"` property, and the link situation that led to the create operation in the `"situation"` property. Properties on the target object can be modified by the script. If a property value is not set by the script, OpenIDM falls back on the default property mapping configuration. If the script throws an exception, the target object creation is aborted. + +onUpdate:: +script object, optional + ++ +A script to execute when a target object is to be updated, after property mappings have been applied. In the root scope, the source object is provided in the `"source"` property, the projected target object in the `"target"` property, and the link situation that led to the update operation in the `"situation"` property. Any changes that the script makes to the target object will be persisted when the object is finally saved to the target resource. If the script throws an exception, the target object update is aborted. + +onDelete:: +script object, optional + ++ +A script to execute when a target object is to be deleted, after property mappings have been applied. In the root scope, the source object is provided in the `"source"` property, the target object in the `"target"` property, and the link situation that led to the delete operation in the `"situation"` property. If the script throws an exception, the target object deletion is aborted. + +onLink:: +script object, optional + ++ +A script to execute when a source object is to be linked to a target object, after property mappings have been applied. In the root scope, the source object is provided in the `"source"` property, and the projected target object in the `"target"` property. + ++ +Note that, although an `onLink` script has access to a copy of the target object, changes made to that copy will not be saved to the target system automatically. If you want to persist changes made to target objects by an `onLink` script, you must explicitly include a call to the action that should be taken on the target object (for example `openidm.create`, `openidm.update` or `openidm.delete`) within the script. + ++ +In the following example, when an LDAP target object is linked, the `"description"` attribute of that object is updated with the value `"Active Account"`. A call to `openidm.update` is made within the `onLink` script, to set the value. ++ + +[source, javascript] +---- +"onLink" : { + "type" : "text/javascript", + "source" : "target.description = 'Active Account'; + openidm.update('system/ldap/account/' + target._id, null, target);" +} +---- ++ +If the script throws an exception, target object linking is aborted. + +onUnlink:: +script object, optional + ++ +A script to execute when a source and a target object are to be unlinked, after property mappings have been applied. In the root scope, the source object is provided in the `"source"` property, and the target object in the `"target"` property. + ++ +Note that, although an `onUnlink` script has access to a copy of the target object, changes made to that copy will not be saved to the target system automatically. If you want to persist changes made to target objects by an `onUnlink` script, you must explicitly include a call to the action that should be taken on the target object (for example `openidm.create`, `openidm.update` or `openidm.delete`) within the script. + ++ +In the following example, when an LDAP target object is unlinked, the `"description"` attribute of that object is updated with the value `"Inactive Account"`. A call to `openidm.update` is made within the `onUnlink` script, to set the value. ++ + +[source, javascript] +---- +"onUnlink" : { + "type" : "text/javascript", + "source" : "target.description = 'Inactive Account'; + openidm.update('system/ldap/account/' + target._id, null, target);" +} +---- ++ +If the script throws an exception, target object unlinking is aborted. + +result:: +script object, optional + ++ +A script for each mapping event, executed only after a successful reconciliation. ++ +The variables available to a `result` script are as follows: + +* `source` - provides statistics about the source phase of the reconciliation + +* `target` - provides statistics about the target phase of the reconciliation + +* `global` - provides statistics about the entire reconciliation operation + + +-- + +[#sync-property-objects] +==== Property Objects + +A property object specifies how the value of a target property is determined. + +[source, javascript] +---- +{ + "target" : string, + "source" : string, + "transform" : script object, + "condition" : script object, + "default": value +} +---- +[#sync-property-object-properties] +.Property Object Properties +-- + +target:: +string, required + ++ +Specifies the path of the property in the target object to map to. + +source:: +string, optional + ++ +Specifies the path of the property in the source object to map from. If not specified, then the target property value is derived from the script or default value. + +transform:: +script object, optional + ++ +A script to determine the target property value. The root scope contains the value of the source in the `"source"` property, if specified. If the `"source"` property has a value of `""`, then the entire source object of the mapping is contained in the root scope. The resulting value yielded by the script is stored in the target property. + +condition:: +script object, optional + ++ +A script to determine whether the mapping should be executed or not. The condition has an `"object"` property available in root scope, which (if specified) contains the full source object. For example `"source": "(object.email != null)"`. The script is considered to return a boolean value. + +default:: +any value, optional + ++ +Specifies the value to assign to the target property if a non-null value is not established by `"source"` or `"transform"`. If not specified, the default value is `null`. + +-- + + +[#sync-policy-objects] +==== Policy Objects + +A policy object specifies a link condition and the associated actions to take in response. + +[source, javascript] +---- +{ + "situation" : string, + "action" : string or script object + "postAction" : optional, script object +} +---- +[#sync-policy-object-properties] +.Policy Object Properties +-- + +situation:: +string, required + ++ +Specifies the situation for which an associated action is to be defined. + +action:: +string or script object, required + ++ +Specifies the action to perform. If a script is specified, the script is executed and is expected to yield a string containing the action to perform. + +postAction:: +script object, optional + ++ +Specifies the action to perform after the previously specified action has completed. + ++ +The `postAction` script has the following variables available in its scope: `source`, `target`, `action`, `sourceAction`, `linkQualifier`, and `reconID`. `sourceAction` is `true` if the action was performed during the source reconciliation phase, and `false` if the action was performed during the target reconciliation phase. For more information, see xref:chap-synchronization.adoc#sync-situations["Synchronization Situations"]. ++ + +[NOTE] +====== +No `postAction` script is triggered if the `action` is either `IGNORE` or `ASYNC`. +====== + +-- + +[#sync-script-objects] +===== Script Object + +Script objects take the following form. + +[source, javascript] +---- +{ + "type" : "text/javascript", + "source": string +} +---- +-- + +type:: +string, required + ++ +Specifies the type of script to be executed. Supported types include `"text/javascript"` and `"groovy"`. + +source:: +string, required + ++ +Specifies the source code of the script to be executed. + +-- + + + + +[#sync-links] +=== Links + +To maintain links between source and target objects in mappings, OpenIDM stores an object set in the repository. The object set identifier follows this scheme. + +[source] +---- +links/mapping +---- +Here, __mapping__ represents the name of the mapping for which links are managed. + +Link entries have the following structure. + +[source, javascript] +---- +{ + "_id":string, + "_rev":string, + "linkType":string, + "firstId":string + "secondId":string, +} +---- +-- + +_id:: +string + ++ +The identifier of the link object. + +_rev:: +string, required + ++ +The value of link object's revision. + +linkType:: +string, required + ++ +The type of the link. Usually then name of the mapping which created the link. + +firstId:: +string, required + ++ +The identifier of the first of the two linked objects. + +secondId:: +string + ++ +The identifier of the second of the two linked objects. + +-- + + +[#sync-queries] +=== Queries + +OpenIDM performs the following queries on a link object set. + +. Find link(s) for a given firstId object identifier. ++ + +[source] +---- +SELECT * FROM links WHERE linkType + = value AND firstId = value +---- ++ +Although a single result makes sense, this query is intended to allow multiple results so that this scenario can be handled as an exception. + +. Select link(s) for a given second object identifier. ++ + +[source] +---- +SELECT * FROM links WHERE linkType + = value AND secondId = value +---- ++ +Although a single result makes sense, this query is intended to allow multiple results so that this scenario can be handled as an exception. + + + +[#sync-reconciliation] +=== Reconciliation + +OpenIDM performs reconciliation on a per-mapping basis. The process of reconciliation for a given mapping includes these stages. + +. Iterate through all objects for the object set specified as `"source"`. For each source object, carry out the following steps. ++ + +.. Look for a link to a target object in the link object set, and perform a correlation query (if defined). + +.. Determine the link condition, as well as whether a target object can be found. + +.. Determine the action to perform based on the policy defined for the condition. + +.. Perform the action. + +.. Keep track of the target objects for which a condition and action has already been determined. + +.. Write the results. + + +. Iterate through all object identifiers for the object set specified as `"target"`. For each identifier, carry out the following steps. ++ + +.. Find the target in the link object set. ++ +Determine if the target object was handled in the first phase. + +.. Determine the action to perform based on the policy defined for the condition. + +.. Perform the action. + +.. Write the results. + + +. Iterate through all link objects, carrying out the following steps. ++ + +.. If the `reconId` is `"my"`, then skip the object. ++ +If the `reconId` is not recognized, then the source or the target is missing. + +.. Determine the action to perform based on the policy. + +.. Perform the action. + +.. Store the `reconId` identifer in the mapping to indicate that it was processed in this run. + + + +[NOTE] +==== +To optimize a reconciliation operation, the reconciliation process does not attempt to correlate source objects to target objects if the set of target objects is empty when the correlation is started. For information on changing this default behaviour, see xref:chap-synchronization.adoc#reconciliation-optimization["Optimizing Reconciliation Performance"]. +==== + + +[#sync-rest-api] +=== REST API + +-- +External synchronized objects expose an API to request immediate synchronization. This API includes the following requests and responses. + +Request:: +Example: ++ + +[source, httprequest] +---- +POST /openidm/system/xml/account/jsmith?_action=liveSync HTTP/1.1 +---- + +Response (success):: +Example: ++ + +[source, httprequest] +---- +HTTP/1.1 204 No Content +... +---- + +Response (synchronization failure):: +Example: ++ + +[source, httprequest] +---- +HTTP/1.1 409 Conflict +... +[JSON representation of error] +---- + +-- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-advanced.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-advanced.adoc new file mode 100644 index 000000000..800d44cee --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-advanced.adoc @@ -0,0 +1,87 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-advanced] +== Advanced Configuration + +OpenIDM is a highly customizable, extensible identity management system. For the most part, the customization and configuration required for a "typical" deployment is described earlier in this book. This chapter describes advanced configuration methods that would usually not be required in a deployment, but that might assist in situations that require a high level of customization. + +[#adv-startup-configuration] +=== Advanced Startup Configuration + +A customizable startup configuration file (named `launcher.json`) enables you to specify how the OSGi Framework is started. You specify the startup configuration file with the `-c` option of the `startup` command. + +Unless you are working with a highly customized deployment, you should not modify the default framework configuration. + +If no configuration file is specified, the default configuration (defined in `/path/to/openidm/bin/launcher.json`) is used. The following command starts OpenIDM with an alternative startup configuration file: + +[source, console] +---- +$ ./startup.sh -c /Users/admin/openidm/bin/launcher.json +---- +You can modify the default startup configuration file to specify a different startup configuration. + +The customizable properties of the default startup configuration file are as follows: + +* `"location" : "bundle"` - resolves to the install location. You can also load OpenIDM from a specified zip file (`"location" : "openidm.zip"`) or you can install a single jar file (`"location" : "openidm-system-2.2.jar"`). + +* `"includes" : "**/openidm-system-*.jar"` - the specified folder is scanned for jar files relating to the system startup. If the value of `"includes"` is `*.jar`, you must specifically exclude any jars in the bundle that you do not want to install, by setting the `"excludes"` property. + +* `"start-level" : 1` - specifies a start level for the jar files identified previously. + +* `"action" : "install.start"` - a period-separated list of actions to be taken on the jar files. Values can be one or more of `"install.start.update.uninstall"`. + +* `"config.properties"` - takes either a path to a configuration file (relative to the project location) or a list of configuration properties and their values. The list must be in the format `"string":"string"`, for example: ++ + +[source, javascript] +---- +"config.properties" : + { + "property" : "value" + }, +---- + +* `"system.properties"` - takes either a path to a `system.properties` file (relative to the project location) or a list of system properties and their values. The list must be in the format `"string":"string"`, for example: ++ + +[source, javascript] +---- +"system.properties" : + { + "property" : "value" + }, +---- + +* `"boot.properties"` - takes either a path to a `boot.properties` file (relative to the project location) or a list of boot properties and their values.The list must be in the format `"string":object`, for example: ++ + +[source, javascript] +---- +"boot.properties" : + { + "property" : true + }, +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-auditing.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-auditing.adoc new file mode 100644 index 000000000..a96c12f30 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-auditing.adoc @@ -0,0 +1,1980 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-auditing] +== Using Audit Logs + +The OpenIDM auditing service can publish and log all relevant system activity to one or more specified targets, including local data files, the OpenIDM repository, and remote systems. + +OpenIDM audit logs can help you record activity by account. With audit data, you can monitor logins, identify problems such as unresponsive devices, and collect information to comply with regulatory requirements. + +OpenIDM logs data from six audit events: access details, system activity, authentication operations, configuration changes, reconciliations, and synchronizations. Auditing provides the data for all relevant reports, including those related to orphan accounts. + +OpenIDM 4.5 supports customization of data from all six audit events. + +Regardless of where audit information is logged, you may query audit logs over the REST interface. For more information, see xref:#querying-audit-over-rest["Querying Audit Logs Over REST"]. + +[#configure-audit-service] +=== Configuring the Audit Service + +OpenIDM exposes the audit logging configuration under `\https://localhost:8443/openidm/config/audit` for the REST API, and in the file `project-dir/conf/audit.json`. + +You can also configure the audit service in the Admin UI. Select Configure > System Preferences and click on the Audit tab. The fields on that form correspond to the configuration parameters described in this section. + +You can also configure the audit service by editing corresponding parameters in the `audit.json` file. +The following list includes major options that you can configure for the audit service. + +* OpenIDM includes several configurable default __audit event handlers__, as described in xref:#configuring-topic-handlers["Configuring Audit Event Handlers"]. + +* You can allow a common `transactionId` for audit data from all ForgeRock products. To do so, edit the `system.properties` file in your `project-dir/conf` directory and set: ++ + +[source, console] +---- +org.forgerock.http.TrustTransactionHeader=true +---- + +To configure the audit service to log an event, you should include it in the list of `events` for the __Audit Event Handler__ used for queries (see xref:#configuring-topic-handlers["Configuring Audit Event Handlers"]). + +You can select one audit event handler to manage queries on the audit logs. The audit query handler can be any one of the event handlers described in the previous section. The default audit query handler is the OpenIDM repository. + +To specify which audit event handler should be used for queries, set the `handlerForQueries` property in the `audit.json` file, as follows: + +[source, javascript] +---- +{ + "auditServiceConfig" : { + "handlerForQueries" : "repo", + "availableAuditEventHandlers" : [ + "org.forgerock.audit.events.handlers.csv.CSVAuditEventHandler", + "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "org.forgerock.openidm.audit.impl.RouterAuditEventHandler" +---- +In this case, the `handlerForQueries` is set to `repo`, which is the `name` of the `RepositoryAuditEventHandler`, which is also shown later in the file. + +The `availableAuditEventHandlers` property provides the array of audit event handlers available to OpenIDM. For more information, see xref:#configuring-topic-handlers["Configuring Audit Event Handlers"]. + +[IMPORTANT] +==== + +* Do not use a file-based audit event handler, such as CSV or JSON, to handle queries __in a clustered environment__. Rather use the repo audit event handler or an external database for queries, in conjunction with your file-based audit handler. ++ +In a clustered environment, file-based audit logs are really useful only for offline review and parsing with external tools. ++ +You can use a file-based audit handler for queries in a non-clustered demonstration or evaluation environment. However, be aware that these handlers do not implement paging, and are therefore subject to general query performance limitations. + +* The JMS, Syslog, and Splunk handlers can __not__ be used as the handler for queries. + +* Logging via CSV or JSON may lead to errors in one or more mappings in the Admin UI. + +==== + + +[#configuring-topic-handlers] +=== Configuring Audit Event Handlers + +An audit event handler manages audit events, sends audit output to a defined location, and controls their format. OpenIDM supports several default audit event handlers, plus audit event handlers for third-party log management tools, as described in xref:appendix-audit.adoc#appendix-audit["Additional Audit Details"]. + +Each audit event handler may also include additional `config` sub-properties, as noted in the tables shown in xref:appendix-audit.adoc#section-audit-event-config["Audit Event Handler Configuration"]. + +The following section illustrate how you can configure these properties for standard OpenIDM audit event handlers. For additional audit event handlers, see xref:appendix-audit.adoc#appendix-audit["Additional Audit Details"]. + +[#audit-csv-handler] +==== CSV Audit Event Handler + +The CSV audit event handler logs events to a comma-separated value (CSV) file. The following code is an excerpt of the `audit.json` file, which depicts a sample CSV audit event handler configuration: + +[source, javascript] +---- +"eventHandlers" : [ +{ + "class" : "org.forgerock.audit.events.handlers.csv.CSVAuditEventHandler", + "config" : { + "name" : "csv", + "logDirectory" : "&{launcher.working.location}/audit", + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ] + } +} +---- +The `"logDirectory"` property indicates the name of the directory in which log files should be written, relative to the __working location__. For more information on the working location, see xref:chap-services.adoc#startup-configuration["Specifying the OpenIDM Startup Configuration"]. + +You can use property value substitution to direct logs to another location on the file system. The example provided in xref:chap-configuration.adoc#custom-audit-log-location["Custom Audit Log Location"] shows how to direct audit logs to a user home directory. + +If you set up a custom CSV Audit Event Handler, you may configure over 20 different properties, as described in xref:appendix-audit.adoc#audit-event-prop["Common Audit Event Handler Property Configuration"]. + +Audit file names are fixed and correspond to the event being audited: +[none] +* `access.csv` +* `activity.csv` +* `authentication.csv` +* `config.csv` +* `recon.csv` +* `sync.csv` + +[#audit-csv-min] +===== Minimum Admin UI CSV Audit Handler Configuration Requirements + +If you configure the CSV Audit Event Handler in the Admin UI, you should at minimum, configure the following: + +* The `logDirectory`, the full path to the directory with audit logs, such as `/path/to/openidm/audit`. You can substitute &{launcher.install.location} for `/path/to/openidm`. + +* Differing entries for the quote character, `quoteChar` and delimiter character, `delimiterChar`. + +* If you enable the CSV tamper-evident configuration, you should include the `keystoreHandlerName`, __or__ a `filename` and `password`. Do not include all three options. ++ +Before including tamper-evident features in the audit configuration, set up the keys as described in xref:#tamper-evident-operation["How CSV Files Become Tamper-Evident"]. + + +[NOTE] +==== +The `signatureInterval` property supports time settings in a human-readable format (default = 1 hour). Examples of allowable `signatureInterval` settings are: + +* 3 days, 4 m + +* 1 hour, 3 sec + +Allowable time units include: + +* days, day, d + +* hours, hour, h + +* minutes, minute, min, m + +* seconds, second, sec, s + +==== + + +[#tamper-evident-operation] +===== How CSV Files Become Tamper-Evident + +The integrity of audit files may be important to some deployers. The `CSVAuditEventHandler` supports both plain and tamper-evident CSV files. + +OpenIDM already has a Java Cryptography Extension Keystore (JCEKS), `keystore.jceks`, in the `/path/to/openidm/security` directory. + +You'll need to initialize a key pair using the RSA encryption algorithm, using the SHA256 hashing mechanism. + +[source, console] +---- +$ cd /path/to/openidm +$ keytool \ + -genkeypair \ + -alias "Signature" \ + -dname CN=openidm \ + -keystore security/keystore.jceks \ + -storepass changeit \ + -storetype JCEKS \ + -keypass changeit \ + -keyalg RSA \ + -sigalg SHA256withRSA +---- +You can now set up a secret key, in Hash-based message authentication code, using the SHA256 hash function (HmacSHA256) + +[source, console] +---- +$ keytool \ + -genseckey \ + -alias "Password" \ + -keystore security/keystore.jceks \ + -storepass changeit \ + -storetype JCEKS \ + -keypass changeit \ + -keyalg HmacSHA256 \ + -keysize 256 +---- +To verify your new entries, run the following command: + +[source, console] +---- +$ keytool \ + -list \ + -keystore security/keystore.jceks \ + -storepass changeit \ + -storetype JCEKS + Keystore type: JCEKS +Keystore provider: SunJCE + +Your keystore contains 5 entries + +signature, May 10, 2016, PrivateKeyEntry, +Certificate fingerprint (SHA1): 62:2E:E4:36:74:F1:7F:E9:06:08:8D:77:82:1C:F6:D4:05:D1:20:01 +openidm-sym-default, May 10, 2016, SecretKeyEntry, +password, May 10, 2016, SecretKeyEntry, +openidm-selfservice-key, May 10, 2016, SecretKeyEntry, +openidm-localhost, May 10, 2016, PrivateKeyEntry, +Certificate fingerprint (SHA1): 31:D2:33:93:E3:63:E8:06:66:CC:C1:4F:7F:DF:0A:F8:C4:D8:0E:BD +---- + + +[#audit-csv-tamper] +===== Configuring Tamper Protection for CSV Audit Logs + +Tamper protection for OpenIDM audit files can ensure the integrity of OpenIDM audit logs written to CSV files. You can activate them in the `audit.json` file directly, or by editing the CSV Audit Event Handler through the Admin UI. + +Once configured, the relevant code snippet in your `project-dir/conf/audit.conf` file should appear as follows: + +[source, javascript] +---- +{ + "class" : "org.forgerock.audit.handlers.csv.CsvAuditEventHandler", + "config" : { + ... + "security" : { + "enabled" : true, + "filename" : "", + "password" : "", + "keyStoreHandlerName" : "openidm", + "signatureInterval" : "10 minutes" + }, + ... +---- +This particular code snippet reflects a tamper-evident configuration where a signature is written to a new line in each CSV file, every 10 minutes. That signature uses the default OpenIDM keystore, configured in the `project-dir//conf/boot/boot.properties` file. The properties are described in xref:appendix-audit.adoc#audit-event-prop["Common Audit Event Handler Property Configuration"]. + +To import a certificate into the OpenIDM keystore, or create your own self-signed certificate, read xref:#tamper-evident-operation["How CSV Files Become Tamper-Evident"]. + +To make these same changes in the Admin UI, log into `\https://localhost:8443/admin`, and click Configure > System Preferences > Audit. You can either edit an existing CSV audit event handler, or create one of your own, with the options just described. + +image::images/ui-tamper.png[] +Before saving these tamper-evident changes to your audit configuration, move or delete any current audit CSV files with commands such as: + +[source, console] +---- +$ cd /path/to/openidm +$ mv audit/*.csv /tmp +---- +Once you've saved tamper-evident configuration changes, you should see the following files in the `/path/to/openidm/audit` directory: + +[source, console] +---- +tamper-evident-access.csv +tamper-evident-access.csv.keystore +tamper-evident-activity.csv +tamper-evident-activity.csv.keystore +tamper-evident-authentication.csv +tamper-evident-authentication.csv.keystore +tamper-evident-config.csv +tamper-evident-config.csv.keystore +tamper-evident-recon.csv +tamper-evident-recon.csv.keystore +tamper-evident-sync.csv +tamper-evident-sync.csv.keystore +---- + + +[#tamper-evident-check] +===== Checking the Integrity of Audit Log Files + +Now that you've configured keystore and tamper-evident features, you can periodically check the integrity of your log files. + +For example, the following command can verify the CSV files in the `--archive` subdirectory (`audit/`), which belong to the access `--topic`, verified with the `keystore.jceks` keystore, using the OpenIDM CSV audit handler bundle, `forgerock-audit-handler-csv-version.jar`: + +[source, console] +---- +$ java -jar \ +bundle/forgerock-audit-handler-csv-version.jar \ +--archive audit/ \ +--topic access \ +--keystore security/keystore.jceks \ +--password changeit +---- +If there are changes to your `tamper-evident-access.csv` file, you'll see a message similar to: + +[source, console] +---- +FAIL tamper-evident-access.csv-2016.05.10-11.05.43 The HMac at row 3 is not correct. +---- + + + +[#audit-router-handler] +==== Router Audit Event Handler + +The router audit event handler logs events to any external or custom endpoint, such as `system/scriptedsql` or `custom-endpoint/myhandler`. + +A sample configuration for a `"router"` event handler is provided in the `audit.json` file in the `openidm/samples/audit-sample/conf` directory, and described in xref:../samples-guide/chap-audit-sample.adoc#audit-config-files["Audit Sample Configuration Files"] in the __Samples Guide__. This sample directs log output to a JDBC repository. The audit configuration file (`conf/audit.json`) for the sample shows the following event handler configuration: + +[source, javascript] +---- +{ + "class": "org.forgerock.openidm.audit.impl.RouterAuditEventHandler", + "config": { + "name": "router", + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ], + "resourcePath" : "system/auditdb" + } +}, +---- +The `"resourcePath"` property in the configuration indicates that logs should be directed to the `system/auditdb` endpoint. This endpoint, and the JDBC connection properties, are defined in the connector configuration file (`conf/provisioner.openicf-scriptedsql.json`), as follows: + +[source, javascript] +---- +{ + "name" : "auditdb", +... + "configurationProperties" : { + "username" : "root", + "password" : "password", + "driverClassName" : "com.mysql.jdbc.Driver", + "url" : "jdbc:mysql://localhost:3306/audit", + "autoCommit" : true, + "reloadScriptOnExecution" : false, + "jdbcDriver" : "com.mysql.jdbc.Driver", + "scriptRoots" : ["&{launcher.project.location}/tools"], + "createScriptFileName" : "CreateScript.groovy", + "testScriptFileName" : "TestScript.groovy", + "searchScriptFileName" : "SearchScript.groovy" + }, +... +---- +Substitute the correct URL or IP address of your remote JDBC repository, and the corresponding connection details. + + +[#audit-repo-handler] +==== Repository Audit Event Handler + +The repository audit event handler sends information to the OpenIDM repository. The log entries vary by repository: + +* In the OrientDB repository, OpenIDM stores log entries in the following tables: ++ + +. `audit_access` + +. `audit_activity` + +. `audit_authentication` + +. `audit_config` + +. `audit_recon` + +. `audit_sync` + + +* In a JDBC repository, OpenIDM stores log entries in the following tables: ++ + +. `auditaccess` + +. `auditactivity` + +. `auditauthentication` + +. `auditconfig` + +. `auditrecon` + +. `auditsync` + + +You can use the repository audit event handler to generate reports that combine information from multiple tables. + +You can find mappings for each of these JDBC tables in your `repo.jdbc.json` file. The following excerpt illustrates the mappings for the `auditauthentication` table: + +[source, javascript] +---- +"audit/authentication" : { + "table" : "auditauthentication", + "objectToColumn" : { + "_id" : "objectid", + "transactionId" : "transactionid", + "timestamp" : "activitydate", + "userId" : "userid", + "eventName" : "eventname", + "result" : "result", + "principal" : {"column" : "principals", "type" : "JSON_LIST"}, + "context" : {"column" : "context", "type" : "JSON_MAP"}, + "entries" : {"column" : "entries", "type" : "JSON_LIST"}, + "trackingIds" : {"column" : "trackingids", "type" : "JSON_LIST"}, + } +}, +---- +Now return to the `audit.json` file. Examine the following sample audit repository log configuration: + +[source, javascript] +---- +{ + "class": "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "config": { + "name": "repo", + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ] + } +}, +---- + + +[#audit-jms-handler] +==== JMS Audit Event Handler + +Starting with OpenIDM 4.5.0, you can configure a Java Message Service (JMS) Audit Event Handler. The Java Message Service (JMS) is a Java API for sending messages between clients. A JMS audit event handler can record messages between a JMS message broker and one or more clients. The default ForgeRock JMS message broker is link:http://activemq.apache.org/[Apache ActiveMQ, window=\_blank]. For a demonstration, see xref:../samples-guide/chap-audit-sample.adoc#jms-audit-sample["Show Audit Events Published on a JMS Topic"] in the __Samples Guide__. + +Alternatively, you can use the link:https://tap.tibco.com/storefront/trialware/tibco-enterprise-message-service/prod15032.html[TIBCO Enterprise Message Service, window=\_blank], as described in this chapter. + +The JMS API architecture includes a __JMS provider__, the messaging system, along with __JMS clients__, the Java programs and components that consume messages. This implementation supports the link:http://docs.oracle.com/javaee/6/tutorial/doc/bncdx.html#bnced[Publish/Subscribe Messaging Domain., window=\_blank] + +As with other audit event handlers, you can configure it directly through the `conf/audit.json` file for your project or through the Admin UI. + +[TIP] +==== +The JMS audit event handler does not support queries. If you enable JMS, you must also enable a second handler that supports queries. You'll see that handler in the `audit.json` file with the `handlerForQueries` property, or in the Admin UI with the `Use For Queries` option. +==== +The ForgeRock JMS audit event handler supports JMS communication, based on the following components: + +* A JMS message broker, which provides clients with connectivity, along with message storage and message delivery functionality. + +* JMS messages, which follow a specific format described in xref:#audit-jms-message["JMS Message Format"]. + +* Destinations, maintained by the message broker, such as the ForgeRock audit service. They may be batched in queues, and can be acknowledged in one of three modes: automatically, by the client, or with direction to accept duplication. The acknowledgement mode is based on the JMS session. + +* Topics: JMS topics differ from ForgeRock audit event topics. The ForgeRock implementation of JMS topics uses the link:http://docs.oracle.com/javaee/6/tutorial/doc/bncdx.html#bnced[publish/subscribe messaging domain, window=\_blank], which can direct messages to the JMS audit event handler. In contrast, ForgeRock audit event topics specify categories of events, including access, activity, authentication, configuration, reconciliation, and synchronization. + +* JMS clients include both the producer and consumer of a JMS message. + +Depending on the configuration, you can expect some or all of these components to be included in JMS audit log messages. + +In the following sections, you can configure the JMS audit event handler in the Admin UI, and through your project's `audit.json` file. For detailed configuration options, see xref:appendix-audit.adoc#audit-config-prop-jms["JMS Audit Event Handler Unique config Properties"]. But first, you should add several bundles to your OpenIDM deployment. + +[#section-jms-bundles] +===== Adding Required Bundles for the JMS Audit Event Handler + +To test this sample, you'll download a total of five JAR files. The first four are OSGi Bundles: + +* link:https://repository.apache.org/content/repositories/releases/org/apache/activemq/activemq-client/[ActiveMQ Client, window=\_top] + +* The link:http://bnd.bndtools.org/[bnd, window=\_blank] JAR for working with OSGi bundles, which you can download from link:https://repo1.maven.org/maven2/biz/aQute/bnd/1.50.0/bnd-1.50.0.jar[bnd-1.50.0.jar, window=\_top]. + +* The Apache Geronimo J2EE management bundle, `geronimo-j2ee-management_1.1_spec-1.0.1.jar`, which you can download from link:https://repo1.maven.org/maven2/org/apache/geronimo/specs/geronimo-j2ee-management_1.1_spec/1.0.1/[https://repo1.maven.org/maven2/org/apache/geronimo/specs/geronimo-j2ee-management_1.1_spec/1.0.1/, window=\_top]. + +* The link:https://github.com/chirino/hawtbuf[hawtbuf, window=\_blank] Maven-based protocol buffer compiler JAR, which you can download from link:https://repo1.maven.org/maven2/org/fusesource/hawtbuf/hawtbuf/1.11/[hawtbuf-1.11.jar, window=\_top]. + +* The ActiveMQ 5.13.2 binary, which you can download from link:http://activemq.apache.org/activemq-5132-release.html[http://activemq.apache.org/activemq-5132-release.html, window=\_top]. + + +[NOTE] +==== +The JMS audit event handler has been tested and documented with the noted versions of the JAR files that you've just downloaded. +==== +Make sure at least the first two JAR files, for __the Active MQ Client__ and __bnd__, are in the same directory. Navigate to that directory, and create an OSGi bundle with the following steps: + +==== + +. Create a BND file named `activemq.bnd` with the following contents: ++ + +[source, console] +---- +version=5.13.2 +Export-Package: *;version=${version} +Bundle-Name: ActiveMQ :: Client +Bundle-SymbolicName: org.apache.activemq +Bundle-Version: ${version} +---- + +. Run the following command to create the OSGi bundle archive file: ++ + +[source, console] +---- +$ java \ +-jar \ +bnd-1.50.0.jar \ +wrap \ +-properties \ +activemq.bnd \ +activemq-client-5.13.2.jar +---- + +. Rename the `activemq-client-5.13.2.bar` file that appears to `activemq-client-5.13.2-osgi.jar` and copy it to the `/path/to/openidm/bundle` directory. + +==== +Copy the other two bundle files, __Apache Geronimo__ and __hawtbuf__, to the `/path/to/openidm/bundle` directory. + + +[#audit-jms-ui] +===== Configuring JMS at the Admin UI + +To configure JMS at the Admin UI, select Configure > System Preferences > Audit. Under Event Handlers, select `JmsAuditEventHandler` and select `Add Event Handler`. You can then configure the JMS audit event handler in the pop-up window that appears. For guidance, see xref:#audit-jms-conf["JMS Configuration File"]. + + +[#audit-jms-conf] +===== JMS Configuration File + +You can configure JMS directly in the `conf/audit.json` file, or indirectly through the Admin UI. The following code is an excerpt of the audit.json file, which depicts a sample JMS audit event handler configuration: + +[source, javascript] +---- +{ + "class" : "org.forgerock.audit.handlers.jms.JmsAuditEventHandler", + "config" : { + "name": "jms", + "enabled" : true, + "topics": [ "access", "activity", "config", "authentication", "sync", "recon" ], + "deliveryMode": "NON_PERSISTENT", + "sessionMode": "AUTO", + "batch": { + "batchEnabled": true, + "capacity": 1000, + "threadCount": 3, + "maxBatchedEvents": 100 + }, + "jndi": { + "contextProperties": { + "java.naming.factory.initial" : "org.apache.activemq.jndi.ActiveMQInitialContextFactory", + "java.naming.provider.url" : "tcp://127.0.0.1:61616?daemon=true", + "topic.audit" : "audit" + }, + "topicName": "audit", + "connectionFactoryName": "ConnectionFactory" + } + } +} +---- +As you can see from the properties, in this configuration, the JMS audit event handler is `enabled`, with `NON_PERSISTENT` delivery of audit events in batches. It is configured to use the Apache ActiveMQ Java Naming and Directory Interface (JNDI) message broker, configured on port 61616. For an example of how to configure Apache ActiveMQ, see xref:../samples-guide/chap-audit-sample.adoc#jms-audit-sample["Show Audit Events Published on a JMS Topic"] in the __Samples Guide__. + +If you substitute a different JNDI message broker, you'll have to change the `jndi` `contextProperties`. If you configure the JNDI message broker on a remote system, substitute the associated IP address. + +To set up SSL, change the value of the `java.naming.provider.url` to: + +[source, console] +---- +ssl://127.0.0.1:61617?daemon=true&socket.enabledCipherSuites= + SSL_RSA_WITH_RC4_128_SHA,SSL_DH_anon_WITH_3DES_EDE_CBC_SHA +---- +You'll also need to set up keystores and truststores, as described in xref:#audit-jms-activemq-ssl["JMS, ActiveMQ, and SSL"]. + + +[#audit-jms-activemq-ssl] +===== JMS, ActiveMQ, and SSL + +If the security of your audit data is important, you can configure SSL for JMS. To do so, you'll need to take the following steps to generate an ActiveMQ broker certificate keystore, a broker export certificate, a client keystore, and a server truststore. You can then import that client certificate into the OpenIDM security truststore. + +[NOTE] +==== +This section is based in part on the ActiveMQ documentation on link:http://activemq.apache.org/how-do-i-use-ssl.html[How do I use SSL, window=\_blank]. As of this writing, it includes the following caution: "In Linux, do not use absolute path to keystore". +==== +But first, you should export two environment variables: + +* Navigate to the directory where you unpacked the ActiveMQ binary: ++ + +[source, console] +---- +$ cd /path/to/apache-activemq-x.y.z +---- + +* *ACTIVEMQ_SSL_OPTS*. Set the `ACTIVEMQ_SSL_OPTS` variable to point to the ActiveMQ broker keystore: ++ + +[source, console] +---- +$ export \ +ACTIVEMQ_SSL_OPTS=\ +'-Djavax.net.ssl.keyStore=/usr/local/activemq/keystore/broker.ks -Djavax.net.ssl.keyStorePassword=changeit' +---- + +* *MAVEN_OPTS* Set the `MAVEN_OPTS` variable, for the sample consumer described in xref:../samples-guide/chap-audit-sample.adoc#jms-sample-consume["Configuring and Using a JMS Consumer Application"] in the __Samples Guide__: ++ + +[source, console] +---- +$ export \ +MAVEN_OPTS=\ +"-Djavax.net.ssl.keyStore=client.ks -Djavax.net.ssl.keyStorePassword=changeit +-Djavax.net.ssl.trustStore=client.ts -Djavax.net.ssl.trustStorePassword=changeit" +---- + +Note that these commands use the default keystore `changeit` password. The commands which follow assume that you use the same password when creating ActiveMQ certificates. + +* Create an ActiveMQ broker certificate (`broker.ks`): ++ + +[source, console] +---- +$ keytool \ +-genkey \ +-alias broker \ +-keyalg RSA \ +-keystore broker.ks +---- + +* Export the certificate to `broker_cert`, so you can share it with clients: ++ + +[source, console] +---- +$ keytool \ +-export \ +-alias broker \ +-keystore broker.ks \ +-file broker_cert +---- + +* Create a client keystore file (`client.ks`): ++ + +[source, console] +---- +$ keytool \ +-genkey \ +-alias client \ +-keyalg RSA \ +-keystore client.ks +---- + +* Create a client truststore file, `client.ts`, and import the broker certificate, `broker_cert`: ++ + +[source, console] +---- +$ keytool \ +-import \ +-alias broker \ +-keystore client.ts \ +-file broker_cert +---- + +* Export the client keystore, `client.ks`, into a client certificate file (`client.crt`): ++ + +[source, console] +---- +$ keytool \ +-export \ +-alias client \ +-keystore client.ks \ +--file client.crt +---- + +* Now make this work with OpenIDM. Import the client certificate file into the OpenIDM truststore: ++ + +[source, console] +---- +$ keytool \ +-import \ +-trustcacerts \ +-alias client \ +-file client.crt \ +-keystore /path/to/openidm/security/truststore +---- + +With these certificate files, you can now set up SSL in the ActiveMQ configuration file, `activemq.xml`, in the `/path/to/apache-activemq-x.y.z/conf` directory. + +You'll add one line to the `` code block with ` + + + + + + + + +---- +You can now make a corresponding change to the OpenIDM audit configuration file, `audit.json`, as described in xref:#audit-jms-conf["JMS Configuration File"]. + +You can now start the ActiveMQ event broker, and start OpenIDM, as described in xref:../samples-guide/chap-audit-sample.adoc#jms-sample-start["Starting the ActiveMQ Broker and OpenIDM"] in the __Samples Guide__. + + +[#audit-jms-message] +===== JMS Message Format + +The following JMS message reflects the authentication of the `openidm-admin` user, logging into the Admin UI from a remote location, IP address 172.16.209.49. + +[source, javascript] +---- +{ + "event": { + "_id": "134ee773-c081-436b-ae61-a41e8158c712-565", + "trackingIds": [ + "4dd1f9de-69ac-4721-b01e-666df388fb17", + "185b9120-406e-47fe-ba8f-e95fd5e0abd8" + ], + "context": { + "id": "openidm-admin", + "ipAddress": "172.16.209.49", + "roles": [ + "openidm-admin", + "openidm-authorized" + ], + "component": "repo/internal/user" + }, + "entries": [ + { + "info": { + "org.forgerock.authentication.principal": "openidm-admin" + }, + "result": "SUCCESSFUL", + "moduleId": "JwtSession" + } + ], + "principal": [ + "openidm-admin" + ], + "result": "SUCCESSFUL", + "userId": "openidm-admin", + "transactionId": "134ee773-c081-436b-ae61-a41e8158c712-562", + "timestamp": "2016-04-15T14:57:53.114Z", + "eventName": "authentication" + }, + "auditTopic": "authentication" +} +---- + + +[#audit-jms-tibco-ssl] +===== JMS, TIBCO, and SSL + +OpenIDM also supports integration between the link:http://www.tibco.com/products/automation/enterprise-messaging/enterprise-message-service[TIBCO Enterprise Message Service, window=\_blank] and the JMS audit event handler. + +You'll need to use two bundles from your TIBCO installation: `tibjms.jar`, and if you're setting up a secure connection, `tibcrypt.jar`. With the following procedure, you'll process `tibjms.jar` into an OSGi bundle: + +==== + +. Download the link:http://bnd.bndtools.org/[bnd, window=\_blank] JAR for working with OSGi bundles, from link:https://repo1.maven.org/maven2/biz/aQute/bnd/1.50.0/bnd-1.50.0.jar[bnd-1.50.0.jar, window=\_top]. If you've previously set up the ActiveMQ server, as described in xref:#section-jms-bundles["Adding Required Bundles for the JMS Audit Event Handler"], you may have already downloaded this JAR archive. + +. In the same directory, create a file named `tibco.bnd`, and add the following lines to that file: ++ + +[source] +---- +version=8.3.0 +Export-Package: *;version=${version} +Bundle-Name: TIBCO Enterprise Message Service +Bundle-SymbolicName: com/tibco/tibjms +Bundle-Version: ${version} +---- + +. Add the `tibco.jar` file to the same directory. + +. Run the following command to create the bundle: ++ + +[source, console] +---- +$ java \ + -jar bnd-1.50.0.jar wrap \ + -properties tibco.bnd tibjms.jar +---- + +. Rename the newly created `tibjms.bar` file to `tibjms-osgi.jar`, and copy it to the `/path/to/openidm/bundle` directory. + +. If you're configuring SSL, copy the `tibcrypt.jar` file from your TIBCO installation to the `/path/to/openidm/bundle` directory. + +==== +You also need to configure your project's `audit.conf` configuration file. The options are similar to those listed earlier in xref:#audit-jms-conf["JMS Configuration File"], except for the following `jndi` code block: + +[source, javascript] +---- +"jndi": { + "contextProperties": { + "java.naming.factory.initial" : "com.tibco.tibjms.naming.TibjmsInitialContextFactory", + "java.naming.provider.url" : "tibjmsnaming://localhost:7222" + }, + "topicName": "audit", + "connectionFactoryName": "ConnectionFactory" +} +---- +If your TIBCO server is on a remote system, substitute appropriately for `localhost`. If you're configuring a secure TIBCO installation, you'll want to configure a different code block: + +[source, javascript] +---- +"jndi": { + "contextProperties": { + "java.naming.factory.initial" : "com.tibco.tibjms.naming.TibjmsInitialContextFactory", + "java.naming.provider.url" : "ssl://localhost:7243", + "com.tibco.tibjms.naming.security_protocol" : "ssl", + "com.tibco.tibjms.naming.ssl_trusted_certs" : "/path/to/tibco/server/certificate/cert.pem", + "com.tibco.tibjms.naming.ssl_enable_verify_hostname" : "false" + }, + "topicName": "audit", + "connectionFactoryName": "SSLConnectionFactory" +} +---- +Do not add the TIBCO certificate to the OpenIDM `truststore` file. The formats are not compatible. + +Once this configuration work is complete, don't forget to start your TIBCO server before starting OpenIDM. For more information, see the following link:https://docs.tibco.com/pub/ems/8.3.0/doc/pdf/TIB_ems_8.3_users_guide.pdf[TIBCO Enterprise Message Service Users's Guide, window=\_blank]. + + + +[#review-audit-handlers] +==== Reviewing Active Audit Event Handlers + +To review the audit event handlers available for your OpenIDM deployment, along with each setting shown in the `audit.json` file, use the following command to POST a request for `availableHandlers`: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +"https://localhost:8443/openidm/audit?_action=availableHandlers" +---- +The output includes a full set of options for each audit event handler, which have been translated in the Admin UI. You can see "human-readable" details when you log into the Admin UI. Click Configure > System Preferences > Audit, and create or customize the event handler of your choice. + +Not all audit event handlers support queries. You'll see this in the REST call output as well as in the Admin UI. In the output for `availableHandlers`, you'll see: + +[source, console] +---- +"isUsableForQueries" : false +---- +In the Admin UI, when you configure the JMS audit event handler, you won't be able to enable the `Use For Queries` option. + + + +[#audit-log-topics] +=== Audit Log Event Topics + +The OpenIDM Audit Service logs information from six audit topics: access, activity, authentication, configuration, reconciliation, and synchronization. + +When you start OpenIDM, it creates audit log files for each topic in the `openidm/audit` directory. If you use the CSV audit event handler, and run a reconciliation on OpenIDM, it adds access, activity, authentication, and reconciliation information to relevant log files. + +This section describes all OpenIDM audit service topics, and shows how the OpenIDM audit configuration support additional audit topics. + +In the Admin UI, you can configure default and custom audit topics. Select Configure > System Preferences. Click on the Audit tab, and review the section on Event Topics. + +[#default-audit-topics] +==== OpenIDM Audit Event Topics + +-- +The OpenIDM Audit Service logs the following event topics by default: + +Access Event Topics:: +OpenIDM writes messages at __system boundaries__, that is REST endpoints and the invocation of scheduled tasks in this log. In short, it includes who, what, and output for every access request. + ++ +Default file: `openidm/audit/access.csv` + +Activity Event Topics:: +OpenIDM logs operations on internal (managed) and external (system) objects to this log. + ++ +Entries in the activity log contain identifiers, both for the action that triggered the activity, and also for the original caller and the relationships between related actions, on internal and external objects. + ++ +Default file: `openidm/audit/activity.csv` + +Authentication Event Topics:: +OpenIDM logs the results of authentication operations to this log, including situations and the actions taken on each object, including when and how a user authenticated and related events. The activity log contains additional detail about each authentication action. + ++ +Default file: `openidm/audit/authentication.csv` + +Configuration Event Topics:: +OpenIDM logs the changes in configuration options in this log. The configuration log includes the "before" and "after" settings for each configuration item, with timestamps. + ++ +Default file: `openidm/audit/config.csv` + +Reconciliation Event Topics:: +OpenIDM logs the results of a reconciliation run to this log (including situations and the resulting actions taken). The activity log contains details about the actions, where log entries display parent activity identifiers, `recon/reconID`, links, and policy events by datastore. + ++ +Default file: `openidm/audit/recon.csv` + +Synchronization Event Topics:: +OpenIDM logs the results of automatic synchronization operations (LiveSync and implicit synchronization) to this log, including situations and the actions taken on each object, by account. The activity log contains additional detail about each action. + ++ +Default file: `openidm/audit/sync.csv` + +-- +For detailed information about each audit event topic, see xref:appendix-audit.adoc#appendix-audit["Additional Audit Details"]. + + + +[#filtering-audit-events] +=== Event Topics: Filtering + +The audit configuration, defined in the `audit.json` file, includes a `filter` parameter that enables you to specify what should be logged, per event type. The information that is logged can be filtered in various ways. The following sections describe the filters that can be applied to each event type. + +You can edit these filtering fields in the Admin UI. Click Configure > System Preferences > Audit. Scroll down to Event Topics, and next to the event of your choice, click the pencil icon. You can edit the filtering fields of your choice, as shown in the following figure. + +image::images/audit-event-topic.png[] +If you do not see some of the options in the Admin UI, look for a drop-down arrow on the right side of the window. If your window looks like this figure, you will see the Password Fields tab in the drop-down menu. + +[#filtering-by-action] +==== Filter Actions: Filtering Audit Entries by Action + +The `filter` `actions` list enables you to specify the actions that are logged, per event type. This filter is essentially a `fields` filter (as described in xref:#filtering-by-field["Filter Fields: Filtering Audit Entries by Field"]) that filters log entries by the value of their `actions` field. + +The following configuration specifies certain action operations: (create, update, delete, patch, and action). The Audit Service may check filter actions, scripts, and more, when included in the `audit.json` file. + +[source] +---- +"eventTopics" : { +... + "activity": { + "filter" : { + "actions" : [ + "create", + "update", + "delete", + "patch", + "action" + ] + }, + "watchedFields" : [ ], + "passwordFields" : [ + "password" + ] + } +} +---- +The list of actions that can be filtered into the log depend on the event type. The following table lists the actions that can be filtered, per event type. + +[#d0e25133] +.Actions that can be Logged Per Event Type +[cols="16%,17%,67%"] +|=== +|Event Type |Actions |Description + +.7+a|Activity and Configuration +a|`read` +a|When an object is read by using its identifier. By default, read actions are not logged. Add the `"read"` action to the list of actions to log all read actions. + + Note that due to the potential result size in the case of read operations on `system/` endpoints, only the read is logged, and not the resource detail. If you really need to log the complete resource detail, add the following line to your `conf/boot/boot.properties` file: + +[source] +---- +openidm.audit.logFullObjects=true +---- + +a|`create` +a|When an object is created. + +a|`update` +a|When an object is updated. + +a|`delete` +a|When an object is deleted. + +a|`patch` +a|When an object is partially modified. (Activity only.) + +a|`query` +a|When a query is performed on an object. By default, query actions are not logged. Add the `"query"` action to the list of actions to log all query actions. + + Note that, due to the potential result size in the case of query operations on `system/` endpoints, only the query is logged, and not the resource detail. If you really need to log the complete resource detail, add the following line to your `conf/boot/boot.properties` file: + +[source] +---- +openidm.audit.logFullObjects=true +---- + +a|`action` +a|When an action is performed on an object. (Activity only.) + +.7+a|Reconciliation and Synchronization +a|`create` +a|When a target object is created. + +a|`delete` +a|When a target object is deleted. + +a|`update` +a|When a target object is updated. + +a|`link` +a|When a link is created between a source object and an existing target object. + +a|`unlink` +a|When a link is removed between a source object and a target object. + +a|`exception` +a|When the synchronization situation results in an exception. For more information, see xref:chap-synchronization.adoc#handling-sync["Synchronization Situations and Actions"]. + +a|`ignore` +a|When the target object is ignored, that is, no action is taken. + +a|Access +a|`-` +a|No actions can be specified for the access log. +|=== + + +[#filtering-by-field] +==== Filter Fields: Filtering Audit Entries by Field + +You can add a list of `filter` `fields` to the audit configuration, that enables you to filter log entries by specific fields. For example, you might want to restrict the reconciliation or audit log so that only summary information is logged for each reconciliation operation. The following addition to the `audit.json` file specifies that entries are logged in the reconciliation log only if their `entryType` is `start` or `summary`. + +[source, javascript] +---- +"eventTopics" : { + ... + "activity" : { + "filter" : { + "actions" : [ + "create", + "update", + "delete", + "patch", + "action + ], + "fields" : [ + { + "name" : "entryType", + "values" : [ + "start", + "summary" + ] + } + ] + } + } + ... +}, +... +---- +To use nested properties, specify the field name as a JSON pointer. For example, to filter entries according to the value of the `authentication.id`, you would specify the field name as `authentication/id`. + + +[#audit-filter-scripts] +==== Filter Script: Using a Script to Filter Audit Data + +Apart from the audit filtering options described in the previous sections, you can use a JavaScript or Groovy script to specify what is logged in your audit logs. Audit filter scripts are referenced in the audit configuration file (`conf/audit.json`), and can be configured per event type. The following sample configuration references a script named `auditfilter.js`, which is used to limit what is logged in the reconciliation audit log: + +[source, javascript] +---- +{ + "eventTopics" : { + ... + "recon" : { + "filter" : { + "script" : { + "type" : "text/javascript", + "file" : "auditfilter.js" + } + } + }, + ... +} +---- +OpenIDM makes the `request` and `context` objects available to the script. Before writing the audit entry, OpenIDM can access the entry as a `request.content` object. For example, to set up a script to log just the summary entries for mapping managed users in an LDAP data store, you could include the following in the `auditfilter.js` script: + +[source, javascript] +---- +(function() { + return request.content.entryType == 'summary' && + request.content.mapping == 'systemLdapAccounts_managedUser' +}()); +---- +The script must return `true` to include the log entry; `false` to exclude it. + + +[#filtering-by-trigger] +==== Filter Triggers: Filtering Audit Entries by Trigger + +You can add a `filter` `triggers` list to the audit configuration, that specifies the actions that will be logged for a specific trigger. For example, the following addition to the `audit.json` file specifies that only `create` and `update` actions are logged for in the activity log, for an activity that was triggered by a `recon`. + +[source, javascript] +---- +"eventTopics" : { + "activity" : { + "filter" : { + "actions" : [ + ... + ], + "triggers" : { + "recon" : [ + "create", + "update" + ] + } + ... +---- +If a trigger is provided, but no actions are specified, nothing is logged for that trigger. If a trigger is omitted, all actions are logged for that trigger. In the current OpenIDM release, only the `recon` trigger is implemented. For a list of reconciliation actions that can be logged, see xref:chap-synchronization.adoc#sync-actions["Synchronization Actions"]. + + +[#audit-watched-fields] +==== Watched Fields: Defining Fields to Monitor + +__For the activity log only__, you can specify fields whose values are considered particularly important in terms of logging. + +The `watchedFields` parameter, configured in the `audit.json` file, is not really a filtering mechanism, but enables you to define a list of properties that should be monitored for changes. When the value of one of the properties in this list changes, the change is logged in the activity log, under the column `"changedFields"`. This parameter enables you to have quick access to important changes in the log. + +Properties to monitor are listed as values of the `watchedFields` parameter, separated by commas, for example: + +[source, console] +---- +"watchedFields" : [ "email", "address" ] +---- +You can monitor changes to any field in this way. + + +[#audit-password-fields] +==== Password Fields: Defining a Password Field + +Also in the activity log, you can include a `passwordFields` parameter to specify a list of password properties. This parameter functions much like the `watchedFields` parameter in that changes to these property values are logged in the activity log, under the column `"changedFields"`. In addition, when a password property is changed, the boolean `"passwordChanged"` flag is set to `true` in the activity log. Properties that should be considered as passwords are listed as values of the `passwordFields` parameter, separated by commas. For example: + +[source, console] +---- +"passwordFields" : [ "password", "userPassword" ] +---- + + + +[#filtering-audit-policies] +=== Filtering Audit Logs by Policy + +By default, the `audit.json` file for OpenIDM includes the following code snippet for `filterPolicies`: + +[source, javascript] +---- +"filterPolicies" : { + "value" : { + "excludeIf" : [ + "/access/http/request/headers/Authorization", + "/access/http/request/headers/X-OpenIDM-Password", + "/access/http/request/cookies/session-jwt", + "/access/http/response/headers/Authorization", + "/access/http/response/headers/X-OpenIDM-Password" + ], + "includeIf" : [ ] + } +} +---- +The `excludeIf` code snippet lists HTTP access log data that the audit service excludes from log files. + +The `includeIf` directive is available for custom audit event handlers, for items that you want included in log files. + + +[#audit-exception-formatter] +=== Configuring an Audit Exception Formatter + +The OpenIDM Audit service includes an __exception formatter__, configured in the following snippet of the `audit.json` file: + +[source, javascript] +---- +"exceptionFormatter" : { + "type" : "text/javascript", + "file" : "bin/defaults/script/audit/stacktraceFormatter.js" +}, +---- +As shown, you may find the script that defines how the exception formatter works in the `stacktraceFormatter.js` file. That file handles the formatting and display of exceptions written to the audit logger. + + +[#audit-write-adjustments] +=== Adjusting Audit Write Behavior + +OpenIDM supports buffering to minimize the writes on your systems. To do so, you can configure buffering either in the `project-dir/conf/audit.json` file, or through the Admin UI. + +You can configure audit buffering through an event handler. To access an event handler in the Admin UI, click Configure > System Preferences and click on the Audit Tab. When you customize or create an event handler, you can configure the following settings: + +[#d0e25517] +.Audit Buffering Options +[cols="20%,40%,40%"] +|=== +|Property |UI Text |Description + +a|`enabled` +a|True or false +a|Enables / disables buffering + +a|`autoFlush` +a|True or false; whether the Audit Service automatically flushes events after writing them to disk +a| +|=== +The following sample code illustrates where you would configure these properties in the `audit.json` file. + +[source, javascript] +---- +... + "eventHandlers" : [ + { + "config" : { + ... + "buffering" : { + "autoFlush" : false, + "enabled" : false + } + }, +... +---- +You can set up `autoFlush` when buffering is enabled. OpenIDM then writes data to audit logs asynchronously, while `autoFlush` functionality ensures that the audit service writes data to logs on a regular basis. + +If audit data is important, do activate `autoFlush`. It minimizes the risk of data loss in case of a server crash. + + +[#audit-purging] +=== Purging Obsolete Audit Information + +If reconciliation audit volumes grow "excessively" large, any subsequent reconciliations, as well as queries to audit tables, can become "sluggish". In a deployment with limited resources, a lack of disk space can affect system performance. + +You might already have restricted what is logged in your audit logs by setting up filters, as described in xref:#filtering-audit-events["Event Topics: Filtering"]. You can also use specific queries to purge reconciliation audit logs, or you can purge reconciliation audit entries older than a specific date, using timestamps. + +OpenIDM includes a sample purge script, `autoPurgeRecon.js` in the `bin/defaults/script/audit` directory. This script purges reconciliation audit log entries only from the internal repository. It does not purge data from the corresponding CSV files or external repositories. + +To purge reconciliation audit logs on a regular basis, you must set up a schedule. A sample schedule is provided in the `schedule-autoPurgeAuditRecon.json` file (in the `openidm/samples/schedules` subdirectory). You can change that schedule as required, and copy the file to the `conf/` directory of your project, in order for it to take effect. + +The sample purge schedule file is as follows: + +[source, console] +---- +{ + "enabled" : false, + "type" : "cron", + "schedule" : "0 0 */12 * * ?", + "persisted" : true, + "misfirePolicy" : "doNothing", + "invokeService" : "script", + "invokeContext" : { + "script" : { + "type" : "text/javascript", + "file" : "audit/autoPurgeAuditRecon.js", + "input" : { + "mappings" : [ "%" ], + "purgeType" : "purgeByNumOfReconsToKeep", + "numOfRecons" : 1, + "intervalUnit" : "minutes", + "intervalValue" : 1 + } + } + } +} +---- +For information about the schedule-related properties in this file, see xref:chap-synchronization.adoc#scheduling-synchronization["Scheduling Synchronization"]. +-- +Beyond scheduling, the following parameters are of interest for purging the reconciliation audit logs: + +input:: +Input information. The parameters below specify different kinds of input. + +mappings:: +An array of mappings to prune. Each element in the array can be either a string or an object. + ++ +Strings must contain the mapping(s) name and can use "%" as a wild card value that will be used in a LIKE condition. + ++ +Objects provide the ability to specify mapping(s) to include/exclude and must be of the form: ++ + +[source, console] +---- +{ + "include" : "mapping1", + "exclude" : "mapping2" + ... +} +---- ++ + +purgeType:: +The type of purge to perform. Can be set to one of the following values: ++ +[open] +==== + +purgeByNumOfReconsToKeep:: +Uses the `deleteFromAuditReconByNumOf` function and the `numOfRecons` config variable. + +purgeByExpired:: +Uses the `deleteFromAuditReconByExpired` function and the config variables `intervalUnit` and `intervalValue`. + +==== + +num-of-recons:: +The number of recon summary entries to keep for a given mapping, including all child entries. + +intervalUnit:: +The type of time interval when using `purgeByExpired`. Acceptable values include: `minutes`, `hours`, or `days`. + +intervalValue:: +The value of the time interval when using `purgeByExpired`. Set to an integer value. + +-- + +[#audit-log-rotation] +==== Audit Log Rotation + +When you have filtered and purged unneeded log information, you can use log rotation services to limit the size of individual log files, and archive them as needed. Some log rotation services also support archiving to remote log servers. Details vary by the service and the operating system. + +Alternatively, you can stop logging of a specific audit event topic. For example, with the following command, you can stop processing to a CSV log file with a date and time stamp. This command also starts logging in a new file with the same base name. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/audit/access?handler=csv&_action=rotate" +---- +If successful, you'll see two `access.csv` files in the `openidm/audit` directory. One will have an extension such as `12.30.15-13.12`, which states that data collection in this file ended on December 30, 2015, at 1:12 pm. + +You can automate log rotation for the CSV audit event handler. In the Admin UI, click Configure > System Preferences > Audit, and edit or add a CSV audit event handler. You can then edit relevant properties like `rotationEnabled` and `rotationInterval`. For a full list of relevant CSV audit event handler log rotation properties, see xref:appendix-audit.adoc#audit-event-prop["Common Audit Event Handler Property Configuration"]. + + + +[#querying-audit-over-rest] +=== Querying Audit Logs Over REST + +Regardless of where audit events are stored, they are accessible over REST on the `/audit` endpoint. The following sections describe how to query the reconciliation, activity and sync logs over REST. These instructions can be applied to all the other log types. + +[NOTE] +==== +Queries on the audit endpoint must use `queryFilter` syntax. Predefined queries are not supported. For more information, see xref:chap-data.adoc#constructing-queries["Constructing Queries"]. +==== + +[#querying-recon-logs] +==== Querying the Reconciliation Audit Log + +With the default audit configuration, reconciliation operations are logged in the file `/path/to/openidm/audit/recon.csv`, and in the repository. You can read and query the reconciliation audit logs over the REST interface, as outlined in the following examples. + +To return all reconciliation operations logged in the audit log, query the `audit/recon` endpoint, as follows: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/audit/recon?_queryFilter=true" +---- +The following code extract shows the reconciliation audit log after the first reconciliation operation in Sample 1. + +[source, javascript] +---- +{ + "result" : [ { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-139", + "_rev" : "1", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.432Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "exception" : null, + "linkQualifier" : null, + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : "Reconciliation initiated by openidm-admin", + "sourceObjectId" : null, + "targetObjectId" : null, + "reconciling" : null, + "ambiguousTargetObjectIds" : null, + "reconAction" : "recon", + "entryType" : "start", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-147", + "_rev" : "1", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.711Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "action" : "CREATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : null, + "situation" : "ABSENT", + "sourceObjectId" : "system/xmlfile/account/bjensen", + "status" : "SUCCESS", + "targetObjectId" : "managed/user/bjensen", + "reconciling" : "source", + "ambiguousTargetObjectIds" : "", + "entryType" : "entry", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-146", + "_rev" : "1", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.711Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "action" : "CREATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : null, + "situation" : "ABSENT", + "sourceObjectId" : "system/xmlfile/account/scarter", + "status" : "SUCCESS", + "targetObjectId" : "managed/user/scarter", + "reconciling" : "source", + "ambiguousTargetObjectIds" : "", + "entryType" : "entry", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-148", + "_rev" : "1", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.732Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "exception" : null, + "linkQualifier" : null, + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : "SOURCE_IGNORED: 0 MISSING: 0 FOUND: 0 AMBIGUOUS: 0 UNQUALIFIED: 0 CONFIRMED: + 0 SOURCE_MISSING: 0 ABSENT: 2 TARGET_IGNORED: 0 UNASSIGNED: 0 FOUND_ALREADY_LINKED: 0 ", + "messageDetail" : { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "mapping" : "systemXmlfileAccounts_managedUser", + "state" : "SUCCESS", + "stage" : "COMPLETED_SUCCESS", + "stageDescription" : "reconciliation completed.", + "progress" : { + "source" : { + "existing" : { + "processed" : 2, + "total" : "2" + } + }, + "target" : { + "existing" : { + "processed" : 0, + "total" : "0" + }, + "created" : 2 + }, + "links" : { + "existing" : { + "processed" : 0, + "total" : "0" + }, + "created" : 2 + } + }, + "situationSummary" : { + "SOURCE_IGNORED" : 0, + "MISSING" : 0, + "FOUND" : 0, + "AMBIGUOUS" : 0, + "UNQUALIFIED" : 0, + "CONFIRMED" : 0, + "SOURCE_MISSING" : 0, + "ABSENT" : 2, + "TARGET_IGNORED" : 0, + "UNASSIGNED" : 0, + "FOUND_ALREADY_LINKED" : 0 + }, + "statusSummary" : { + "FAILURE" : 0, + "SUCCESS" : 2 + }, + "parameters" : { + "sourceQuery" : { + "resourceName" : "system/xmlfile/account", + "queryId" : "query-all-ids" + }, + "targetQuery" : { + "resourceName" : "managed/user", + "queryId" : "query-all-ids" + } + }, + "started" : "2015-11-23T00:18:34.431Z", + "ended" : "2015-11-23T00:18:34.730Z", + "duration" : 299 + }, + "sourceObjectId" : null, + "status" : "SUCCESS", + "targetObjectId" : null, + "reconciling" : null, + "ambiguousTargetObjectIds" : null, + "reconAction" : "recon", + "entryType" : "summary", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135" + } ], + "resultCount" : 4, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- +Most of the fields in the reconciliation audit log are self-explanatory. Each distinct reconciliation operation is identified by its `reconId`. Each entry in the log is identified by a unique `_id`. The first log entry indicates the status for the complete reconciliation operation. Successive entries indicate the status for each entry affected by the reconciliation. + +To obtain information about a specific log entry, include its entry `_id` in the URL. For example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/audit/recon/414a4921-5d9d-4398-bf86-7d5312a9f5d1-146" +---- +The following sample output shows the results of a read operation on a specific reconciliation audit entry. The entry shows the creation of bjensen's account in the managed user repository, as the result of a reconciliation operation. + +[source, javascript] +---- +{ + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-146", + "_rev" : "1", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.711Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "action" : "CREATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : null, + "situation" : "ABSENT", + "sourceObjectId" : "system/xmlfile/account/scarter", + "status" : "SUCCESS", + "targetObjectId" : "managed/user/scarter", + "reconciling" : "source", + "ambiguousTargetObjectIds" : "", + "entryType" : "entry", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135" +} +---- +To obtain information for a specific reconciliation operation, include the `reconId` in the query. You can filter the log so that the query returns only the fields you want to see, by adding the `_fields` parameter. + +The following query returns the `"mapping"`, `"timestamp"`, and `"entryType"` fields for a specific reconciliation operation. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + 'https://localhost:8443/openidm/audit/recon?_queryFilter=/reconId+eq+"4261227f-1d44-4042-ba7e-1dcbc6ac96b8"&_fields=mapping,timestamp,entryType' + { + "result" : [ { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-148", + "_rev" : "1", + "mapping" : "systemXmlfileAccounts_managedUser", + "timestamp" : "2015-11-23T00:18:34.732Z", + "entryType" : "summary" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-146", + "_rev" : "1", + "mapping" : "systemXmlfileAccounts_managedUser", + "timestamp" : "2015-11-23T00:18:34.711Z", + "entryType" : "entry" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-147", + "_rev" : "1", + "mapping" : "systemXmlfileAccounts_managedUser", + "timestamp" : "2015-11-23T00:18:34.711Z", + "entryType" : "entry" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-139", + "_rev" : "1", + "mapping" : "systemXmlfileAccounts_managedUser", + "timestamp" : "2015-11-23T00:18:34.432Z", + "entryType" : "start" + } ], + "resultCount" : 4, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- +To query the reconciliation audit log for a particular reconciliation situation, include the `reconId` and the `situation` in the query. For example, the following query returns all ABSENT entries that were found during the specified reconciliation operation: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + 'https://localhost:8443/openidm/audit/recon?_queryFilter=/reconId+eq+"414a4921-5d9d-4398-bf86-7d5312a9f5d1-135"+and+situation+eq+"ABSENT"' + { + "result" : [ { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-146", + "_rev" : "1", + "situation" : "ABSENT", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.711Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "action" : "CREATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : null, + "sourceObjectId" : "system/xmlfile/account/scarter", + "status" : "SUCCESS", + "targetObjectId" : "managed/user/scarter", + "reconciling" : "source", + "ambiguousTargetObjectIds" : "", + "entryType" : "entry" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-147", + "_rev" : "1", + "situation" : "ABSENT", + "reconId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.711Z", + "eventName" : "recon", + "userId" : "openidm-admin", + "action" : "CREATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "systemXmlfileAccounts_managedUser", + "message" : null, + "sourceObjectId" : "system/xmlfile/account/bjensen", + "status" : "SUCCESS", + "targetObjectId" : "managed/user/bjensen", + "reconciling" : "source", + "ambiguousTargetObjectIds" : "", + "entryType" : "entry" + } ], + "resultCount" : 2, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- + + +[#querying-activity-logs] +==== Querying the Activity Audit Log + +The activity logs track all operations on internal (managed) and external (system) objects. Entries in the activity log contain identifiers for the reconciliation or synchronization action that triggered an activity, and for the original caller and the relationships between related actions. + +You can access the activity logs over REST with the following call: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/audit/activity?_queryFilter=true" +---- +The following extract of the activity log shows one entry that created user bjensen. + +[source, javascript] +---- +}, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-145", + "_rev" : "1", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-135", + "timestamp" : "2015-11-23T00:18:34.674Z", + "eventName" : "activity", + "userId" : "openidm-admin", + "runAs" : "openidm-admin", + "operation" : "CREATE", + "before" : null, + "after" : "{ \"mail\": \"bjensen@example.com\", \"givenName\": \"Barbara\", \"sn\": \"Jensen\", + \"description\": \"Created By XML1\", \"_id\": \"bjensen\", \"userName\": \"bjensen@example.com\", + \"password\": { \"$crypto\": { \"value\": { \"iv\": \"KHjYJYacmk4UrXzfoTDaSQ==\", \"data\": + \"o0Lq5HYqgJPSrKSD4AXYsA==\", \"cipher\": \"AES/CBC/PKCS5Padding\", \"key\": \"openidm-sym-default\" }, + \"type\": \"x-simple-encryption\" } }, \"telephoneNumber\": \"1234567\", \"accountStatus\": \"active\", + \"effectiveRoles\": null, \"effectiveAssignments\": [ ], \"_rev\": \"1\" }", + "changedFields" : [ ], + "revision" : "1", + "message" : "create", + "objectId" : "managed/user/bjensen", + "passwordChanged" : true, + "status" : "SUCCESS" + } ], +... +---- +To return the activity information for a specific action, include the `_id` of the action in the URL, for example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + 'https://localhost:8443/openidm/audit/activity/414a4921-5d9d-4398-bf86-7d5312a9f5d1-145' +---- +Each action in the activity log has a `transactionId` that is the same as the `transactionId` that was assigned to the incoming or initiating request. So, for example, if an HTTP request invokes a script that changes a user's password, the HTTP request is assigned a `transactionId`. The action taken by the script is assigned the same `transactionId`, which enables you to track the complete set of changes resulting from a single action. You can query the activity log for all actions that resulted from a specific transaction, by including the `transactionId` in the query. + +The following command returns all actions in the activity log that happened as a result of a reconciliation, with a specific `transactionId`. The results of the query are restricted to only the `objectId` and the `resourceOperation`. You can see from the output that the reconciliation with this `transactionId` resulted in two CREATEs and two UPDATEs in the managed repository. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + 'https://localhost:8443/openidm/audit/activity?_queryFilter=/transactionId+eq+"414a4921-5d9d-4398-bf86-7d5312a9f5d1-135"&_fields=objectId,operation' +---- +The following sample output shows the result of a query that created users scarter and bjensen. + +[source, javascript] +---- +{ + "result" : [ { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-144", + "_rev" : "1", + "objectId" : "managed/user/scarter", + "operation" : "CREATE" + }, { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-145", + "_rev" : "1", + "objectId" : "managed/user/bjensen", + "operation" : "CREATE" + } ], + "resultCount" : 2, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- + + +[#querying-sync-logs] +==== Querying the Synchronization Audit Log + +LiveSync and implicit sync operations are logged in the file `/path/to/openidm/audit/sync.csv` and in the repository. You can read the synchronization audit logs over the REST interface, as outlined in the following examples. + +To return all operations logged in the synchronization audit log, query the `audit/sync` endpoint, as follows: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/audit/sync?_queryFilter=true" +{ + "result" : [ { + "_id" : "53709f21-5b83-4ea0-ac35-9af39c3090cf-95", + "_rev" : "1", + "transactionId" : "53709f21-5b83-4ea0-ac35-9af39c3090cf-85", + "timestamp" : "2015-11-23T05:07:39.376Z", + "eventName" : "sync", + "userId" : "openidm-admin", + "action" : "UPDATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "managedUser_systemLdapAccounts", + "message" : null, + "situation" : "CONFIRMED", + "sourceObjectId" : "managed/user/128e0e85-5a07-4e72-bfc8-4d9500a027ce", + "status" : "SUCCESS", + "targetObjectId" : "uid=jdoe,ou=People,dc=example,dc=com" + }, { +... +---- +Most of the fields in the synchronization audit log are self-explanatory. Each entry in the log synchronization operation is identified by a unique `_id`. Each __synchronization operation__ is identified with a `transactionId`. The same base `transactionId` is assigned to the incoming or initiating request - so if a modification to a user entry triggers an implicit synchronization operation, both the sync operation and the original change operation have the same `transactionId`. You can query the sync log for all actions that resulted from a specific transaction, by including the `transactionId` in the query. + +To obtain information on a specific sync audit log entry, include its entry `_id` in the URL. For example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/audit/sync/53709f21-5b83-4ea0-ac35-9af39c3090cf-95" +{ + "_id" : "53709f21-5b83-4ea0-ac35-9af39c3090cf-95", + "_rev" : "1", + "transactionId" : "53709f21-5b83-4ea0-ac35-9af39c3090cf-85", + "timestamp" : "2015-11-23T05:07:39.376Z", + "eventName" : "sync", + "userId" : "openidm-admin", + "action" : "UPDATE", + "exception" : null, + "linkQualifier" : "default", + "mapping" : "managedUser_systemLdapAccounts", + "message" : null, + "situation" : "CONFIRMED", + "sourceObjectId" : "managed/user/128e0e85-5a07-4e72-bfc8-4d9500a027ce", + "status" : "SUCCESS", + "targetObjectId" : "uid=jdoe,ou=People,dc=example,dc=com" +} +---- + + +[#querying-auth-log] +==== Querying the Authentication Audit Log + +The authentication log includes details of all successful and failed authentication attempts. The output may be long. The output that follows is one excerpt from 114 entries. To obtain the complete audit log over REST, use the following query: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/audit/authentication?_queryFilter=true" +{ + "result" : [ { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-5", + "_rev" : "1", + "context" : { + "id" : "anonymous", + "component" : "repo/internal/user", + "roles" : [ "openidm-reg" ], + "ipAddress" : "127.0.0.1" + }, + "entries" : [ { + "moduleId" : "IDMAuthModuleWrapper", + "result" : "FAILED", + "reason" : { }, + "info" : { } + }, { + "moduleId" : "IDMAuthModuleWrapper", + "result" : "SUCCESSFUL", + "info" : { + "org.forgerock.authentication.principal" : "anonymous" + } + } ], + "principal" : [ "anonymous" ], + "result" : "SUCCESSFUL", + "userId" : "anonymous", + "transactionId" : "be858917-764c-4b05-8a6b-ee91cfd8c7e7", + "timestamp" : "2015-11-23T00:18:10.231Z", + "eventName" : "authentication", + "trackingIds" : [ "ea9e65f1-fd28-4153-abc2-891ccbfd482e" ] +} +... +---- +You can filter the results to return only those audit entries that you are interested in. For example, the following query returns all authentication attempts made by a specific user (`user.0`) but displays only the security context and the result of the authentication attempt. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + 'https://localhost:8443/openidm/audit/authentication?_queryFilter=/principal+eq+"user.0"&_fields=context,result' +{ + "result": [ + { + "context": { + "id": "e98fdfbe-d436-4e09-b44e-f6727b1e293d", + "component": "managed/user", + "roles": [ + "openidm-authorized" + ], + "ipAddress": "0:0:0:0:0:0:0:1" + }, + "result": "SUCCESSFUL" + }, + { + "context": { + "ipAddress": "0:0:0:0:0:0:0:1" + }, + "result": "FAILED" + }, + { + "context": { + "ipAddress": "0:0:0:0:0:0:0:1" + }, + "result": "FAILED" + }, + { + "context": { + "id": "e98fdfbe-d436-4e09-b44e-f6727b1e293d", + "component": "managed/user", + "roles": [ + "openidm-authorized" + ], + "ipAddress": "0:0:0:0:0:0:0:1" + }, + "result": "SUCCESSFUL" + }, + { + "context": { + "id": "e98fdfbe-d436-4e09-b44e-f6727b1e293d", + "component": "managed/user", + "roles": [ + "openidm-authorized" + ], + "ipAddress": "0:0:0:0:0:0:0:1" + }, + "result": "SUCCESSFUL" + }, + { + "context": { + "id": "e98fdfbe-d436-4e09-b44e-f6727b1e293d", + "component": "managed/user", + "roles": [ + "openidm-authorized" + ], + "ipAddress": "0:0:0:0:0:0:0:1" + }, + "result": "SUCCESSFUL" + }, +... +---- + + +[#querying-config-log] +==== Querying the Configuration Audit Log + +This audit log lists changes made to the configuration in the audited OpenIDM server. You can read through the changes in the `config.extension` file in the `openidm/audit` directory. + +You can also read the complete audit log over REST with the following query: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"https://localhost:8443/openidm/audit/config?_queryFilter=true" +{ + "result" : [ { + "_id" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-73", + "_rev" : "1", + "operation" : "CREATE", + "userId" : "openidm-admin", + "runAs" : "openidm-admin", + "transactionId" : "414a4921-5d9d-4398-bf86-7d5312a9f5d1-58", + "revision" : null, + "timestamp" : "2015-11-23T00:18:17.808Z", + "objectId" : "ui", + "eventName" : "CONFIG", + "before" : "", + "after" : "{ \"icons\": + ... + } ], + "resultCount" : 3, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- +The output includes a `"before"` and `"after"` entry, which represents the changes in OpenIDM configuration files. + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-auth.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-auth.adoc new file mode 100644 index 000000000..058dd729b --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-auth.adoc @@ -0,0 +1,1136 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-auth] +== Managing Authentication, Authorization and Role-Based Access Control + +OpenIDM provides a flexible authentication and authorization mechanism, based on REST interface URLs and on managed roles. This chapter describes how to configure the supported authentication modules, and how roles are used to support authentication, authorization, and access control. + +[#openidm-authentication] +=== OpenIDM Authentication + +OpenIDM does not allow access to the REST interface without authentication. User self-registration requires anonymous access. For this purpose, OpenIDM includes an `anonymous` user, with the password `anonymous`. For more information, see xref:#internal-users["Internal Users"]. + +OpenIDM supports an enhanced authentication mechanism over the REST interface, that is compatible with the AJAX framework. Although OpenIDM understands the authorization header of the HTTP basic authorization contract, it deliberately does not utilize the full contract. In other words, it does not cause the browser built in mechanism to prompt for username and password. However, OpenIDM does understand utilities such as `curl` that can send the username and password in the Authorization header. + +In general, the HTTP basic authentication mechanism does not work well with client side web applications, and applications that need to render their own login screens. Because the browser stores and sends the username and password with each request, HTTP basic authentication has significant security vulnerabilities. OpenIDM therefore supports sending the username and password via the authorization header, and returns a token for subsequent access. + +This document uses the OpenIDM authentication headers in all REST examples, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + ... +---- +For more information about the OpenIDM authentication mechanism, see xref:chap-security.adoc#security-messages["Use Message Level Security"]. + +[#openidm-users] +==== Authenticating OpenIDM Users + +OpenIDM stores two types of users in its repository - internal users and managed users. The way in which both of these user types are authenticated is defined in your project's `conf/authentication.json` file. + +[#internal-users] +===== Internal Users + +OpenIDM creates two internal users by default: `anonymous` and `openidm-admin`. These internal user accounts are separated from other user accounts to protect them from any reconciliation or synchronization processes. + +OpenIDM stores internal users and their role membership in a table in the repository. The two default internal users have the following functions: +-- + +anonymous:: +This user enables anonymous access to OpenIDM, for users who do not have their own accounts. The anonymous user has limited rights within OpenIDM. By default, the anonymous user has the `openidm-reg` role, and can be used to allow self-registration. For more information about self-registration, see xref:chap-ui.adoc#ui-self-registration["The End User and Commons User Self-Service"]. + +openidm-admin:: +This user serves as the top-level administrator. After installation, the `openidm-admin` user has full access, and provides a fallback mechanism in the event that other users are locked out of their accounts. Do not use `openidm-admin` for regular tasks. Under normal circumstances, the `openidm-admin` account does not represent a regular user, so audit log records for this account do not represent the actions of any real person. + ++ +The default password for the `openidm-admin` user (also `openidm-admin`) is not encrypted, and is not secure. In production environments, you must change this password to a more secure one, as described in the following section. The new password will be encoded using a salted hash algorithm, when it is changed. + +-- + +[#repo-internal-user] +====== Managing Internal Users Over REST + +Like any other user in the repository, you can manage internal users over the REST interface. + +To list the internal users over REST, query the `repo` endpoint as follows: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/repo/internal/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "openidm-admin", + "_rev": "1" + }, + { + "_id": "anonymous", + "_rev": "1" + } + ], + "resultCount": 2, + "pagedResultsCookie": null, + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "remainingPagedResults": -1 +} +---- +To query the details of an internal user, include the user's ID in the request, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/repo/internal/user/openidm-admin" +{ + "_id": "openidm-admin", + "_rev": "1", + "roles": [ + { + "_ref": "repo/internal/role/openidm-admin" + }, + { + "_ref": "repo/internal/role/openidm-authorized" + } + ], + "userName": "openidm-admin", + "password": "openidm-admin" +} +---- +To change the password of the default administrative user, send a PUT request to the user object. The following example changes the password of the `openidm-admin` user to `Passw0rd`: + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PUT \ + --data '{ + "_id": "openidm-admin", + "roles": [ + { + "_ref": "repo/internal/role/openidm-admin" + }, + { + "_ref": "repo/internal/role/openidm-authorized" + } + ], + "userName": "openidm-admin", + "password": "Passw0rd" + }' \ + "http://localhost:8080/openidm/repo/internal/user/openidm-admin" +{ + "_id": "openidm-admin", + "_rev": "2", + "roles": [ + { + "_ref": "repo/internal/role/openidm-admin" + }, + { + "_ref": "repo/internal/role/openidm-authorized" + } + ], + "userName": "openidm-admin", + "password": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "spKRPPYpDFZZWuJsOQa03vT2Gf+pFYUW8Zj6eCXuvMj19wZasYmdI2sCOrmmxiUQ" + }, + "type": "salted-hash" + } + } +} +---- + + + +[#managed-users] +===== Managed Users + +External users that are managed by OpenIDM are known as managed users. + +The table in which managed users are stored depends on the type of repository. For JDBC repositories, OpenIDM stores managed users in the managed objects table, named `managedobjects`, and indexes those objects in a table named `managedobjectproperties`. + +For an OrientDB repository, managed objects are stored in the table `managed_user`. + +OpenIDM provides RESTful access to managed users, at the context path `/openidm/managed/user`. For more information, see xref:appendix-rest.adoc#managing-users-REST["Managing Users Over REST"]. + + +[#internal-managed-authentication] +===== Authenticating Internal and Managed Users + +By default, the attribute names that are used to authenticate managed and internal users are `username` and `password`, respectively. However, you can explicitly define the properties that constitute usernames, passwords or roles with the `propertyMapping` object in the `conf/authentication.json` file. The following excerpt of the `authentication.json` file shows the default property mapping object: + +[source, javascript] +---- +... + "propertyMapping" : { + "authenticationId" : "username", + "userCredential" : "password", + "userRoles" : "roles" + }, + ... +---- +If you change the attribute names that are used for authentication, you must adjust the following authentication queries (defined in the repository configuration file, `openidm/conf/repo.repo-type.json`). +-- +Two queries are defined by default. + +`credential-internaluser-query`:: +This query uses the `username` attribute for login, for internal users. For example, the following `credential-internaluser-query` is defined in the default repository configuration file for a MySQL repository. ++ + +[source, console] +---- +"credential-internaluser-query" : "SELECT objectid, pwd, roles FROM + ${_dbSchema}.${_table} WHERE objectid = ${username}", +---- + +`credential-query`:: +This query uses the `username` attribute for login, for managed users. For example, the following `credential-query` is defined in the default repository configuration file for a MySQL repository. ++ + +[source, console] +---- +"credential-query" : "SELECT * FROM ${_dbSchema}.${_table} WHERE + objectid = ${username} and accountStatus = 'active'", +---- + +-- +The query that is used for a particular resource is specified by the `queryId` property in the `authentication.json` file. The following sample excerpt of that file shows that the `credential-query` is used when validating managed user credentials. + +[source, javascript] +---- +{ + "queryId" : "credential-query", + "queryOnResource" : "managed/user", +... +} +---- + + + +[#supported-auth-session-modules] +==== Supported Authentication and Session Modules + +The authentication configuration is defined in `conf/authentication.json`. This file configures the methods by which a user request is authenticated. It includes both session and authentication module configuration. + +You can review and configure supported modules in the Admin UI. To do so, log into `\https://localhost:8443/admin`, and select Configure > System Preferences > Authentication. + +[#admin-ui-auth-modules] +image::images/admin-ui-authentication.png[] + +[#supported-session-modules] +===== Supported Session Module + +At this time, OpenIDM includes one supported session module. The JSON Web Token session module configuration specifies keystore information, and details about the session lifespan. The default `JWT_SESSION` configuration is as follows: + +[source, javascript] +---- +"sessionModule" : { + "name" : "JWT_SESSION", + "properties" : { + "keyAlias" : "&{openidm.https.keystore.cert.alias}", + "privateKeyPassword" : "&{openidm.keystore.password}", + "keystoreType" : "&{openidm.keystore.type}", + "keystoreFile" : "&{openidm.keystore.location}", + "keystorePassword" : "&{openidm.keystore.password}", + "sessionOnly" : true + "isHttpOnly" : true + "maxTokenLifeMinutes" : "120", + "tokenIdleTimeMinutes" : "30" + } +}, +---- + +[NOTE] +==== +If you're working with the link:../integrators-guide/index.html#openam-session-module[OPENAM_SESSION] module, change the token lifetime properties as shown here, to match the session token lifetime associated with OpenAM. + +[source, javascript] +---- +"maxTokenLifeSeconds" : "5", + "tokenIdleTimeSeconds" : "5" +---- +==== +For more information about the `JWT_SESSION` module, see the following Javadoc page: link:http://commons.forgerock.org/forgerock-auth-filters/forgerock-authn-filter/forgerock-jaspi-modules/forgerock-jaspi-jwt-session-module/apidocs/org/forgerock/jaspi/modules/session/jwt/JwtSessionModule.html[Class JwtSessionModule, window=\_blank]. + + +[#supported-auth-modules] +===== Supported Authentication Modules + +-- +OpenIDM evaluates modules in the order shown in the `authentication.json` file for your project. When OpenIDM finds a module to authenticate a user, it does not evaluate subsequent modules. + +You can also configure the order of authentication modules in the Admin UI. After logging in, click Configure > System Preferences > Authentication. The following figure illustrates how you might include the IWA module in the Admin UI. + +[#auth-iwa-module] +image::images/auth-iwa-module.png[] +You must prioritize the authentication modules that query OpenIDM resources. Prioritizing the modules that query external resources might lead to authentication problems for internal users such as `openidm-admin`. + +STATIC_USER:: +`STATIC_USER` authentication provides an anonymous authentication mechanism that bypasses any database lookups if the headers in a request indicate that the user is `anonymous`. The following sample REST call uses `STATIC_USER` authentication in the self-registration process: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Password: anonymous" \ + --header "X-OpenIDM-Username: anonymous" \ + --header "Content-Type: application/json" \ + --data '{ + "userName":"steve", + "givenName":"Steve", + "sn":"Carter", + "telephoneNumber":"0828290289", + "mail":"scarter@example.com", + "password":"Passw0rd" + }' \ + --request POST \ + "http://localhost:8080/openidm/managed/user/?_action=create" +---- ++ +Note that this is not the same as an anonymous request that is issued without headers. + ++ +Authenticating with the `STATIC_USER` module avoids the performance cost of reading the database for self-registration, certain UI requests, and other actions that can be performed anonymously. Authenticating the anonymous user with the `STATIC_USER` module is identical to authenticating the anonymous user with the `INTERNAL_USER` module, except that the database is not accessed. So, `STATIC_USER` authentication provides an authentication mechanism for the anonymous user that avoids the database lookups incurred when using `INTERNAL_USER`. + ++ +A sample `STATIC_USER` authentication configuration follows: ++ + +[source] +---- +{ + "name" : "STATIC_USER", + "enabled" : true, + "properties" : { + "propertyMapping" : "{}", + "queryOnResource" : "repo/internal/user", + "username" : "anonymous", + "password" : "anonymous", + "defaultUserRoles" : [ + "openidm-reg" + ], + "augmentSecurityContext" : null + } +} +---- + +TRUSTED_ATTRIBUTE:: +The `TRUSTED_ATTRIBUTE` authentication module allows you to configure OpenIDM to trust the `HttpServletRequest` attribute of your choice. You can configure it by adding the `TRUSTED_ATTRIBUTE` module to your `authentication.json` file, as shown in the following code block: ++ + +[source, javascript] +---- +... +{ + "name" : "TRUSTED_ATTRIBUTE", + "properties" : { + "queryOnResource" : "managed/user", + "propertyMapping" : { + "authenticationId" : "username", + "userRoles" : "authzRoles" + }, + "defaultUserRoles" : [ ], + "authenticationIdAttribute" : "X-ForgeRock-AuthenticationId", + "augmentSecurityContext" : { + "type" : "text/javascript", + "file" : "auth/populateRolesFromRelationship.js" + } + }, + "enabled" : true +} +... +---- ++ +`TRUSTED_ATTRIBUTE` authentication queries the `managed/user` repository, and allows authentication when credentials match, based on the `username` and `authzRoles` assigned to that user, specifically the `X-ForgeRock-AuthenticationId` attribute. + ++ +To see how you can configure this with OpenIDM, see xref:../samples-guide/chap-trustedfilter-sample.adoc#chap-trustedfilter-sample["The Trusted Servlet Filter Sample"] in the __Samples Guide__. + +MANAGED_USER:: +`MANAGED_USER` authentication queries the repository, specifically the `managed/user` objects, and allows authentication if the credentials match. The default configuration uses the `username` and `password` of the managed user to authenticate, as shown in the following sample configuration: ++ + +[source, javascript] +---- +{ + "name" : "MANAGED_USER", + "enabled" : true, + "properties" : { + "queryId" : "credential-query", + "queryOnResource" : "managed/user", + "propertyMapping" : { + "authenticationId" : "username", + "userCredential" : "password", + "userRoles" : "roles" + }, + "defaultUserRoles" : [ ] + } +}, +---- + +INTERNAL_USER:: +`INTERNAL_USER` authentication queries the repository, specifically the `repo/internal/user` objects, and allows authentication if the credentials match. The default configuration uses the `username` and `password` of the internal user to authenticate, as shown in the following sample configuration: ++ + +[source, javascript] +---- +{ + "name" : "INTERNAL_USER", + "enabled" : true, + "properties" : { + "queryId" : "credential-internaluser-query", + "queryOnResource" : "repo/internal/user", + "propertyMapping" : { + "authenticationId" : "username", + "userCredential" : "password", + "userRoles" : "roles" + }, + "defaultUserRoles" : [ ] + } +}, +---- + +CLIENT_CERT:: +The client certificate module, `CLIENT_CERT`, provides authentication by validating a client certificate, transmitted via an HTTP request. OpenIDM compares the subject DN of the request certificate with the subject DN of the truststore. + ++ +A sample `CLIENT_CERT` authentication configuration follows: ++ + +[source] +---- +{ + "name" : "CLIENT_CERT", + "enabled" : true, + "properties" : { + "queryOnResource" : "security/truststore", + "defaultUserRoles" : [ "openidm-cert" ], + "allowedAuthenticationIdPatterns" : [ ] + } +}, +---- ++ +The `allowedAuthenticationIdPatterns` filter enables you to specify an array of usernames or username patterns that will be accepted for authentication. If this property is empty, any username can authenticate. + ++ +For detailed options, see xref:#auth-client-cert["Configuring the CLIENT_CERT Authentication Module"]. + +-- +The modules that follow point to external systems. In the `authentication.json` file, you should generally include these modules after any modules that query internal OpenIDM resources. +-- + +[#passthrough-module] +PASSTHROUGH:: +`PASSTHROUGH` authentication queries an external system, such as an LDAP server, and allows authentication if the provided credentials match those in the external system. The following sample configuration shows pass-through authentication using the user objects in the system endpoint `system/ldap/account`. For more information on pass-through authentication, see xref:#passthrough-auth["Configuring Pass-Through Authentication"]. + +[#openam-session-module] +OPENAM_SESSION:: +The `OPENAM_SESSION` module enables you to protect an OpenIDM deployment with ForgeRock's link:http://docs.forgerock.org/en/index.html?product=openam[OpenAM Access Management, window=\_blank] product. For an example of how you might use the `OPENAM_SESSION` module, see xref:../samples-guide/chap-fullstack-sample.adoc#chap-fullstack-sample["Full Stack Sample - Using OpenIDM in the ForgeRock Identity Platform"] in the __Samples Guide__. + ++ +For detailed options, see xref:appendix-auth-modules.adoc#openam-module-details["OPENAM_SESSION Module Configuration Options"]. + ++ +The use case is when you need to integrate IDM endpoints behind the scenes within other applications, such as with a company intranet portal. In that configuration, users would log into OpenAM to access the portal; at that point, their sessions would use the OpenAM SSO cookie, also known as `iPlanetDirectoryPro`. For more information, see the OpenAM Administration Guide section on link:../../../openam/13.5/admin-guide/#session-state-cookies[Session Cookies, window=\_blank]. ++ + +[NOTE] +====== +If you use the `OPENAM_SESSION` token, you'll need to set a `JWT_SESSION` maximum token lifetime of __5 seconds__, to match the corresponding token session lifetime in OpenAM. For more information on the `JWT_SESSION` module, see the following section: link:../integrators-guide/index.html#supported-session-modules[Supported Session Modules] + +Ensure that at least one user in any shared OpenDJ repository has an `openidm-admin` role. + +Set up logins with OpenAM, to work with the related login session cookie, known as `iPlanetDirectoryPro`. +====== + +IWA:: +The IWA module enables users to authenticate by using Integrated Windows Authentication (IWA), rather than by providing a username and password. For information about configuring the IWA module with OpenIDM, see xref:#openidm-auth-kerberos["Configuring IWA Authentication"]. + +-- + + + +[#passthrough-auth] +==== Configuring Pass-Through Authentication + +OpenIDM 4.5 supports a pass-through authentication mechanism. With pass-through authentication, the credentials included with the REST request are validated against those stored in a remote system, such as an LDAP server. + +The following excerpt of an `authentication.json` shows a pass-through authentication configuration for an LDAP system. + +[source, javascript] +---- +"authModules" : [ + { + "name" : "PASSTHROUGH", + "enabled" : true, + "properties" : { + "augmentSecurityContext": { + "type" : "text/javascript", + "file" : "auth/populateAsManagedUser.js" + }, + "queryOnResource" : "system/ldap/account", + "propertyMapping" : { + "authenticationId" : "uid", + "groupMembership" : "memberOf" + }, + "groupRoleMapping" : { + "openidm-admin" : ["cn=admins"] + }, + "managedUserLink" : "systemLdapAccounts_managedUser", + "defaultUserRoles" : [ + "openidm-authorized" + ] + }, + }, + ... + ] +---- +For more information on authentication module properties, see the following: xref:appendix-auth-modules.adoc#appendix-auth-modules["Authentication and Session Module Configuration Details"]. + +The OpenIDM samples, described in xref:../samples-guide/chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"] in the __Samples Guide__, include several examples of pass-through authentication configuration. Samples 2, 2b, 2c, and 2d use an external LDAP system for authentication. Sample 3 authenticates against a SQL database. Sample 6 authenticates against an Active Directory server. The `scriptedrest2dj` sample uses a scripted REST connector to authenticate against an OpenDJ server. + + +[#openidm-auth-kerberos] +==== Configuring IWA Authentication + +When OpenIDM is configured for IWA authentication, client browsers can authenticate to OpenIDM using a Kerberos ticket. + +To enable Kerberos authentication, OpenIDM needs a specific Kerberos user account in Active Directory, and a keytab file that maps the service principal to this user account. When this is set up, the client presents OpenIDM with a Kerberos ticket. If OpenIDM can validate that ticket, the client is granted an encrypted session key for the OpenIDM service. That client can then access OpenIDM without providing a username or password, for the duration of the session. + +The complete Kerberos authentication process is shown in the following diagram: + +[#d0e21021] +image::images/kerberos-auth.png[] +This section assumes that you have an active Kerberos server acting as a Key Distribution Center (KDC). If you are running Active Directory in your deployment, that service includes a Kerberos KDC by default. + +The steps required to set up IWA with OpenIDM are described in the following sections: + +. xref:#iwa-create-user["Creating a Specific Kerberos User Account for OpenIDM"] + +. xref:#iwa-keytab["Creating a Keytab File"] + +. xref:#iwa-openidm-config["Configuring OpenIDM for IWA"] + + +[#iwa-create-user] +===== Creating a Specific Kerberos User Account for OpenIDM + +To authenticate OpenIDM to the Kerberos KDC you must create a specific user entry in Active Directory whose credentials will be used for this authentication. This Kerberos user account must not be used for anything else. + +The Kerberos user account is used to generate the Kerberos keytab. If you change the password of this Kerberos user after you have set up IWA authentication, you must update the keytab accordingly. + +==== +Create a new user in Active Directory as follows: + +. Select New > User and provide a login name for the user that reflects its purpose, for example, openidm@example.com. + +. Enter a password for the user. Check the __Password never expires__ option and leave all other options unchecked. ++ +If the password of this user account expires, and is reset, you must update the keytab with the new password. It is therefore easier to create an account with a password that does not expire. + +. Click Finish to create the user. + +==== + + +[#iwa-keytab] +===== Creating a Keytab File + +A Kerberos keytab file (`krb5.keytab`) enables OpenIDM to validate the Kerberos tickets that it receives from client browsers. You must create a Kerberos keytab file for the host on which OpenIDM is running. + +This section describes how to use the `ktpass` command, included in the Windows Server toolkit, to create the keytab file. Run the `ktpass` command on the Active Directory domain controller. Pay close attention to the use of capitalization in this example because the keytab file is case-sensitive. Note that you must disable UAC or run the `ktpass` command as a user with administration privileges. + +The following command creates a keytab file (named `openidm.HTTP.keytab`) for the OpenIDM service located at `openidm.example.com`. + +[source, console] +---- +C:\Users\Administrator>ktpass ^ + -princ HTTP/openidm.example.com@EXAMPLE.COM ^ + -mapUser EXAMPLE\openidm ^ + -mapOp set ^ + -pass Passw0rd1 ^ + -crypto ALL + -pType KRB5_NT_PRINCIPAL ^ + -kvno 0 ^ + -out openidm.HTTP.keytab + +Targeting domain controller: host.example.com +Using legacy password setting method +Successfully mapped HTTP/openidm.example.com to openidm. +Key created. +Output keytab to openidm.HTTP.keytab: +Keytab version: 0x502 +keysize 79 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x1 (DES-CBC-CRC) keylength 8 (0x73a28fd307ad4f83) +keysize 79 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x3 (DES-CBC-MD5) keylength 8 (0x73a28fd307ad4f83) +keysize 87 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x17 (RC4-HMAC) keylength 16 (0xa87f3a337d73085c45f9416be5787d86) +keysize 103 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x12 (AES256-SHA1) keylength 32 (0x6df9c282abe3be787553f23a3d1fcefc + 6fc4a29c3165a38bae36a8493e866d60) +keysize 87 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x11 (AES128-SHA1) keylength 16 (0xf616977f071542cd8ef3ff4e2ebcc09c) +---- +The `ktpass` command takes the following options: + +* `-princ` specifies the service principal name in the format `service/host-name@realm` ++ +In this example (`HTTP/openidm.example.com@EXAMPLE.COM`), the client browser constructs an SPN based on the following: ++ + +** The service name (HTTP). ++ +The service name for SPNEGO web authentication __must__ be HTTP. + +** The FQDN of the host on which OpenIDM runs (`openidm.example.com`). ++ +This example assumes that users will access OpenIDM at the URL `\https://openidm.example.com:8443`. + +** The Kerberos realm name (`EXAMPLE.COM`). ++ +The realm name must be in upper case. A Kerberos realm defines the area of authority of the Kerberos authentication server. + + +* `-mapUser` specifies the name of the Kerberos user account to which the principal should be mapped (the account that you created in xref:#iwa-create-user["Creating a Specific Kerberos User Account for OpenIDM"]). The username must be specified in down-level logon name format (DOMAIN\UserName). In our example, the Kerberos user name is `EXAMPLE\openidm`. + +* `-mapOp` specifies how the Kerberos user account is linked. Use `set` to set the first user name to be linked. The default (`add`) adds the value of the specified local user name if a value already exists. + +* `-pass` specifies a password for the principal user name. Use "*" to prompt for a password. + +* `-crypto` Specifies the cryptographic type of the keys that are generated in the keytab file. Use `ALL` to specify all crypto types. ++ +This procedure assumes a 128-bit cryptosystem, with a default RC4-HMAC-NT cryptography algorithm. You can use the `ktpass` command to view the crypto algorithm, as follows: ++ + +[source, console] +---- +C:\Users\Administrator> ktpass -in .\openidm.HTTP.keytab +Existing keytab: +Keytab version: 0x502 +keysize 79 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x1 (DES-CBC-CRC) keylength 8 (0x73a28fd307ad4f83) +keysize 79 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x3 (DES-CBC-MD5) keylength 8 (0x73a28fd307ad4f83) +keysize 87 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x17 (RC4-HMAC) keylength 16 (0xa87f3a337d73085c45f9416be5787d86) +keysize 103 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x12 (AES256-SHA1) keylength 32 (0x6df9c282abe3be787553f23a3d1fcefc6 + fc4a29c3165a38bae36a8493e866d60) +keysize 87 HTTP/openidm.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) + vno 0 etype 0x11 (AES128-SHA1) keylength 16 (0xf616977f071542cd8ef3ff4e2ebcc09c) +---- + +* `-ptype` Specifies the principal type. Use `KRB5_NT_PRINCIPAL`. + +* `-kvno` specifies the key version number. Set the key version number to 0. + +* `-out` specifies the name of the keytab file that will be generated, for example, `openidm.HTTP.keytab`. ++ +Note that the keys that are stored in the keytab file are similar to user passwords. You must therefore protect the Kerberos keytab file in the same way that you would protect a file containing passwords. + +For more information about the `ktpass` command, see the link:http://technet.microsoft.com/en-us/library/cc753771(v=WS.10).aspx[ktpass reference, window=\_blank] in the Windows server documentation. + + +[#iwa-openidm-config] +===== Configuring OpenIDM for IWA + +To configure the IWA authentication module, you must do the following: + +. Add the `IWA` authentication module to your project's `conf/authentication.json` file. + +. Modify your project's `conf/system.properties` file to include a pointer to your login configuration for JAAS. + +This section assumes that the connection from OpenIDM to the Active Directory Server is through an LDAP connector, and that the mapping from managed users to the users in Active Directory (in your project's `conf/sync.json` file) identifies the Active Directory target as `system/ad/account`. If you have named the target differently, modify the `"queryOnResource" : "system/ad/account"` property accordingly. + +Add the IWA authentication module towards the end of your `conf/authentication.json` file. For example: + +[source, javascript] +---- +"authModules" : [ + ... + { + "name" : "IWA", + "properties": { + "servicePrincipal" : "HTTP/openidm.example.com@EXAMPLE.COM", + "keytabFileName" : "openidm.HTTP.keytab", + "kerberosRealm" : "EXAMPLE.COM", + "kerberosServerName" : "kdc.example.com", + "queryOnResource" : "system/ad/account", + "propertyMapping" : { + "authenticationId" : "sAMAccountName", + "groupMembership" : "memberOf" + }, + "groupRoleMapping" : { + "openidm-admin": [ ] + }, + "groupComparisonMethod": "ldap", + "defaultUserRoles" : [ + "openidm-authorized" + ], + "augmentSecurityContext" : { + "type" : "text/javascript", + "file" : "auth/populateAsManagedUser.js" + } + }, + "enabled" : true + } +---- +-- +The IWA authentication module includes the following configurable properties: + +`servicePrincipal`:: +The Kerberos principal for authentication, in the following format: ++ + +[source] +---- +HTTP/host.domain@DC-DOMAIN-NAME +---- ++ +__host__ and __domain__ correspond to the host and domain names of the OpenIDM server. __DC-DOMAIN-NAME__ is the domain name of the Windows Kerberos domain controller server. The __DC-DOMAIN-NAME__ can differ from the domain name for the OpenIDM server. + +`keytabFileName`:: +The full path to the keytab file for the Service Principal. + +`kerberosRealm`:: +The Kerberos Key Distribution Center realm. For the Windows Kerberos service, this is the domain controller server domain name. + +`kerberosServerName`:: +The fully qualified domain name of the Kerberos Key Distribution Center server, such as that of the domain controller server. + +`groupRoleMapping`:: +Enables you to grant different roles to users who are authenticated through the `IWA` module. + +-- +You can use the `IWA` module in conjunction with the `PASSTHROUGH` authentication module. In this case, a failure in the `IWA` module allows users to revert to forms-based authentication. + +To add the `PASSTHROUGH` module, follow xref:#passthrough-auth["Configuring Pass-Through Authentication"]. + +When you have included the `IWA` module in your `conf/authentication.json` file, edit the `conf/system.properties` file to include a pointer to your login configuration file for JAAS. For example: + +[source, console] +---- +java.security.auth.login.config=&{launcher.project.location}/conf/gssapi_jaas.conf +---- +Your `gssapi_jaas.conf` file must include the following information related to the LDAP connector: + +[source, javascript] +---- +org.identityconnectors.ldap.LdapConnector { + com.sun.security.auth.module.Krb5LoginModule required client=TRUE + principal="openidm.example.com@EXAMPLE.COM" + useKeyTab=true keyTab="/path/to/openidm.HTTP.keytab"; +}; +---- +The `principal` and `keyTab` values must match what you have configured in your `authentication.json` file. + + + +[#auth-client-cert] +==== Configuring the CLIENT_CERT Authentication Module + +The `CLIENT_CERT` authentication module compares the subject DN of the client certificate with the subject DN of the OpenIDM truststore. + +The following procedure allows you to review the process with a generated self-signed certificate for the `CLIENT_CERT` module. If you have a `*.pem` file signed by a certificate authority, substitute accordingly. + +In this procedure, you will verify the certificate over port 8444 as defined in your project's `conf/boot/boot.properties` file: + +[source, console] +---- +openidm.auth.clientauthonlyports=8443,8444 +---- + +[#client-cert-demo] +.Demonstrating the CLIENT_CERT Module +==== + +. Generate the self-signed certificate with the following command: ++ + +[source, console] +---- +$ openssl \ + req \ + -x509 \ + -newkey rsa:1024 \ + -keyout key.pem \ + -out cert.pem \ + -days 3650 \ + -nodes +---- + +. Respond to the questions when prompted. ++ + +[source, console] +---- +Country Name (2 letter code) [XX]: +State or Province Name (full name) []: +Locality Name (eg, city) [Default City]: +Name (eg, company) [Default Company Ltd]:ForgeRock +Organizational Unit Name (eg, section) []: +Common Name (eg, your name or your server's hostname) []:localhost +Email Address []: +---- ++ +In this case, the `Name` corresponds to the `O` (for organization) of ForgeRock, and the `Common Name` corresponds to the `cn` of `localhost`. You'll use this information in a couple of steps. + +. Import the certificate `cert.pem` file into the OpenIDM truststore: ++ + +[source, console] +---- +$ keytool \ + -importcert \ + -keystore \ + /path/to/openidm/security/truststore \ + -storetype JKS \ + -storepass changeit \ + -file cert.pem \ + -trustcacerts \ + -noprompt \ + -alias \ + client-cert-example + Certificate was added to keystore +---- + +. Open the `authentication.json` file in the `project-dir/conf` directory. Scroll to the code block with `CLIENT_CERT` and include the information from when you generated the self-signed certificate: ++ + +[source, javascript] +---- +... +{ + "name" : "CLIENT_CERT", + "properties" : { + "queryOnResource" : "security/truststore", + "defaultUserRoles" : [ + "openidm-cert" + ], + "allowedAuthenticationIdPatterns" : [ + "cn=localhost, O=ForgeRock" + ] + }, + "enabled" : true +} +... +---- + +. Start OpenIDM: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p project-dir +---- + +. Send an HTTP request with your certificate file `cert.pem`: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --cert-type PEM \ + --key key.pem \ + --key-type PEM \ + --tlsv1 \ + --cert /path/to/./cert.pem \ + --header "X-OpenIDM-Username: anonymous" \ + --header "X-OpenIDM-Password: anonymous" \ + --request GET \ + "https://localhost:8444/openidm/info/ping" + { + "_id":"", + "state":"ACTIVE_READY", + "shortDesc":"OpenIDM ready" +} +---- + +==== + + + +[#openidm-roles] +=== Roles and Authentication + +OpenIDM includes a number of default roles, and supports the configuration of managed roles, enabling you to customize the roles mechanism as needed. +-- +The following roles are configured by default: + +openidm-reg:: +Role assigned to users who access OpenIDM with the default anonymous account. + ++ +The `openidm-reg` role is excluded from the reauthorization required policy definition by default. + +openidm-admin:: +OpenIDM administrator role, excluded from the reauthorization required policy definition by default. + +openidm-authorized:: +Default role for any user who has authenticated with a user name and password. + +openidm-cert:: +Default role for any user authenticated with mutual SSL authentication. + ++ +This role applies only for mutual authentication. Furthermore, the shared secret (certificate) must be adequately protected. The `openidm-cert` role is excluded from the reauthorization required policy definition by default. + +openidm-tasks-manager:: +Role for users who can be assigned to workflow tasks. + +-- +When a user authenticates, OpenIDM calculates that user's roles as follows: + +* If the authentication module with which the user authenticates includes a `defaultUserRoles` property, OpenIDM assigns those roles to the user on authentication. The `defaultUserRoles` property is specified as an array. + +* The `userRoles` property is a mapping that specifies the attribute or list of attributes in the user entry that contains that specific user's authorization roles. For example, the following excerpt indicates that the `userRoles` should be taken from the user's `authzRoles` property on authentication: ++ + +[source, console] +---- +"userRoles" : "authzRoles" +---- + +* If the authentication module includes a `groupRoleMapping`, `groupMembership`, or `groupComparison` property, OpenIDM can assign additional roles to the user, depending on the user's group membership. + +The roles calculated in sequence are cumulative. + +For users who have authenticated with mutual SSL authentication, the module is `CLIENT_CERT` and the default role for such users is `openidm-cert`. + +[source, javascript] +---- +{ "name" : "CLIENT_CERT", + "properties" : { + "queryOnResource": "security/truststore", + "defaultUserRoles": [ "openidm-cert" ], + "allowedAuthenticationPatterns" : [ ] + }, + "enabled" : "true" +} +---- +Access control for such users is configured in the `access.js` file. For more information, see xref:#openidm-authorization["Authorization"]. + + +[#openidm-authorization] +=== Authorization + +OpenIDM provides role-based authorization that restricts direct HTTP access to REST interface URLs. The default authorization configuration grants different access rights to users that are assigned one or more of the following roles: +[none] +* `"openidm-reg"` +* `"openidm-authorized"` +* `"openidm-admin"` +* `"openidm-cert"` +* `"openidm-tasks-manager"` +Note that this access control applies to direct HTTP calls only. Access for internal calls (for example, calls from scripts) is not affected by this mechanism. + +Authorization roles are referenced in a user's `"authzRoles"` property, and are implemented using the relationships mechanism, described in xref:chap-users-groups-roles.adoc#managing-relationships["Managing Relationships Between Objects"]. + +The following example request shows that user `psmith` has the `"openidm-authorized"` authorization role. + +[source, console] +---- +$ 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/psmith?_fields=authzRoles" +{ + "_id": "psmith", + "_rev": "1", + "authzRoles": [ + { + "_ref": "repo/internal/role/openidm-authorized", + "_refProperties": { + "_id": "8e7b2c97-dfa8-4eec-a95b-b40b710d443d", + "_rev": "1" + } + } + ] +} +---- +The authorization implementation is configured in two script files: + +* `openidm/bin/defaults/script/router-authz.js` + +* `project-dir/script/access.js` + +OpenIDM calls the `router-authz.js` script for each request, through an `onRequest` hook that is defined in the `router.json` file. `router-authz.js` calls your project's access configuration script (`access.js`) to determine the allowed HTTP requests. If access is denied, according to the configuration defined in `access.js`, the `router-authz.js` script throws an exception, and OpenIDM denies the request. + +[#router-authz-js] +==== Understanding the Router Authorization Script (router-authz.js) + +This file provides the functions that enforce access rules. For example, the following function controls whether users with a certain role can start a specified process. + +[source, javascript] +---- +... +function isAllowedToStartProcess() { +var processDefinitionId = request.content._processDefinitionId; +return isProcessOnUsersList(processDefinitionId); +} +... +---- +There are certain authorization-related functions in `router-authz.js` that should __not__ be altered, as indicated in the comments in the file. + + +[#access-js] +==== Understanding the Access Configuration Script (access.js) + +This file defines the access configuration for HTTP requests and references the methods defined in `router-authz.js`. Each entry in the configuration contains a pattern to match against the incoming request ID, and the associated roles, methods, and actions that are allowed for requests on that pattern. + +The options shown in the default version of the file do not include all of the actions available at each endpoint. + +The following sample configuration entry indicates the configurable parameters and their purpose. + +[source, javascript] +---- +{ + "pattern" : "*", + "roles" : "openidm-admin", + "methods" : "*", // default to all methods allowed + "actions" : "*", // default to all actions allowed + "customAuthz" : "disallowQueryExpression()", + "excludePatterns": "system/*" +}, +---- +As shown, this entry affects users with the `openidm-admin` role. Such users have HTTP access to all but `system` endpoints. The parameters are as follows: + +* `"pattern"` - the REST endpoint to which access is being controlled. `"*"` indicates access to all endpoints. `"managed/user/*"` would indicate access to all managed user objects. + +* `"roles"` - a list of the roles to which this access configuration applies. ++ +The `"roles"` referenced here align with the details that are read from an object's security context (`security.authorization.roles`). Managed users use their `"authzRoles"` relationship property to produce this security context value during authentication. + +* `"methods"` - a comma separated list of the methods to which access is being granted. The method can be one or more of `create, read, update, delete, patch, action, query`. A value of `"*"` indicates that all methods are allowed. A value of `""` indicates that no methods are allowed. + +* `"actions"` - a comma separated list of the allowed actions. The possible values depend on the service (URL) that is being exposed. The following list indicates the possible actions for each service. ++ +[none] +* `openidm/info/*` - `(no action parameter applies)` +* `openidm/authentication` - `reauthenticate` +* `openidm/config/ui/*` - `(no action parameter applies)` +* `openidm/endpoint/getprocessforuser` - `create, complete` +* `openidm/endpoint/gettasksview` - `create, complete` +* `openidm/external/email` - `send` +* `openidm/external/rest` - `(no action parameter applies)` +* `openidm/managed` - `patch, triggerSyncCheck` +* `openidm/managed/user` - `validateObject, validateProperty` +* `openidm/policy` - `validateObject, validateProperty` +* `openidm/recon` - `recon, reconByQuery, reconById, cancel` +* `openidm/repo` - `updateDbCredentials` +* `openidm/script/*` - `eval` +* `openidm/security/keystore` - `generateCert, generateCSR` +* `openidm/security/truststore` - `generateCert, generateCSR` +* `openidm/sync` - `notifyCreate, notifyUpdate, notifyDelete, recon, performAction` +* `openidm/system` - `test, testConfig, availableConnectors, createCoreConfig, createFullConfig, liveSync, authenticate` +* `openidm/system/` - `script, test, liveSync` +* `openidm/system//{id}` - `authenticate, liveSync` +* `openidm/taskscanner` - `execute, cancel` +* `openidm/workflow/processdefinition` - `create, complete` +* `openidm/workflow/processinstance` - `create, complete` +* `openidm/workflow/taskinstance` - `claim, create, complete` ++ +A value of `"*"` indicates that all actions exposed for that service are allowed. A value of `""` indicates that no actions are allowed. + +* `"customAuthz"` - an optional parameter that enables you to specify a custom function for additional authorization checks. These functions are defined in `router-authz.js`. + +* `"excludePatterns"` - an optional parameter that enables you to specify particular endpoints to which access should not be given. + + + +[#authorization-extending] +==== Extending the Authorization Mechanism + +You can extend the default authorization mechanism by defining additional functions in `router-authz.js` and by creating new access control configuration definitions in `access.js`. + + + +[#user-group-role-auth-assignment] +=== Building Role-Based Access Control (RBAC) + +In OpenIDM, role assignments can be configured with different authentication options. Roles can be assigned in a number of ways. The roles assigned to specific users are cumulative. + +The roles for each user are calculated based on the process depicted here: + +[#figure-roles-auth] +image::images/roles-auth.png[] +In OpenIDM, RBAC incorporates authentication and authorization options from roles configured for clients, for managed / internal users, as well as for group memberships. + +The properties listed in this section are described in xref:#passthrough-auth["Configuring Pass-Through Authentication"]. + +Roles and authentication options can be configured for users in three stages: +-- + +Client Controlled:: +The `defaultUserRoles` may be added to authentication modules configured in the applicable `authentication.json` file. Default roles are listed in xref:#openidm-roles["Roles and Authentication"]. + ++ +If you see the following entry in `authentication.json`, the cited authentication property applies to all authenticated users: ++ + +[source, console] +---- +"defaultUserRoles" : [ ] +---- + +Managed / Internal:: +Accumulated roles for users are collected in the `userRoles` property. + ++ +For a definition of managed and internal users, see xref:#openidm-users["Authenticating OpenIDM Users"]. + +Group roles:: +OpenIDM also uses group roles as input. Options include `groupMembership`, `groupRoleMapping`, and `groupComparison` + +context.security:: +Once OpenIDM assigns roles and authentication modules to a user, OpenIDM then evaluates the result based on the `context.security` map, based on the scripts in the `policy.js` file. For more information, see xref:#auth-security-context["Roles, Authentication, and the Security Context"]. + +-- + +[#auth-security-context] +==== Roles, Authentication, and the Security Context + +The Security Context (`context.security`), consists of a principal (defined by the `authenticationId` property) and an access control element (defined by the `authorization` property). + +If authentication is successful, the authentication framework sets the principal. OpenIDM stores that principal as the `authenticationId`. For more information, see the authentication components defined in xref:#supported-auth-modules["Supported Authentication Modules"]. + +The `authorization` property includes an `id`, an array of `roles` (see xref:#openidm-roles["Roles and Authentication"]), and a `component`, that specifies the resource against which authorization is validated. For more information, see xref:#passthrough-auth["Configuring Pass-Through Authentication"]. : + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-best-practices.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-best-practices.adoc new file mode 100644 index 000000000..b8f4bbf4a --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-best-practices.adoc @@ -0,0 +1,110 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-best-practices] +== OpenIDM Project Best Practices + +This chapter lists points to check when implementing an identity management solution with OpenIDM. + +[#immplementation-phase] +=== Implementation Phases + +Any identity management project should follow a set of well defined phases, where each phase defines discrete deliverables. The phases take the project from initiation to finally going live with a tested solution. + +[#d0e27169] +==== Initiation + +The project's initiation phase involves identifying and gathering project background, requirements, and goals at a high level. The deliverable for this phase is a statement of work or a mission statement. + + +[#d0e27174] +==== Definition + +In the definition phase, you gather more detailed information on existing systems, determine how to integrate, describe account schemas, procedures, and other information relevant to the OpenIDM deployment. The deliverable for this phase is one or more documents that define detailed requirements for the project, and that cover project definition, the business case, use cases to solve, and functional specifications. +-- +The definition phase should capture at least the following. + +User Administration and Management:: +Procedures for managing users and accounts, who manages users, what processes look like for joiners, movers and leavers, and what is required of OpenIDM to manage users + +Password Management and Password Synchronization:: +Procedures for managing account passwords, password policies, who manages passwords, and what is required of OpenIDM to manage passwords + +Security Policy:: +What security policies defines for users, accounts, passwords, and access control + +Target Systems:: +Target systems and resources with which OpenIDM must integrate. Information such as schema, attribute mappings and attribute transformation flow, credentials and other integration specific information. + +Entitlement Management:: +Procedures to manage user access to resources, individual entitlements, grouping provisioning activities into encapsulated concepts such as roles and groups + +Synchronization and Data Flow:: +Detailed outlines showing how identity information flows from authoritative sources to target systems, attribute transformations required + +Interfaces:: +How to secure the REST, user and file-based interfaces, and to secure the communication protocols involved + +Auditing and Reporting:: +Procedures for auditing and reporting, including who takes responsibility for auditing and reporting, and what information is aggregated and reported. Characteristics of reporting engines provided, or definition of the reporting engine to be integrated. + +Technical Requirements:: +Other technical requirements for the solution such as how to maintain the solution in terms of monitoring, patch management, availability, backup, restore and recovery process. This includes any other components leveraged such as a ConnectorServer and plug-ins for password synchronization on Active Directory, or OpenDJ. + +-- + + +[#d0e27236] +==== Design + +This phase focuses on solution design including on OpenIDM and other components. The deliverables for this phase are the architecture and design documents, and also success criteria with detailed descriptions and test cases to verify when project goals have been met. + + +[#d0e27241] +==== Configure and Test + +This phase configures and tests the solution prior to moving the solution into production. +-- + +Configure a Connector:: +Most deployments include a connection to one or more remote data stores. You should first define all properties for your connector configuration as described in xref:chap-resource-conf.adoc#connectors-with-openidm["Connectors Supported With OpenIDM 4.5"]. + ++ +If you have custom attributes, you can add them as described in: xref:chap-resource-conf.adoc#adding-to-connector-config["Adding Attributes to Connector Configurations"]. + +Test Communication to Remote Data Stores:: +You can then test communication with each remote data store with appropriate REST calls, such as those described in: xref:chap-resource-conf.adoc#systems-over-rest["Checking the Status of External Systems Over REST"]. When your tests succeed, you can have confidence in the way you configured OpenIDM to communicate with your remote data stores. + +Set Up a Mapping:: +You can now set up a mapping between data stores. xref:chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"] includes an extensive discussion of how you can customize a mapping in the `sync.json` file. + +-- +Once complete, you should set up associated custom configuration files in a directory __outside__ of the OpenIDM installation directory (in other words, outside the `/path/to/openidm` directory tree). + + +[#d0e27286] +==== Production + +This phase deploys the solution into production until an application steady state is reached and maintenance routines and procedures can be applied. + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-cli.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-cli.adoc new file mode 100644 index 000000000..e51dd72cf --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-cli.adoc @@ -0,0 +1,530 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-cli] +== OpenIDM Command-Line Interface + +This chapter describes the basic command-line interface provided with OpenIDM. The command-line interface includes a number of utilities for managing an OpenIDM instance. + +All of the utilities are subcommands of the `cli.sh` (UNIX) or `cli.bat` (Windows) scripts. To use the utilities, you can either run them as subcommands, or launch the `cli` script first, and then run the utility. For example, to run the `encrypt` utility on a UNIX system: + +[source, console] +---- +$ cd /path/to/openidm +$ ./cli.sh +Using boot properties at /path/to/openidm/conf/boot/boot.properties +openidm# encrypt .... +---- +or + +[source, console] +---- +$ cd /path/to/openidm +$ ./cli.sh encrypt ... +---- +By default, the command-line utilities run with the properties defined in your project's `conf/boot/boot.properties` file. + +If you run the `cli.sh` command by itself, it opens an OpenIDM-specific shell prompt: + +[source, console] +---- +openidm# +---- +The startup and shutdown scripts are not discussed in this chapter. For information about these scripts, see xref:chap-services.adoc#chap-services["Starting and Stopping OpenIDM"]. + +The following sections describe the subcommands and their use. Examples assume that you are running the commands on a UNIX system. For Windows systems, use `cli.bat` instead of `cli.sh`. + +For a list of subcommands available from the `openidm#` prompt, run the `cli.sh help` command. The `help` and `exit` options shown below are self-explanatory. The other subcommands are explained in the subsections that follow: + +[source, console] +---- +local:keytool Export or import a SecretKeyEntry. + The Java Keytool does not allow for exporting or importing SecretKeyEntries. +local:encrypt Encrypt the input string. +local:secureHash Hash the input string. +local:validate Validates all json configuration files in the configuration + (default: /conf) folder. +basic:help Displays available commands. +basic:exit Exit from the console. +remote:update Update the system with the provided update file. +remote:configureconnector Generate connector configuration. +remote:configexport Exports all configurations. +remote:configimport Imports the configuration set from local file/directory. +---- +The following options are common to the `configexport`, `configimport`, and `configureconnector` subcommands: +-- + +-u or --user USER[:PASSWORD]:: +Allows you to specify the server user and password. Specifying a username is mandatory. If you do not specify a username, the following error is output to the OSGi console: `Remote operation failed: Unauthorized`. If you do not specify a password, you are prompted for one. This option is used by all three subcommands. + +--url URL:: +The URL of the OpenIDM REST service. The default URL is `\http://localhost:8080/openidm/`. This can be used to import configuration files from a remote running instance of OpenIDM. This option is used by all three subcommands. + +-P or --port PORT:: +The port number associated with the OpenIDM REST service. If specified, this option overrides any port number specified with the `--url` option. The default port is 8080. This option is used by all three subcommands. + +-- + +[#cli-configexport] +=== Using the configexport Subcommand + +The `configexport` subcommand exports all configuration objects to a specified location, enabling you to reuse a system configuration in another environment. For example, you can test a configuration in a development environment, then export it and import it into a production environment. This subcommand also enables you to inspect the active configuration of an OpenIDM instance. + +OpenIDM must be running when you execute this command. + +Usage is as follows: + +[source, console] +---- +$ ./cli.sh configexport --user username:password export-location +---- +For example: + +[source, console] +---- +$ ./cli.sh configexport --user openidm-admin:openidm-admin /tmp/conf +---- +On Windows systems, the __export-location__ must be provided in quotation marks, for example: + +[source, console] +---- +C:\openidm\cli.bat configexport --user openidm-admin:openidm-admin "C:\temp\openidm" +---- +Configuration objects are exported as `.json` files to the specified directory. The command creates the directory if needed. Configuration files that are present in this directory are renamed as backup files, with a timestamp, for example, `audit.json.2014-02-19T12-00-28.bkp`, and are not overwritten. The following configuration objects are exported: + +* The internal repository table configuration (`repo.orientdb.json` or `repo.jdbc.json`) and the datasource connection configuration, for JDBC repositories (`datasource.jdbc-default.json`) + +* The script configuration (`script.json`) + +* The log configuration (`audit.json`) + +* The authentication configuration (`authentication.json`) + +* The cluster configuration (`cluster.json`) + +* The configuration of a connected SMTP email server (`external.email.json)` + +* Custom configuration information (`info-name.json`) + +* The managed object configuration (`managed.json`) + +* The connector configuration (`provisioner.openicf-*.json`) + +* The router service configuration (`router.json`) + +* The scheduler service configuration (`scheduler.json`) + +* Any configured schedules (`schedule-*.json`) + +* Standard knowledge-based authentication questions (`selfservice.kba.json)` + +* The synchronization mapping configuration (`sync.json`) + +* If workflows are defined, the configuration of the workflow engine (`workflow.json`) and the workflow access configuration (`process-access.json`) + +* Any configuration files related to the user interface (`ui-*.json`) + +* The configuration of any custom endpoints (`endpoint-*.json`) + +* The configuration of servlet filters (`servletfilter-*.json`) + +* The policy configuration (`policy.json`) + + + +[#cli-configimport] +=== Using the configimport Subcommand + +The `configimport` subcommand imports configuration objects from the specified directory, enabling you to reuse a system configuration from another environment. For example, you can test a configuration in a development environment, then export it and import it into a production environment. + +The command updates the existing configuration from the __import-location__ over the OpenIDM REST interface. By default, if configuration objects are present in the __import-location__ and not in the existing configuration, these objects are added. If configuration objects are present in the existing location but not in the __import-location__, these objects are left untouched in the existing configuration. +-- +The subcommand takes the following options: + +`-r`, `--replaceall`, `--replaceAll`:: +Replaces the entire list of configuration files with the files in the specified import location. + ++ +Note that this option wipes out the existing configuration and replaces it with the configuration in the __import-location__. Objects in the existing configuration that are not present in the __import-location__ are deleted. + +`--retries` (integer):: +New in OpenIDM 4.5.1-20, this option specifies the number of times the command should attempt to update the configuration if OpenIDM is not ready. + ++ +Default value : 10 + +`--retryDelay` (integer):: +New in OpenIDM 4.5.1-20, this option specifies the delay (in milliseconds) between configuration update retries if OpenIDM is not ready. + ++ +Default value : 500 + +-- +Usage is as follows: + +[source, console] +---- +$ ./cli.sh configimport --user username:password [--replaceAll] [--retries integer] [--retryDelay integer] import-location +---- +For example: + +[source, console] +---- +$ ./cli.sh configimport --user openidm-admin:openidm-admin --retries 5 --retryDelay 250 --replaceAll /tmp/conf +---- +On Windows systems, the __import-location__ must be provided in quotation marks, for example: + +[source, console] +---- +C:\openidm\cli.bat configimport --user openidm-admin:openidm-admin --replaceAll "C:\temp\openidm" +---- +Configuration objects are imported as `.json` files from the specified directory to the `conf` directory. The configuration objects that are imported are the same as those for the `export` command, described in the previous section. + + +[#cli-configureconnector] +=== Using the configureconnector Subcommand + +The `configureconnector` subcommand generates a configuration for an OpenICF connector. + +Usage is as follows: + +[source, console] +---- +$ ./cli.sh configureconnector --user username:password --name connector-name +---- +Select the type of connector that you want to configure. The following example configures a new XML connector: + +[source, console] +---- +$ ./cli.sh configureconnector --user openidm-admin:openidm-admin --name myXmlConnector + Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +0. XML Connector version 1.1.0.3 +1. SSH Connector version 1.4.0.0 +2. LDAP Connector version 1.4.1.2 +3. Kerberos Connector version 1.4.0.0 +4. Scripted SQL Connector version 1.4.2.1 +5. Scripted REST Connector version 1.4.2.1 +6. Scripted CREST Connector version 1.4.2.1 +7. Scripted Poolable Groovy Connector version 1.4.2.1 +8. Scripted Groovy Connector version 1.4.2.1 +9. Database Table Connector version 1.1.0.2 +10. CSV File Connector version 1.5.1.4 +11. Exit +Select [0..11]: 0 +Edit the configuration file and run the command again. The configuration was +saved to /openidm/temp/provisioner.openicf-myXmlConnector.json +---- +The basic configuration is saved in a file named `/openidm/temp/provisioner.openicf-connector-name.json`. Edit the `configurationProperties` parameter in this file to complete the connector configuration. For an XML connector, you can use the schema definitions in Sample 1 for an example configuration: + +[source, javascript] +---- +"configurationProperties" : { + "xmlFilePath" : "samples/sample1/data/resource-schema-1.xsd", + "createFileIfNotExists" : false, + "xsdFilePath" : "samples/sample1/data/resource-schema-extension.xsd", + "xsdIcfFilePath" : "samples/sample1/data/xmlConnectorData.xml" + }, +---- +For more information about the connector configuration properties, see xref:chap-resource-conf.adoc#openicf-provisioner-conf["Configuring Connectors"]. + +When you have modified the file, run the `configureconnector` command again so that OpenIDM can pick up the new connector configuration: + +[source, console] +---- +$ ./cli.sh configureconnector --user openidm-admin:openidm-admin --name myXmlConnector +Executing ./cli.sh... +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Configuration was found and read from: /path/to/openidm/temp/provisioner.openicf-myXmlConnector.json +---- +You can now copy the new `provisioner.openicf-myXmlConnector.json` file to the `conf/` subdirectory. + +You can also configure connectors over the REST interface, or through the Admin UI. For more information, see xref:chap-resource-conf.adoc#connector-wiz["Creating Default Connector Configurations"] and xref:chap-resource-conf.adoc#connector-wiz-adminui["Adding New Connectors from the Admin UI"]. + + +[#cli-encrypt] +=== Using the encrypt Subcommand + +The `encrypt` subcommand encrypts an input string, or JSON object, provided at the command line. This subcommand can be used to encrypt passwords, or other sensitive data, to be stored in the OpenIDM repository. The encrypted value is output to standard output and provides details of the cryptography key that is used to encrypt the data. + +Usage is as follows: + +[source, console] +---- +$ ./cli.sh encrypt [-j] string +---- +The `-j` option specifies that the string to be encrypted is a JSON object. If you do not enter the string as part of the command, the command prompts for the string to be encrypted. If you enter the string as part of the command, any special characters, for example quotation marks, must be escaped. + +The following example encrypts a normal string value: + +[source, console] +---- +$ ./cli.sh encrypt mypassword +Executing ./cli.sh +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Activating cryptography service of type: JCEKS provider: location: security/keystore.jceks +Available cryptography key: openidm-sym-default +Available cryptography key: openidm-localhost +CryptoService is initialized with 2 keys. +-----BEGIN ENCRYPTED VALUE----- +{ + "$crypto" : { + "value" : { + "iv" : "M2913T5ZADlC2ip2imeOyg==", + "data" : "DZAAAM1nKjQM1qpLwh3BgA==", + "cipher" : "AES/CBC/PKCS5Padding", + "key" : "openidm-sym-default" + }, + "type" : "x-simple-encryption" + } +} +------END ENCRYPTED VALUE------ +---- +The following example encrypts a JSON object. The input string must be a valid JSON object: + +[source, console] +---- +$ ./cli.sh encrypt -j {\"password\":\"myPassw0rd\"} +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Activating cryptography service of type: JCEKS provider: location: security/keystore.jceks +Available cryptography key: openidm-sym-default +Available cryptography key: openidm-localhost +CryptoService is initialized with 2 keys. +-----BEGIN ENCRYPTED VALUE----- +{ + "$crypto" : { + "value" : { + "iv" : "M2913T5ZADlC2ip2imeOyg==", + "data" : "DZAAAM1nKjQM1qpLwh3BgA==", + "cipher" : "AES/CBC/PKCS5Padding", + "key" : "openidm-sym-default" + }, + "type" : "x-simple-encryption" + } +} +------END ENCRYPTED VALUE------ +---- +The following example prompts for a JSON object to be encrypted. In this case, you do not need to escape the special characters: + +[source, console] +---- +$ ./cli.sh encrypt -j +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Enter the Json value + +> Press ctrl-D to finish input +Start data input: +{"password":"myPassw0rd"} +^D +Activating cryptography service of type: JCEKS provider: location: security/keystore.jceks +Available cryptography key: openidm-sym-default +Available cryptography key: openidm-localhost +CryptoService is initialized with 2 keys. +-----BEGIN ENCRYPTED VALUE----- +{ + "$crypto" : { + "value" : { + "iv" : "6e0RK8/4F1EK5FzSZHwNYQ==", + "data" : "gwHSdDTmzmUXeD6Gtfn6JFC8cAUiksiAGfvzTsdnAqQ=", + "cipher" : "AES/CBC/PKCS5Padding", + "key" : "openidm-sym-default" + }, + "type" : "x-simple-encryption" + } +} +------END ENCRYPTED VALUE------ +---- + + +[#cli-secure-hash] +=== Using the secureHash Subcommand + +The `secureHash` subcommand hashes an input string, or JSON object, using the specified hash algorithm. This subcommand can be used to hash password values, or other sensitive data, to be stored in the OpenIDM repository. The hashed value is output to standard output and provides details of the algorithm that was used to hash the data. + +Usage is as follows: + +[source, console] +---- +$ ./cli.sh secureHash --algorithm [-j] string +---- +The `-a` or `--algorithm` option specifies the hash algorithm to use. OpenIDM supports the following hash algorithms: `MD5`, `SHA-1`, `SHA-256`, `SHA-384`, and `SHA-512`. If you do not specify a hash algorithm, `SHA-256` is used. + +The `-j` option specifies that the string to be hashed is a JSON object. If you do not enter the string as part of the command, the command prompts for the string to be hashed. If you enter the string as part of the command, any special characters, for example quotation marks, must be escaped. + +The following example hashes a password value (`mypassword`) using the `SHA-1` algorithm: + +[source, console] +---- +$ ./cli.sh secureHash --algorithm SHA-1 mypassword +Executing ./cli.sh... +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Activating cryptography service of type: JCEKS provider: location: security/keystore.jceks +Available cryptography key: openidm-sym-default +Available cryptography key: openidm-localhost +CryptoService is initialized with 2 keys. +-----BEGIN HASHED VALUE----- +{ + "$crypto" : { + "value" : { + "algorithm" : "SHA-1", + "data" : "YNBVgtR/jlOaMm01W8xnCBAj2J+x73iFpbhgMEXl7cOsCeWm" + }, + "type" : "salted-hash" + } +} +------END HASHED VALUE------ +---- +The following example hashes a JSON object. The input string must be a valid JSON object: + +[source, console] +---- +$ ./cli.sh secureHash --algorithm SHA-1 -j {\"password\":\"myPassw0rd\"} +Executing ./cli.sh... +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Activating cryptography service of type: JCEKS provider: location: security/keystore.jceks +Available cryptography key: openidm-sym-default +Available cryptography key: openidm-localhost +CryptoService is initialized with 2 keys. +-----BEGIN HASHED VALUE----- +{ + "$crypto" : { + "value" : { + "algorithm" : "SHA-1", + "data" : "ztpt8rEbeqvLXUE3asgA3uf5gJ77I3cED2OvOIxd5bi1eHtG" + }, + "type" : "salted-hash" + } +} +------END HASHED VALUE------ +---- +The following example prompts for a JSON object to be hashed. In this case, you do not need to escape the special characters: + +[source, console] +---- +$ ./cli.sh secureHash --algorithm SHA-1 -j +Using boot properties at /path/to/openidm/conf/boot/boot.properties +Enter the Json value + +> Press ctrl-D to finish input +Start data input: +{"password":"myPassw0rd"} +^D +Activating cryptography service of type: JCEKS provider: location: security/keystore.jceks +Available cryptography key: openidm-sym-default +Available cryptography key: openidm-localhost +CryptoService is initialized with 2 keys. +-----BEGIN HASHED VALUE----- +{ + "$crypto" : { + "value" : { + "algorithm" : "SHA-1", + "data" : "ztpt8rEbeqvLXUE3asgA3uf5gJ77I3cED2OvOIxd5bi1eHtG" + }, + "type" : "salted-hash" + } +} +------END HASHED VALUE------ +---- + + +[#cli-keytool] +=== Using the keytool Subcommand + +The `keytool` subcommand exports or imports secret key values. + +The Java `keytool` command enables you to export and import public keys and certificates, but not secret or symmetric keys. The OpenIDM `keytool` subcommand provides this functionality. + +Usage is as follows: + +[source, console] +---- +$ ./cli.sh keytool [--export, --import] alias +---- +For example, to export the default OpenIDM symmetric key, run the following command: + +[source, console] +---- +$ ./cli.sh keytool --export openidm-sym-default + Using boot properties at /openidm/conf/boot/boot.properties +Use KeyStore from: /openidm/security/keystore.jceks +Please enter the password: +[OK] Secret key entry with algorithm AES +AES:606d80ae316be58e94439f91ad8ce1c0 +---- +The default keystore password is `changeit`. For security reasons, you __must__ change this password in a production environment. For information about changing the keystore password, see xref:chap-security.adoc#security-keystore-password["Change the Default Keystore Password"]. + +To import a new secret key named __my-new-key__, run the following command: + +[source, console] +---- +$ ./cli.sh keytool --import my-new-key +Using boot properties at /openidm/conf/boot/boot.properties +Use KeyStore from: /openidm/security/keystore.jceks +Please enter the password: +Enter the key: +AES:606d80ae316be58e94439f91ad8ce1c0 +---- +If a secret key of that name already exists, OpenIDM returns the following error: + +[source, console] +---- +"KeyStore contains a key with this alias" +---- + + +[#cli-validate] +=== Using the validate Subcommand + +The `validate` subcommand validates all .json configuration files in your project's `conf/` directory. + +Usage is as follows: + +[source, console] +---- +$ ./cli.sh validate +Executing ./cli.sh +Starting shell in /path/to/openidm +Using boot properties at /path/to/openidm/conf/boot/boot.properties +................................................................... +[Validating] Load JSON configuration files from: +[Validating] /path/to/openidm/conf +[Validating] audit.json .................................. SUCCESS +[Validating] authentication.json ......................... SUCCESS + ... +[Validating] sync.json ................................... SUCCESS +[Validating] ui-configuration.json ....................... SUCCESS +[Validating] ui-countries.json ........................... SUCCESS +[Validating] workflow.json ............................... SUCCESS +---- + + +[#cli-update] +=== Using the update Subcommand + +The `update` subcommand supports updates of OpenIDM 4.5 for patches and migrations. For an example of this process, see xref:../install-guide/chap-update.adoc#chap-update["Updating OpenIDM"] in the __Installation Guide__. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-cluster.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-cluster.adoc new file mode 100644 index 000000000..9b61cfc47 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-cluster.adoc @@ -0,0 +1,381 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-cluster] +== Configuring OpenIDM for High Availability + +To ensure high availability of the identity management service, you can deploy multiple OpenIDM systems in a cluster. In a clustered environment, each OpenIDM system must point to the same external repository. If the database is also clustered, OpenIDM points to the cluster as a single system. + +If one OpenIDM system in a cluster shuts down or fails to check in with the cluster management service, a second OpenIDM instance will detect the failure. When configuration is complete, all OpenIDM instances in a cluster are equal. + +For example, if an OpenIDM system named `instance1` loses connectivity while executing a scheduled task, the cluster manager notifies the scheduler service that `instance1` is not available. The scheduler service then attempts to clean up any jobs that `instance1` was running at that time. The scheduler service has the same response for any other clustered OpenIDM system that fails. + +All OpenIDM systems (instances) in a cluster run simultaneously. When configured with a load balancer, it works as an Active-Active High Availability Cluster. + +This chapter describes the changes required to configure multiple instances of OpenIDM in a single cluster. However, it does not specify how you might configure a load balancer. When you run scheduled tasks in a cluster, the different instances claim tasks in a random order. For more information, see xref:#clustering-scheduled-tasks["Managing Scheduled Tasks Across a Cluster"]. + +The following diagram depicts a relatively simple cluster configuration. + +[#figure-cluster] +image::images/ha-cluster-config.png[] + +[IMPORTANT] +==== +A clustered OpenIDM deployment relies on system heartbeats to assess the cluster state. For the heartbeat mechanism to work, you __must__ synchronize the system clocks of all the machines in the cluster using a time synchronization service that runs regularly. The system clocks must be within one second of each other. For information on how you can achieve this using the Network Time Protocol (NTP) daemon, see the link:https://tools.ietf.org/html/rfc7822[NTP RFC, window=\_blank]. +==== +The OpenIDM cluster service is configured in three files: `conf/cluster.json`, `conf/boot/boot.properties`, and `conf/scheduler.json`. When you set up OpenIDM instances in a cluster, you modify these files on each instance. + +[#cluster-failover-concepts] +=== Configuring and Adding to a Cluster + +When you configure a new cluster, you'll designate __one__ OpenIDM system as the `clustered-first` system. + +When you add OpenIDM instances to a cluster, you'll designate them as `clustered-additional` systems, even if you have installed those systems in different geographic locations. + +On the `clustered-first` instance, the Crypto Service activates and generates a new secret key (if not present). The Security Manager activates and generates a new private key (if not present), reloads the keystore within the JVM, and stores the entire keystore in the following file: `security/keystore.jceks`. + +Except for that generation activity, the `clustered-first` instance is functionally equivalent to all `clustered-additional` instances. + +[IMPORTANT] +==== +Do not add a new `clustered-first` system to an existing cluster. If you do, OpenIDM assumes that you are trying to create a new cluster with a "clean" uninitialized repository. +==== +If the `clustered-first` instance of OpenIDM fails, the `clustered-additional` systems take over, and the cluster continues to operate normally. You can replace that `clustered-first` instance with a new `clustered-additional` instance. It gets a copy of the Crypto Service secret key and Security Manager private key from other `clustered-additional` instances. + +The following sections describe how you can configure one `clustered-first` instance and additional `clustered-additional` instances of OpenIDM. + + +[#cluster-config] +=== Configuring an OpenIDM Instance as Part of a Cluster + +Each OpenIDM instance in a cluster must be configured to use the same external repository. Because OrientDB is not supported in production environments, refer to xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__ for instructions on setting up a supported repository. + +OpenIDM provides consistency and concurrency across all instances in a cluster, using multi-version concurrency control (MVCC). MVCC ensures consistency because each instance updates only the particular revision of the object that was specified in the update. + +To configure an individual OpenIDM instance as a part of a clustered deployment, follow these steps. + +. If OpenIDM is running, shut it down using the OSGi console. ++ + +[source, console] +---- +-> shutdown +---- + +. Configure OpenIDM for a supported repository, as described in xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. ++ +Make sure that each database connection configuration file (`datasource.jdbc-default.json`) points to the appropriate port number and IP address for the database. ++ +In that chapter, you should see a reference to a data definition language script file. You need to import that file into just one OpenIDM instance in your cluster. + +. Follow the steps in xref:#cluster-boot-config["Editing the Boot Configuration File"]. + +. Follow the steps in xref:#cluster-config-file["Editing the Cluster Configuration File"]. + +. Follow the steps in xref:#disable-polling-cluster["Disabling Automating Polling of Configuration Changes"]. + +. If your deployment uses scheduled tasks, configure persistent schedules so that jobs and tasks are launched only once across the cluster. For more information, see xref:chap-scheduler-conf.adoc#persistent-schedules["Configuring Persistent Schedules"]. + +. Start each instance of OpenIDM. + +The OpenIDM audit service logs configuration changes only on the modified instance of OpenIDM. Although the cluster service replicates configuration changes to other instances, those changes are not logged. For more information on the audit service, see xref:chap-auditing.adoc#chap-auditing["Using Audit Logs"]. + +[#cluster-boot-config] +==== Editing the Boot Configuration File + +On each OpenIDM instance in your cluster, open the following file: `conf/boot/boot.properties`. + +* Find the `openidm.node.id` property. Specify a unique identifier for each OpenIDM instance. For the primary instance, you might specify the following: ++ + +[source, console] +---- +openidm.node.id=instance1 +---- ++ +For the second OpenIDM instance, you might specify the following; you could then specify `instance3` for the third OpenIDM instance, and so on. ++ + +[source, console] +---- +openidm.node.id=instance2 +---- ++ +You can set any value for `openidm.node.id`, as long as the value is unique within the cluster. The cluster manager detects unavailable OpenIDM instances by node ID. + +* Find the `openidm.instance.type` property. ++ + +** On the __primary__ OpenIDM instance, set `openidm.instance.type` as follows: ++ + +[source] +---- +openidm.instance.type=clustered-first +---- + +** On all other OpenIDM instances in the cluster, set `openidm.instance.type` as follows: ++ + +[source] +---- +openidm.instance.type=clustered-additional +---- + +** If no instance type is specified, the default value for this property is `openidm.instance.type=standalone`, which indicates that the instance will not be part of a cluster. ++ +For a `standalone` instance, the Crypto Service activates and generates a new secret key (if not present). The Security Manager generates a new private key (if not present) and reloads the keystore within the JVM. + + +The value of `openidm.instance.type` is used during the setup process. When the primary OpenIDM instance has been configured, additional nodes are bootstrapped with the security settings (keystore and truststore) of the primary node. Once the process is complete, all OpenIDM instances in the cluster are considered equal. In other words, OpenIDM clusters do not have a "master" node. + +[#cluster-new-key] +===== Clusters and the Security Manager + +On the primary node in a cluster, the Security Manager performs the following tasks: + +* Activates and reads in the keystore from the repository. + +* Overwrites the local keystore. + +* Reloads the keystore within the JVM. + +* Adds `decryptionTransformers` to support key decryption. + +* Calls the Crypto Service to update the `keySelector` with the new keystore. + +To take full advantage of the primary node, run the following `keytool` command to set up a secret key with an alias of `new-sym-key`. This command also stores that key in the `keystore.jceks` file: + +[source, console] +---- +$ keytool \ +-genseckey \ +-alias new-sym-key \ +-keyalg AES \ +-keysize 128 \ +-keystore security/keystore.jceks \ +-storetype JCEKS +---- +Include the __alias__ for the new key in the `conf/boot/boot.properties` file: + +[source, console] +---- +openidm.config.crypto.alias=new-sym-key +---- +and in the `conf/managed.json` file: + +[source, javascript] +---- +{ + "name" : "securityAnswer", + "encryption" : { + "key" : "new-sym-key" + } + "scope" : "private" +}, +{ + "name" : "password", + "encryption" : { + "key" : "new-sym-key" + } + "scope" : "private" +}, +---- +The cluster service replicates the key to the `clustered-additional` nodes. + +For each OpenIDM instance set to `clustered-additional`, the Crypto Service activates, but does not generate, a new secret key. The Crypto Service does not add any `decryptionTransformers`. + +[IMPORTANT] +==== +If you make changes to the keystore and truststore files in clustered environments, shut down all the instances, then make these changes on the `clustered-first` instance while the other instances are down. Then restart the `clustered-first` instance, and __then__ the remaining instances. The `clustered-additional` instances will receive the keystore changes through the repository. If you change the keystore and truststore files on the `clustered-additional` instances, the changes are deleted when these instances are restarted because they read their keystore information from the repository. +==== + + + +[#cluster-config-file] +==== Editing the Cluster Configuration File + +The cluster configuration file is `/path/to/openidm/conf/cluster.json`. The default version of this file accommodates a cluster, as shown with the value of the `enabled` property: + +[source, javascript] +---- +{ + "instanceId" : "&{openidm.node.id}", + "instanceTimeout" : "30000", + "instanceRecoveryTimeout" : "30000", + "instanceCheckInInterval" : "5000", + "instanceCheckInOffset" : "0", + "enabled" : true +} +---- + +* The `instanceId` is set to the value of `openidm.node.id`, as configured in the `conf/boot/boot.properties` file. So it is important to set unique values for `openidm.node.id` for each member of the cluster. + +* The `instanceTimeout` specifies the length of time (in milliseconds) that a member of the cluster can be "down" before the cluster service considers that instance to be in recovery mode. ++ +__Recovery mode__ suggests that the `instanceTimeout` of an OpenIDM instance has expired, and that another OpenIDM instance in the cluster has detected that event. ++ +The scheduler component of the second OpenIDM instance should now be moving any incomplete jobs into the queue for the cluster. + +* The `instanceRecoveryTimeout` specifies the time (in milliseconds) that an OpenIDM instance can be in recovery mode before it is considered to be offline. ++ +This property sets a limit; after this recovery timeout, other members of the cluster stops trying access an unavailable OpenIDM instance. + +* The `instanceCheckInInterval` specifies the frequency (in milliseconds) that this OpenIDM instance checks in with the cluster manager to indicate that it is still online. + +* The `instanceCheckInOffset` specifies an offset (in milliseconds) for the checkin timing, when multiple OpenIDM instances in a cluster are started simultaneously. ++ +The checkin offset prevents multiple OpenIDM instances from checking in simultaneously, which would strain the cluster manager resource. + +* The `enabled` property notes whether or not the clustering service is enabled when you start OpenIDM. Note how this property is set to `true` by default. + +If the default cluster configuration is not suitable for your deployment, edit the `cluster.json` file for each instance. + + +[#disable-polling-cluster] +==== Disabling Automating Polling of Configuration Changes + +On all but one cluster instance, you __must__ disable automatic polling for configuration changes. Open the `conf/system.properties` file on each `clustered-additional` instance and uncomment the following line: + +[source] +---- +# openidm.fileinstall.enabled=false +---- +For more information, see xref:chap-configuration.adoc#disabling-auto-config-updates["Disabling Automatic Configuration Updates"]. As noted in that section, you must have started one OpenIDM instance at least once to ensure that the configuration has been loaded into the repository. + + + +[#clustering-scheduled-tasks] +=== Managing Scheduled Tasks Across a Cluster + +In a clustered environment, the scheduler service looks for pending jobs and handles them as follows: + +* Non-persistent (in-memory) jobs execute on each node in the cluster. + +* Persistent scheduled jobs are picked up and executed by a single node in the cluster. + +* Jobs that are configured as persistent but __not concurrent__ run only on one instance in the cluster. That job will not run again at the scheduled time, on any instance in the cluster, until the current job is complete. ++ +For example, a reconciliation operation that runs for longer than the time between scheduled intervals will not trigger a duplicate job while it is still running. + +OpenIDM instances in a cluster claim jobs in a random order. If one instance fails, the cluster manager automatically reassigns unstarted jobs that were claimed by that failed instance. + +For example, if OpenIDM instance A claims a job but does not start it, and then loses connectivity, OpenIDM instance B can claim that job. + +In contrast, if OpenIDM instance A claims a job, starts it, and then loses connectivity, other OpenIDM instances in the cluster cannot claim that job. That specific job is never completed. Instead, a second OpenIDM instance claims the next scheduled occurrence of that job. + +[NOTE] +==== +This behavior varies from OpenIDM 2.1.0, in which an unavailable OpenIDM instance would have to reconnect to the cluster to free a job that it had already claimed. +==== +You may override this behavior with an external load balancer. + +If a LiveSync operation leads to multiple changes, a single OpenIDM instance process all changes related to that operation. + +[#revising-cluster-tasks] +==== Variations in Scheduled Tasks + +Several elements can change the behavior of how scheduled tasks operate in a cluster, in the following files in the `conf/` subdirectory: `boot.properties`, `scheduler.json`,and `system.properties`. + +[#revising-cluster-boot] +===== Modify an OpenIDM Instance in a Cluster + +Since all nodes in a cluster read their configuration from a single repository, use the `boot.properties` file to define a specific scheduler configuration for each instance. + +You can prevent a specific OpenIDM instance from claiming pending jobs, or participating in processing clustered schedules. To do so in one specific OpenIDM instance, edit its `boot.properties` file and add the following line: + +[source] +---- +execute.clustered.schedules=false +---- +Configure multiple instance in a cluster with the ability to execute persistent schedules. To do so, edit the `boot.properties` file for each instance, and make sure to set: + +[source] +---- +openidm.scheduler.execute.persistent.schedules=true +---- +If the failed instance of OpenIDM did not complete a task, the next action depends on the __misfire policy__, defined in the scheduler configuration. For more information, see `misfirePolicy`. + + + + +[#cluster-over-REST] +=== Managing Nodes Over REST + +You can manage clusters and individual nodes over the REST interface, at the URL `\https://localhost:8443/openidm/cluster/`. The following sample REST commands demonstrate the cluster information that is available over REST. + +[#d0e26550] +.Displaying the Nodes in the Cluster +==== +The following REST request displays the nodes configured in the cluster, and their status. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/cluster" + +{ + "results": [ + { + "state" : "running", + "instanceId" : "instance2", + "startup" : "2015-08-28T12:50:37.209-07:00", + "shutdown" : "" + }, + { + "state" : "running", + "instanceId" : "instance1", + "startup" : "2015-08-28T11:33:12.650-07:00", + "shutdown" : "" + } + ] +} +---- +==== + +[#d0e26564] +.Checking the State of an Individual Node +==== +To check the status of a specific node, include its node ID in the URL, for example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/cluster/instance1" +{ + "state" : "running", + "instanceId" : "instance1", + "startup" : "2015-08-28T11:33:12.650-07:00", + "shutdown" : "" +} +---- +==== + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc new file mode 100644 index 000000000..0df04c546 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc @@ -0,0 +1,768 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-configuration] +== Configuring OpenIDM + +OpenIDM configuration is split between `.properties` and container configuration files, and also dynamic configuration objects. Most of OpenIDM's configuration files are stored in your project's `conf/` directory, as described in xref:appendix-file-layout.adoc#appendix-file-layout["File Layout"]. + +OpenIDM stores configuration objects in its internal repository. You can manage the configuration by using REST access to the configuration objects, or by using the JSON file-based views. Several aspects of the configuration can also be managed by using the Admin UI, as described in xref:chap-ui.adoc#ui-admin["Configuring OpenIDM from the Admin UI"]. + +[#configuration-objects] +=== OpenIDM Configuration Objects + +OpenIDM exposes internal configuration objects in JSON format. Configuration elements can be either single instance or multiple instance for an OpenIDM installation. + +[#single-instance-configuration-objects] +==== Single Instance Configuration Objects + +Single instance configuration objects correspond to services that have at most one instance per installation. JSON file views of these configuration objects are named `object-name.json`. +The following list describes the single instance configuration objects: + +* The `audit` configuration specifies how audit events are logged. + +* The `authentication` configuration controls REST access. + +* The `cluster` configuration defines how one OpenIDM instance can be configured in a cluster. + +* The `endpoint` configuration controls any custom REST endpoints. + +* The `info` configuration points to script files for the customizable information service. + +* The `managed` configuration defines managed objects and their schemas. + +* The `policy` configuration defines the policy validation service. + +* The `process access` configuration defines access to configured workflows. + +* The `repo.repo-type` configuration such as `repo.orientdb` or `repo.jdbc` configures the internal repository. + +* The `router` configuration specifies filters to apply for specific operations. + +* The `script` configuration defines the parameters that are used when compiling, debugging, and running JavaScript and Groovy scripts. + +* The `sync` configuration defines the mappings that OpenIDM uses when it synchronizes and reconciles managed objects. + +* The `ui` configuration defines the configurable aspects of the default user interfaces. + +* The `workflow` configuration defines the configuration of the workflow engine. + +OpenIDM stores managed objects in the repository, and exposes them under `/openidm/managed`. System objects on external resources are exposed under `/openidm/system`. + +The following image shows the paths to objects in the OpenIDM namespace. + +[#d0e5734] +image::images/ServiceTree.png[] + + +[#multiple-instance-configuration-objects] +==== Multiple Instance Configuration Objects + +Multiple instance configuration objects correspond to services that can have many instances per installation. Multiple instance configuration objects are named `objectname/instancename`, for example, `provisioner.openicf/xml`. + +__JSON file__ views of these configuration objects are named `objectname-instancename.json`, for example, `provisioner.openicf-xml.json.` +OpenIDM provides the following multiple instance configuration objects: + +* Multiple `schedule` configurations can run reconciliations and other tasks on different schedules. + +* Multiple `provisioner.openicf` configurations correspond to the resources connected to OpenIDM. + +* Multiple `servletfilter` configurations can be used for different servlet filters such as the Cross Origin and GZip filters. + + + + +[#changing-configuration] +=== Changing the Default Configuration + +When you change OpenIDM's configuration objects, take the following points into account: + +* 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. ++ +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. + +* OpenIDM recognizes changes to JSON files when it is running. OpenIDM __must__ be running when you delete configuration objects, even if you do so by editing the JSON files. + +* 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. + +* 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 __persistent configuration__ in the `conf/system.properties` file by uncommenting the following line: ++ + +[source] +---- +# openidm.config.repo.enabled=false +---- ++ +Disabling persistent configuration means that OpenIDM will store its configuration in memory only. You should not disable persistent configuration in a production environment. + + + +[#configuring-for-production] +=== Configuring an OpenIDM System for Production + +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. + +[#configuring-production-repo] +==== Configuring a Production Repository + +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. + +For more information, see xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. + + +[#disabling-auto-config-updates] +==== Disabling Automatic Configuration Updates + +By default, OpenIDM polls the JSON files in the `conf` 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. + +To disable automatic polling for configuration changes, edit the `conf/system.properties` file for your project, and uncomment the following line: + +[source] +---- +# openidm.fileinstall.enabled=false +---- +This setting also disables the file-based configuration view, which means that OpenIDM reads its configuration only from the repository. + +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. Be aware, if automatic polling is enabled, OpenIDM immediately uses changes to scripts called from a JSON configuration file. + +When your configuration is complete, you can disable writes to configuration files. To do so, add the following line to the `conf/config.properties` file for your project: + +[source] +---- +felix.fileinstall.enableConfigSave=false +---- + + +[#configuring-proxy] +==== Communicating Through a Proxy Server + +To set up OpenIDM to communicate through a proxy server, you need to use JVM parameters that identify the proxy host system, and the OpenIDM port number. + +If you've configured OpenIDM behind a proxy server, include JVM properties from the following table, in the OpenIDM startup script: + +[#jvm-proxy-properties] +.JVM Proxy Properties +[cols="20%,40%,40%"] +|=== +|JVM Property |Example Values |Description + +a|`-Dhttps.proxyHost` +a|proxy.example.com, 192.168.0.1 +a|Hostname or IP address of the proxy server + +a|`-Dhttps.proxyPort` +a|8443, 9443 +a|Port number used by OpenIDM +|=== +If an insecure port is acceptable, you can also use the `-Dhttp.proxyHost` and `-Dhttp.proxyPort` options. You can add these JVM proxy properties to the value of `OPENIDM_OPTS` in your startup script (`startup.sh` or `startup.bat`): + +[source] +---- +# Only set OPENIDM_OPTS if not already set +[ -z "$OPENIDM_OPTS" ] && OPENIDM_OPTS="-Xmx1024m -Xms1024m -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8443" +---- + + + +[#configuring-over-rest] +=== Configuring OpenIDM Over REST + +OpenIDM exposes configuration objects under the `/openidm/config` context path. +You can list the configuration on the local host by performing a GET `\https://localhost:8443/openidm/config`. The examples shown in this section are based on first OpenIDM sample, described in xref:../samples-guide/chap-xml-samples.adoc#more-sample-1["First OpenIDM Sample - Reconciling an XML File Resource"] in the __Samples Guide__. + +The following REST call includes excerpts of the default configuration for an OpenIDM instance started with Sample 1: + +[source, console] +---- +$ curl \ + --request GET \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --cacert self-signed.crt \ + https://localhost:8443/openidm/config +{ + "_id" : "", + "configurations" : [ { + "_id" : "endpoint/usernotifications", + "pid" : "endpoint.95b46fcd-f0b7-4627-9f89-6f3180c826e4", + "factoryPid" : "endpoint" + }, { + "_id" : "router", + "pid" : "router", + "factoryPid" : null + }, + ... + { + "_id" : "endpoint/reconResults", + "pid" : "endpoint.ad3f451c-f34e-4096-9a59-0a8b7bc6989a", + "factoryPid" : "endpoint" + }, { + "_id" : "endpoint/gettasksview", + "pid" : "endpoint.bc400043-f6db-4768-92e5-ebac0674e201", + "factoryPid" : "endpoint" + }, + ... + { + "_id" : "workflow", + "pid" : "workflow", + "factoryPid" : null + }, { + "_id" : "ui.context/selfservice", + "pid" : "ui.context.537a5838-217b-4f67-9301-3fde19a51784", + "factoryPid" : "ui.context" + } ] +} +---- +Single instance configuration objects are located under `openidm/config/object-name`. The following example shows the Sample 1 `audit` configuration: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + "https://localhost:8443/openidm/config/audit" +{ + "_id" : "audit", + "auditServiceConfig" : { + "handlerForQueries" : "repo", + "availableAuditEventHandlers" : [ + "org.forgerock.audit.handlers.csv.CsvAuditEventHandler", + "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "org.forgerock.openidm.audit.impl.RouterAuditEventHandler" + ], + "filterPolicies" : { + "value" : { + "excludeIf" : [ + "/access/http/request/headers/Authorization", + "/access/http/request/headers/X-OpenIDM-Password", + "/access/http/request/cookies/session-jwt", + "/access/http/response/headers/Authorization", + "/access/http/response/headers/X-OpenIDM-Password" + ], + "includeIf" : [ ] + } + } + }, + "eventHandlers" : [ { + "class" : "org.forgerock.audit.handlers.csv.CsvAuditEventHandler", + "config" : { + "name" : "csv", + "logDirectory" : "/root/openidm/audit", + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ] + } + }, { + "class" : "org.forgerock.openidm.audit.impl.RepositoryAuditEventHandler", + "config" : { + "name" : "repo", + "topics" : [ "access", "activity", "recon", "sync", "authentication", "config" ] + } + } ], + "eventTopics" : { + "config" : { + "filter" : { + "actions" : [ "create", "update", "delete", "patch", "action" ] + } + }, + "activity" : { + "filter" : { + "actions" : [ "create", "update", "delete", "patch", "action" ] + }, + "watchedFields" : [ ], + "passwordFields" : [ "password" ] + } + }, + "exceptionFormatter" : { + "type" : "text/javascript", + "file" : "bin/defaults/script/audit/stacktraceFormatter.js" + } +} +---- +Multiple instance configuration objects are found under `openidm/config/object-name/instance-name`. + +The following example shows the configuration for the XML connector provisioner shown in the first OpenIDM sample. The output has been cropped for legibility: + +[source, console] +---- +$ 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" +{ + "_id" : "provisioner.openicf/xml", + "name" : "xmlfile", + "connectorRef" : { + "bundleName" : "org.forgerock.openicf.connectors.xml-connector", + "bundleVersion" : "1.1.0.2", + "connectorName" : "org.forgerock.openicf.connectors.xml.XMLConnector" + }, + ... + "configurationProperties" : { + "xsdIcfFilePath" : "/root/openidm/samples/sample1/data/resource-schema-1.xsd", + "xsdFilePath" : "/root/openidm/samples/sample1/data/resource-schema-extension.xsd", + "xmlFilePath" : "/root/openidm/samples/sample1/data/xmlConnectorData.xml" + }, + "syncFailureHandler" : { + "maxRetries" : 5, + "postRetryAction" : "logged-ignore" + }, + "objectTypes" : { + "account" : { + "$schema" : "http://json-schema.org/draft-03/schema", + "id" : "__ACCOUNT__", + "type" : "object", + "nativeType" : "__ACCOUNT__", + "properties" : { + "description" : { + "type" : "string", + "nativeName" : "__DESCRIPTION__", + "nativeType" : "string" + }, + ... + "roles" : { + "type" : "string", + "required" : false, + "nativeName" : "roles", + "nativeType" : "string" + } + } + } + }, + "operationOptions" : { } +} +---- +You can change the configuration over REST by using an HTTP PUT or HTTP PATCH request to modify the required configuration object. + +The following example uses a PUT request to modify the configuration of the scheduler service, increasing the maximum number of threads that are available for the concurrent execution of scheduled tasks: + +[source, console] +---- +$ 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 '{ + "threadPool": { + "threadCount": "20" + }, + "scheduler": { + "executePersistentSchedules": "&{openidm.scheduler.execute.persistent.schedules}" + } +}' \ + "https://localhost:8443/openidm/config/scheduler" +{ + "_id" : "scheduler", + "threadPool": { + "threadCount": "20" + }, + "scheduler": { + "executePersistentSchedules": "true" + } +} +---- +The following example uses a PATCH request to reset the number of threads to their original value. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { + "operation" : "replace", + "field" : "/threadPool/threadCount", + "value" : "10" + } + ]' \ + "https://localhost:8443/openidm/config/scheduler" +{ + "_id": "scheduler", + "threadPool": { + "threadCount": "10" + }, + "scheduler": { + "executePersistentSchedules": "true" + } +} +---- +For more information about using the REST API to update objects, see xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + + +[#using-property-substitution] +=== Using Property Value Substitution In the Configuration + +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. +Property substitution enables you to achieve the following: + +* Define a configuration that is specific to a single OpenIDM instance, for example, setting the location of the keystore on a particular host. + +* Define a configuration whose parameters vary between different environments, for example, the URLs and passwords for test, development, and production environments. + +* Disable certain capabilities on specific nodes. For example, you might want to disable the workflow engine on specific instances. + +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. + +Property references are contained within the construct `&{ }`. When such references are found, OpenIDM replaces them with the appropriate property value, defined in the `boot.properties` file. + +[#d0e6067] +.Using Separate OpenIDM Environments +==== +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. + +The environments are defined by adding the following lines to the `conf/boot.properties` file: + +[source, javascript] +---- +PROD.location=production +DEV.location=development +---- +The database URL is then specified as follows in the `repo.orientdb.json` file: + +[source, javascript] +---- +{ + "dbUrl" : "plocal:./db/&{&{environment}.location}-openidm", + ... +} +---- +The effective database URL is determined by setting the `OPENIDM_OPTS` environment variable when you start OpenIDM. To use the production environment, start OpenIDM as follows: + +[source, console] +---- +$ export OPENIDM_OPTS="-Xmx1024m -Xms1024m -Denvironment=PROD" +$ ./startup.sh +---- +To use the development environment, start OpenIDM as follows: + +[source, console] +---- +$ export OPENIDM_OPTS="-Xmx1024m -Xms1024m -Denvironment=DEV" +$ ./startup.sh +---- +==== + +[#property-substitution-system] +==== Using Property Value Substitution With System Properties + +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. + +[#custom-audit-log-location] +.Custom Audit Log Location +==== +The following example modifies the `audit.json` file so that the log file is written to the user's directory. The `user.home` property is a default Java System property: + +[source, javascript] +---- +{ + "logTo" : [ + { + "logType" : "csv", + "location" : "&{user.home}/audit" + } + ] +} +---- +==== +You can define __nested__ properties (that is a property definition within another property definition) and you can combine system properties and boot properties. + +[#d0e6125] +.Defining Different Ports in the Configuration +==== +The following example uses the `user.country` property, a default Java system property. The example defines specific LDAP ports, depending on the country (identified by the country code) in the `boot.properties` file. The value of the LDAP port (set in the `provisioner.openicf-ldap.json` file) depends on the value of the `user.country` system property. + +The port numbers are defined in the `boot.properties` file as follows: + +[source, javascript] +---- +openidm.NO.ldap.port=2389 +openidm.EN.ldap.port=3389 +openidm.US.ldap.port=1389 +---- +The following excerpt of the `provisioner.openicf-ldap.json` file shows how the value of the LDAP port is eventually determined, based on the system property: + +[source, javascript] +---- +"configurationProperties" : + { + "credentials" : "Passw0rd", + "port" : "&{openidm.&{user.country}.ldap.port}", + "principal" : "cn=Directory Manager", + "baseContexts" : + [ + "dc=example,dc=com" + ], + "host" : "localhost" + } +---- +==== + + +[#property-substitution-limitations] +==== Limitations of Property Value Substitution + +Note the following limitations when you use property value substitution: + +* You cannot reference complex objects or properties with syntaxes other than string. Property values are resolved from the `boot.properties` file or from the system properties and the value of these properties is always in string format. ++ +Property substitution of boolean values is currently only supported in stringified format, that is, resulting in `"true"` or `"false"`. + +* Substitution of encrypted property values is not supported. + + + + +[#script-config] +=== Setting the Script Configuration + +The script configuration file (`conf/script.json`) enables you to modify the parameters that are used when compiling, debugging, and running JavaScript and Groovy scripts. + +The default `script.json` file includes the following parameters: +-- + +properties:: +Any custom properties that should be provided to the script engine. + +ECMAScript:: +Specifies JavaScript debug and compile options. JavaScript is an ECMAScript language. ++ + +* `javascript.recompile.minimumInterval` - minimum time after which a script can be recompiled. ++ +The default value is `60000`, or 60 seconds. This means that any changes made to scripts will not get picked up for up to 60 seconds. If you are developing scripts, reduce this parameter to around `100` (100 milliseconds). + + +Groovy:: +Specifies compilation and debugging options related to Groovy scripts. Many of these options are commented out in the default script configuration file. Remove the comments to set these properties: ++ + +* `groovy.warnings` - the log level for Groovy scripts. Possible values are `none`, `likely`, `possible`, and `paranoia`. + +* `groovy.source.encoding` - the encoding format for Groovy scripts. Possible values are `UTF-8` and `US-ASCII`. + +* `groovy.target.directory` - the directory to which compiled Groovy classes will be output. The default directory is `install-dir/classes`. + +* `groovy.target.bytecode` - the bytecode version that is used to compile Groovy scripts. The default version is `1.5`. + +* `groovy.classpath` - the directory in which the compiler should look for compiled classes. The default classpath is `install-dir/lib`. ++ +To call an external library from a Groovy script, you must specify the complete path to the .jar file or files, as a value of this property. For example: ++ + +[source, console] +---- +"groovy.classpath" : "/&{launcher.install.location}/lib/http-builder-0.7.1.jar: + /&{launcher.install.location}/lib/json-lib-2.3-jdk15.jar: + /&{launcher.install.location}/lib/xml-resolver-1.2.jar: + /&{launcher.install.location}/lib/commons-collections-3.2.1.jar", +---- + +* `groovy.output.verbose` - specifies the verbosity of stack traces. Boolean, `true` or `false`. + +* `groovy.output.debug` - specifies whether debugging messages are output. Boolean, `true` or `false`. + +* `groovy.errors.tolerance` - sets the number of non-fatal errors that can occur before a compilation is aborted. The default is `10` errors. + +* `groovy.script.extension` - specifies the file extension for Groovy scripts. The default is `.groovy`. + +* `groovy.script.base` - defines the base class for Groovy scripts. By default any class extends `groovy.lang.Script`. + +* `groovy.recompile` - indicates whether scripts can be recompiled. Boolean, `true` or `false`, with default `true`. + +* `groovy.recompile.minimumInterval` - sets the minimum time between which Groovy scripts can be recompiled. ++ +The default value is `60000`, or 60 seconds. This means that any changes made to scripts will not get picked up for up to 60 seconds. If you are developing scripts, reduce this parameter to around `100` (100 milliseconds). + +* `groovy.target.indy` - specifies whether a link:http://docs.groovy-lang.org/latest/html/documentation/invokedynamic-support.html[Groovy indy test, window=\_blank] can be used. Boolean, `true` or `false`, with default `true`. + +* `groovy.disabled.global.ast.transformations` - specifies a list of disabled Abstract Syntax Transformations (ASTs). + + +sources:: +Specifies the locations in which OpenIDM expects to find JavaScript and Groovy scripts that are referenced in the configuration. + ++ +The following excerpt of the `script.json` file shows the default locations: ++ + +[source] +---- +... +"sources" : { + "default" : { + "directory" : "&{launcher.install.location}/bin/defaults/script" + }, + "install" : { + "directory" : "&{launcher.install.location}" + }, + "project" : { + "directory" : "&{launcher.project.location}" + }, + "project-script" : { + "directory" : "&{launcher.project.location}/script" + } +... +---- ++ + +[NOTE] +====== +The order in which locations are listed in the `sources` property is important. Scripts are loaded from the __bottom up__ in this list, that is, scripts found in the last location on the list are loaded first. +====== + +-- + +[NOTE] +==== +By default, debug information (such as file name and line number) is excluded from JavaScript exceptions. To troubleshoot script exceptions, you can include debug information by changing the following setting to `true` in your project's `conf/boot/boot.properties` file: + +[source] +---- +javascript.exception.debug.info=false +---- +Including debug information in a production environment is not recommended. +==== + + +[#script-call] +=== Calling a Script From a Configuration File + +You can call a script from within a configuration file by providing the script source, or by referencing a file that contains the script source. For example: + +[source, javascript] +---- +{ + "type" : "text/javascript", + "source": string +} +---- +or + +[source, javascript] +---- +{ + "type" : "text/javascript", + "file" : file location +} +---- +-- + +type:: +string, required + ++ +Specifies the type of script to be executed. Supported types include `text/javascript`, and `groovy`. + +source:: +string, required if `file` is not specified + ++ +Specifies the source code of the script to be executed. + +file:: +string, required if `source` is not specified + ++ +Specifies the file containing the source code of the script to execute. + +-- +The following sample excerpts from configuration files indicate how scripts can be called. + +The following example (included in the `sync.json` file) returns `true` if the `employeeType` is equal to `external`, otherwise returns `false`. This script can be useful during reconciliation to establish whether a target object should be included in the reconciliation process, or should be ignored: + +[source, javascript] +---- +"validTarget": { + "type" : "text/javascript", + "source": "target.employeeType == 'external'" +} +---- +The following example (included in the `sync.json` file) sets the `__PASSWORD__` attribute to `defaultpwd` when OpenIDM creates a target object: + +[source, javascript] +---- +"onCreate" : { + "type" : "text/javascript", + "source": "target.__PASSWORD__ = 'defaultpwd'" +} +---- +The following example (included in the `router.json` file) shows a trigger to create Solaris home directories using a script. The script is located in the file, `project-dir/script/createUnixHomeDir.js`: + +[source, javascript] +---- +{ + "filters" : [ { + "pattern" : "^system/solaris/account$", + "methods" : [ "create" ], + "onResponse" : { + "type" : "text/javascript", + "file" : "script/createUnixHomeDir.js" + } + } ] +} +---- +Often, script files are reused in different contexts. You can pass variables to your scripts to provide these contextual details at runtime. You pass variables to the scripts that are referenced in configuration files by declaring the variable name in the script reference. + +The following example of a scheduled task configuration calls a script named `triggerEmailNotification.js`. The example sets the sender and recipient of the email in the schedule configuration, rather than in the script itself: + +[source, javascript] +---- +{ + "enabled" : true, + "type" : "cron", + "schedule" : "0 0/1 * * * ?", + "invokeService" : "script", + "invokeContext" : { + "script": { + "type" : "text/javascript", + "file" : "script/triggerEmailNotification.js", + "fromSender" : "admin@example.com", + "toEmail" : "user@example.com" + } + } +} +---- + +[TIP] +==== +In general, you should namespace variables passed into scripts with the `globals` map. Passing variables in this way prevents collisions with the top-level reserved words for script maps, such as `file`, `source`, and `type`. The following example uses the `globals` map to namespace the variables passed in the previous example. + +[source, javascript] +---- +"script": { + "type" : "text/javascript", + "file" : "script/triggerEmailNotification.js", + "globals" : { + "fromSender" : "admin@example.com", + "toEmail" : "user@example.com" + } +} +---- +==== +Script variables are not necessarily simple `key:value` pairs. A script variable can be any arbitrarily complex JSON object. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-data.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-data.adoc new file mode 100644 index 000000000..2037839f8 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-data.adoc @@ -0,0 +1,916 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-data] +== Accessing Data Objects + +OpenIDM supports a variety of objects that can be addressed via a URL or URI. You can access data objects by using scripts (through the Resource API) or by using direct HTTP calls (through the REST API). + +The following sections describe these two methods of accessing data objects, and provide information on constructing and calling data queries. + +[#data-scripts] +=== Accessing Data Objects By Using Scripts + +OpenIDM's uniform programming model means that all objects are queried and manipulated in the same way, using the Resource API. The URL or URI that is used to identify the target object for an operation depends on the object type. For an explanation of object types, see xref:appendix-objects.adoc#appendix-objects["Data Models and Objects Reference"]. For more information about scripts and the objects available to scripts, see xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"]. + +You can use the Resource API to obtain managed, system, configuration, and repository objects, as follows: + +[source, javascript] +---- +val = openidm.read("managed/organization/mysampleorg") +val = openidm.read("system/mysystem/account") +val = openidm.read("config/custom/mylookuptable") +val = openidm.read("repo/custom/mylookuptable") +---- +For information about constructing an object ID, see xref:appendix-rest.adoc#rest-uri-scheme["URI Scheme"]. + +You can update entire objects with the `update()` function, as follows: + +[source, javascript] +---- +openidm.update("managed/organization/mysampleorg", object) +openidm.update("system/mysystem/account", object) +openidm.update("config/custom/mylookuptable", object) +openidm.update("repo/custom/mylookuptable", object) +---- +You can apply a partial update to a managed or system object by using the `patch()` function: + +[source, javascript] +---- +openidm.patch("managed/organization/mysampleorg", rev, value) +---- +The `create()`, `delete()`, and `query()` functions work the same way. + + +[#data-rest] +=== Accessing Data Objects By Using the REST API + +OpenIDM provides RESTful access to data objects via ForgeRock's Common REST API. To access objects over REST, you can use a browser-based REST client, such as the Simple REST Client for Chrome, or RESTClient for Firefox. Alternatively you can use the link:http://curl.haxx.se/[curl, window=\_top] command-line utility. + +For a comprehensive overview of the REST API, see xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + +To obtain a managed object through the REST API, depending on your security settings and authentication configuration, perform an HTTP GET on the corresponding URL, for example `\https://localhost:8443/openidm/managed/organization/mysampleorg`. + +By default, the HTTP GET returns a JSON representation of the object. + +In general, you can map any HTTP request to the corresponding `openidm.method` call. The following example shows how the parameters provided in an `openidm.query` request correspond with the key-value pairs that you would include in a similar HTTP GET request: + +Reading an object using the Resource API: + +[source, console] +---- +openidm.query("managed/user", { "_queryId": "query-all-ids" }, ["userName","sn"]) +---- +Reading an object using the REST API: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids&_fields=userName,sn" +---- + + +[#queries] +=== Defining and Calling Queries + +OpenIDM supports an advanced query model that enables you to define queries, and to call them over the REST or Resource API. Three types of queries are supported, on both managed, and system objects: + +* Common filter expressions + +* Parameterized, or predefined queries + +* Native query expressions + +Each of these mechanisms is discussed in the following sections. + +[#query-filters] +==== Common Filter Expressions + +The ForgeRock REST API defines common filter expressions that enable you to form arbitrary queries using a number of supported filter operations. This query capability is the standard way to query data if no predefined query exists, and is supported for all managed and system objects. + +Common filter expressions are useful in that they do not require knowledge of how the object is stored and do not require additions to the repository configuration. + +Common filter expressions are called with the `_queryFilter` keyword. The following example uses a common filter expression to retrieve managed user objects whose user name is Smith: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + 'https://localhost:8443/openidm/managed/user?_queryFilter=userName+eq+"smith"' +---- +The filter is URL encoded in this example. The corresponding filter using the resource API would be: + +[source, console] +---- +openidm.query("managed/user", { "_queryFilter" : '/userName eq "smith"' }); +---- +Note that, this JavaScript invocation is internal and is not subject to the same URL-encoding requirements that a GET request would be. Also, because JavaScript supports the use of single quotes, it is not necessary to escape the double quotes in this example. + +For a list of supported filter operations, see xref:#constructing-queries["Constructing Queries"]. + +Note that using common filter expressions to retrieve values from arrays is currently not supported. If you need to search within an array, you should set up a predefined (parameterized) in your repository configuration. For more information, see xref:#parameterized-queries["Parameterized Queries"]. + + +[#parameterized-queries] +==== Parameterized Queries + +Managed objects in the supported OpenIDM repositories can be accessed using a parameterized query mechanism. Parameterized queries on repositories are defined in the repository configuration (`repo.*.json`) and are called by their `_queryId`. + +Parameterized queries provide precise control over the query that is executed. Such control might be useful for tuning, or for performing database operations such as aggregation (which is not possible with a common filter expression.) + +Parameterized queries provide security and portability for the query call signature, regardless of the backend implementation. Queries that are exposed over the REST interface __must__ be parameterized queries to guard against injection attacks and other misuse. Queries on the officially supported repositories have been reviewed and hardened against injection attacks. + +For system objects, support for parameterized queries is restricted to `_queryId=query-all-ids`. There is currently no support for user-defined parameterized queries on system objects. Typically, parameterized queries on system objects are not called directly over the REST interface, but are issued from internal calls, such as correlation queries. + +A typical query definition is as follows: + +[source] +---- +"query-all-ids" : "select _openidm_id from ${unquoted:_resource}" +---- +To call this query, you would reference its ID, as follows: + +[source] +---- +?_queryId=query-all-ids +---- +The following example calls `query-all-ids` over the REST interface: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + "https://localhost:8443/openidm/managed/user?_queryId=query-all-ids" +---- + + +[#native-queries] +==== Native Query Expressions + +Native query expressions are supported for all managed objects and system objects, and can be called directly, rather than being defined in the repository configuration. + +Native queries are intended specifically for internal callers, such as custom scripts, and should be used only in situations where the common filter or parameterized query facilities are insufficient. For example, native queries are useful if the query needs to be generated dynamically. + +The query expression is specific to the target resource. For repositories, queries use the native language of the underlying data store. For system objects that are backed by OpenICF connectors, queries use the applicable query language of the system resource. + +Native queries on the repository are made using the `_queryExpression` keyword. For example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + "https://localhost:8443/openidm/managed/user?_queryExpression=select+from+managed_user" +---- +Unless you have specifically enabled native queries over REST, the previous command returns a 403 access denied error message. Native queries are not portable and do not guard against injection attacks. Such query expressions should therefore not be used or made accessible over the REST interface or over HTTP in production environments. They should be used only via the internal Resource API. If you want to enable native queries over REST for development, see xref:chap-security.adoc#security-urls["Protect Sensitive REST Interface URLs"]. + +Alternatively, if you really need to expose native queries over HTTP, in a selective manner, you can design a custom endpoint to wrap such access. + + +[#constructing-queries] +==== Constructing Queries + +The `openidm.query` function enables you to query OpenIDM managed and system objects. The query syntax is `openidm.query(id, params)`, where `id` specifies the object on which the query should be performed and `params` provides the parameters that are passed to the query, either `_queryFilter` or `_queryID`. For example: + +[source, javascript] +---- +var params = { + '_queryFilter' : 'givenName co "' + sourceCriteria + '" or ' + 'sn co "' + sourceCriteria + '"' +}; +var results = openidm.query("system/ScriptedSQL/account", params) +---- +Over the REST interface, the query filter is specified as `_queryFilter=filter`, for example: + +[source, console] +---- +$ 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?_queryFilter=userName+eq+"Smith"' +---- +Note the use of double-quotes around the search term: `Smith`. In `_queryFilter` expressions, string values __must__ use double-quotes. Numeric and boolean expressions should not use quotes. + +When called over REST, you must URL encode the filter expression. The following examples show the filter expressions using the resource API and the REST API, but do not show the URL encoding, to make them easier to read. + +Note that, for generic mappings, any fields that are included in the query filter (for example `userName` in the previous query), must be explicitly defined as __searchable__, if you have set the global `searchableDefault` to false. For more information, see xref:chap-repo.adoc#searches-with-generic-mappings["Improving Search Performance for Generic Mappings"]. + +The __filter__ expression is constructed from the building blocks shown in this section. In these expressions the simplest __json-pointer__ is a field of the JSON resource, such as `userName` or `id`. A JSON pointer can, however, point to nested elements. + +[NOTE] +==== +You can also use the negation operator (__!__) to help construct a query. For example, a `_queryFilter=!(userName+eq+"jdoe")` query would return every `userName` except for `jdoe`. +==== +You can set up query filters with one of the following types of expressions. + +[#query-comp-expression] +===== Comparison Expressions + + +* Equal queries (see xref:#query-comp-express-eq["Querying Objects That Equal the Given Value"]) + +* Contains queries (see xref:#query-comp-express-contains["Querying Objects That Contain the Given Value"]) + +* Starts with queries (see xref:#query-comp-express-starts["Querying Objects That Start With the Given Value"]) + +* Less than queries (see xref:#query-comp-express-lessthan["Querying Objects That Are Less Than the Given Value"]) + +* Less than or equal to queries (see xref:#query-comp-express-lesseq["Querying Objects That Are Less Than or Equal to the Given Value"]) + +* Greater than queries (see xref:#query-comp-express-gthan["Querying Objects That Are Greater Than the Given Value"]) + +* Greater than or equal to queries (see xref:#query-comp-express-ge["Querying Objects That Are Greater Than or Equal to the Given Value"]) + + +[NOTE] +==== +Certain system endpoints also support `EndsWith` and `ContainsAllValues` queries. However, such queries are __not supported__ for managed objects and have not been tested with all supported OpenICF connectors. +==== + +[#query-comp-express-eq] +====== Querying Objects That Equal the Given Value + +This is the associated JSON comparison expression: `json-pointer eq json-value`. + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/givenName eq "Dan"' +---- +The following REST call returns the user name and given name of all managed users whose first name (`givenName`) is "Dan": + +[source, console] +---- +$ 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?_queryFilter=givenName+eq+"Dan"&_fields=userName,givenName' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 3, + "result": [ + { + "givenName": "Dan", + "userName": "dlangdon" + }, + { + "givenName": "Dan", + "userName": "dcope" + }, + { + "givenName": "Dan", + "userName": "dlanoway" + } +} +---- + + +[#query-comp-express-contains] +====== Querying Objects That Contain the Given Value + +This is the associated JSON comparison expression: `json-pointer co json-value`. + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/givenName co "Da"' +---- +The following REST call returns the user name and given name of all managed users whose first name (`givenName`) contains "Da": + +[source, console] +---- +$ 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?_queryFilter=givenName+co+"Da"&_fields=userName,givenName' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 10, + "result": [ + { + "givenName": "Dave", + "userName": "djensen" + }, + { + "givenName": "David", + "userName": "dakers" + }, + { + "givenName": "Dan", + "userName": "dlangdon" + }, + { + "givenName": "Dan", + "userName": "dcope" + }, + { + "givenName": "Dan", + "userName": "dlanoway" + }, + { + "givenName": "Daniel", + "userName": "dsmith" + }, +... +} +---- + + +[#query-comp-express-starts] +====== Querying Objects That Start With the Given Value + +This is the associated JSON comparison expression: `json-pointer sw json-value`. + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/sn sw "Jen"' +---- +The following REST call returns the user names of all managed users whose last name (`sn`) starts with "Jen": + +[source, console] +---- +$ 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?_queryFilter=sn+sw+"Jen"&_fields=userName' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 4, + "result": [ + { + "userName": "bjensen" + }, + { + "userName": "djensen" + }, + { + "userName": "cjenkins" + }, + { + "userName": "mjennings" + } + ] +} +---- + + +[#query-comp-express-lessthan] +====== Querying Objects That Are Less Than the Given Value + +This is the associated JSON comparison expression: `json-pointer lt json-value`. + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/employeeNumber lt 5000' +---- +The following REST call returns the user names of all managed users whose `employeeNumber` is lower than 5000: + +[source, console] +---- +$ 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?_queryFilter=employeeNumber+lt+5000&_fields=userName,employeeNumber' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 4999, + "result": [ + { + "employeeNumber": 4907, + "userName": "jnorris" + }, + { + "employeeNumber": 4905, + "userName": "afrancis" + }, + { + "employeeNumber": 3095, + "userName": "twhite" + }, + { + "employeeNumber": 3921, + "userName": "abasson" + }, + { + "employeeNumber": 2892, + "userName": "dcarter" + } +... + ] +} +---- + + +[#query-comp-express-lesseq] +====== Querying Objects That Are Less Than or Equal to the Given Value + +This is the associated JSON comparison expression: `json-pointer le json-value`. + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/employeeNumber le 5000' +---- +The following REST call returns the user names of all managed users whose `employeeNumber` is 5000 or less: + +[source, console] +---- +$ 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?_queryFilter=employeeNumber+le+5000&_fields=userName,employeeNumber' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 5000, + "result": [ + { + "employeeNumber": 4907, + "userName": "jnorris" + }, + { + "employeeNumber": 4905, + "userName": "afrancis" + }, + { + "employeeNumber": 3095, + "userName": "twhite" + }, + { + "employeeNumber": 3921, + "userName": "abasson" + }, + { + "employeeNumber": 2892, + "userName": "dcarter" + } +... + ] +} +---- + + +[#query-comp-express-gthan] +====== Querying Objects That Are Greater Than the Given Value + +This is the associated JSON comparison expression: `json-pointer gt json-value` + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/employeeNumber gt 5000' +---- +The following REST call returns the user names of all managed users whose `employeeNumber` is higher than 5000: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + 'http://localhost:8443/openidm/managed/user?_queryFilter=employeeNumber+gt+5000&_fields=userName,employeeNumber' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 1458, + "result": [ + { + "employeeNumber": 5003, + "userName": "agilder" + }, + { + "employeeNumber": 5011, + "userName": "bsmith" + }, + { + "employeeNumber": 5034, + "userName": "bjensen" + }, + { + "employeeNumber": 5027, + "userName": "cclarke" + }, + { + "employeeNumber": 5033, + "userName": "scarter" + } +... + ] +} +---- + + +[#query-comp-express-ge] +====== Querying Objects That Are Greater Than or Equal to the Given Value + +This is the associated JSON comparison expression: `json-pointer ge json-value`. + +Review the following example: + +[source, javascript] +---- +"_queryFilter" : '/employeeNumber ge 5000' +---- +The following REST call returns the user names of all managed users whose `employeeNumber` is 5000 or greater: + +[source, console] +---- +$ 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?_queryFilter=employeeNumber+ge+5000&_fields=userName,employeeNumber' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 1457, + "result": [ + { + "employeeNumber": 5000, + "userName": "agilder" + }, + { + "employeeNumber": 5011, + "userName": "bsmith" + }, + { + "employeeNumber": 5034, + "userName": "bjensen" + }, + { + "employeeNumber": 5027, + "userName": "cclarke" + }, + { + "employeeNumber": 5033, + "userName": "scarter" + } +... + ] +} +---- + + + +[#query-presence] +===== Presence Expressions + +The following examples show how you can build filters using a presence expression, shown as `pr`. The presence expression is a filter that returns all records with a given attribute. + +A presence expression filter evaluates to `true` when a `json-pointer pr` matches any object in which the __json-pointer__ is present, and contains a non-null value. Review the following expression: + +[source, javascript] +---- +"_queryFilter" : '/mail pr' +---- +The following REST call uses that expression to return the mail addresses for all managed users with a `mail` property: + +[source, console] +---- +$ 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?_queryFilter=mail+pr&_fields=mail' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 2, + "result": [ + { + "mail": "jdoe@exampleAD.com" + }, + { + "mail": "bjensen@example.com" + } + ] +} +---- +From OpenIDM 4.5.1-20 onwards, you can also apply the presence filter on system objects. For example, the following query returns the `uid` of all users in an LDAP system who have the `uid` attribute in their entries: + +[source, console] +---- +$ 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?_queryFilter=uid+pr&_fields=uid' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 2, + "result": [ + { + "uid": "jdoe" + }, + { + "uid": "bjensen" + } + ] +} +---- + + +[#query-literal] +===== Literal Expressions + +A literal expression is a boolean: + +* `true` matches any object in the resource. + +* `false` matches no object in the resource. + +For example, you can list the `_id` of all managed objects as follows: + +[source, console] +---- +$ 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?_queryFilter=true&_fields=_id' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 2, + "result": [ + { + "_id": "d2e29d5f-0d74-4d04-bcfe-b1daf508ad7c" + }, + { + "_id": "709fed03-897b-4ff0-8a59-6faaa34e3af6" + } + ] +} +---- + + +[#query-complex] +===== Complex Expressions + +You can combine expressions using the boolean operators `and`, `or`, and `!` (not). The following example queries managed user objects located in London, with last name Jensen: + +[source, console] +---- +$ 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/?_queryFilter=city+eq+"London"+and+sn+eq+"Jensen"&_fields=userName,givenName,sn' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 3, + "result": [ + { + "sn": "Jensen", + "givenName": "Clive", + "userName": "cjensen" + }, + { + "sn": "Jensen", + "givenName": "Dave", + "userName": "djensen" + }, + { + "sn": "Jensen", + "givenName": "Margaret", + "userName": "mjensen" + } + ] +} +---- + + + +[#paging-query-results] +==== Paging and Counting Query Results + +The common filter query mechanism supports paged query results for managed objects, and for some system objects, depending on the system resource. + +Predefined queries must be configured to support paging, in the repository configuration. For example: + +[source, console] +---- +"query-all-ids" : "select _openidm_id from ${unquoted:_resource} SKIP ${unquoted:_pagedResultsOffset} + LIMIT ${unquoted:_pageSize}", +---- +The query implementation includes a configurable count policy that can be set per query. Currently, counting results is supported only for predefined queries, not for filtered queries. +The count policy can be one of the following: + +* `NONE` - to disable counting entirely for that query. + +* `EXACT` - to return the precise number of query results. Note that this has a negative impact on query performance. + +* `ESTIMATE` - to return a best estimate of the number of query results in the shortest possible time. This number generally correlates with the number of records in the index. + +If no count policy is specified, the policy is assumed to be `NONE`. This prevents the overhead of counting results, unless a result count is specifically required. + +The following query returns the first three records in the managed user repository: + +[source, console] +---- +$ 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&_pageSize=3" +{ + "result": [ + { + "_id": "scarter", + "_rev": "1" + }, + { + "_id": "bjensen", + "_rev": "1" + }, + { + "_id": "asmith", + "_rev": "1" + } + ], + "resultCount": 3, + "pagedResultsCookie": "3", + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "remainingPagedResults": -1 +} +---- +Notice that no counting is done in this query, so the returned value the of `"totalPagedResults"` and `"remainingPagedResults"` fields is `-1`. + +To specify that either an `EXACT` or `ESTIMATE` result count be applied, add the `"totalPagedResultsPolicy"` to the query. + +The following query is identical to the previous query but includes a count of the total results in the result set. + +[source, console] +---- +$ 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&_pageSize=3&_totalPagedResultsPolicy=EXACT" +{ + "result": [ + { + "_id": "scarter", + "_rev": "1" + }, + { + "_id": "bjensen", + "_rev": "1" + }, + { + "_id": "asmith", + "_rev": "1" + } + ], + "resultCount": 3, + "pagedResultsCookie": "3", + "totalPagedResultsPolicy": "EXACT", + "totalPagedResults": 4, + "remainingPagedResults": -1 +} +---- +Note that the `totalPagedResultsPolicy` is `EXACT` for this query. To return an exact result count, a corresponding `count` query must be defined in the repository configuration. The following excerpt of the default `repo.orientdb.json` file shows the predefined `query-all-ids` query, and its corresponding `count` query: + +[source] +---- +"query-all-ids" : "select _openidm_id, @version from ${unquoted:_resource} + SKIP ${unquoted:_pagedResultsOffset} LIMIT ${unquoted:_pageSize}", +"query-all-ids-count" : "select count(_openidm_id) AS total from ${unquoted:_resource}", +---- +-- +The following paging parameters are supported: + +`_pagedResultsCookie`:: +Opaque cookie used by the server to keep track of the position in the search results. The format of the cookie is a string value. + ++ +The server provides the cookie value on the first request. You should then supply the cookie value in subsequent requests until the server returns a null cookie, meaning that the final page of results has been returned. + ++ +Paged results are enabled only if the `_pageSize` is a non-zero integer. + +`_pagedResultsOffset`:: +Specifies the index within the result set of the number of records to be skipped before the first result is returned. The format of the `_pagedResultsOffset` is an integer value. When the value of `_pagedResultsOffset` is greater than or equal to 1, the server returns pages, starting after the specified index. + ++ +This request assumes that the `_pageSize` is set, and not equal to zero. + ++ +For example, if the result set includes 10 records, the `_pageSize` is 2, and the `_pagedResultsOffset` is 6, the server skips the first 6 records, then returns 2 records, 7 and 8. The `_pagedResultsCookie` value would then be 8 (the index of the last returned record) and the `_remainingPagedResults` value would be 2, the last two records (9 and 10) that have not yet been returned. + ++ +If the offset points to a page beyond the last of the search results, the result set returned is empty. + ++ +Note that the `totalPagedResults` and `_remainingPagedResults` parameters are not supported for all queries. Where they are not supported, their returned value is always `-1`. + +`_pageSize`:: +An optional parameter indicating that query results should be returned in pages of the specified size. For all paged result requests other than the initial request, a cookie should be provided with the query request. + ++ +The default behavior is not to return paged query results. If set, this parameter should be an integer value, greater than zero. + +-- + + +[#sorting-query-results] +==== Sorting Query Results + +For common filter query expressions, you can sort the results of a query using the `_sortKeys` parameter. This parameter takes a comma-separated list as a value and orders the way in which the JSON result is returned, based on this list. + +The `_sortKeys` parameter is not supported for predefined queries. + +The following query returns all users with the `givenName` `Dan`, and sorts the results alphabetically, according to surname (`sn`): + +[source, console] +---- +$ 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?_queryFilter=givenName+eq+"Dan"&_fields=givenName,sn&_sortKeys=sn' +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 3, + "result": [ + { + "sn": "Cope", + "givenName": "Dan" + }, + { + "sn": "Langdon", + "givenName": "Dan" + }, + { + "sn": "Lanoway", + "givenName": "Dan" + } + ] +} +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-external-rest.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-external-rest.adoc new file mode 100644 index 000000000..77e6f1c15 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-external-rest.adoc @@ -0,0 +1,264 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-external-rest] +== Accessing External REST Services + +You can access remote REST services by using the `openidm/external/rest` endpoint, or by specifying the `external/rest` resource in your scripts. Note that this service is not intended as a full connector to synchronize or reconcile identity data, but as a way to make dynamic HTTP calls as part of the OpenIDM logic. For more declarative and encapsulated interaction with remote REST services, and for synchronization or reconciliation operations, you should rather use the scripted REST connector. + +An external REST call via a script might look something like the following: + +[source] +---- +openidm.action("external/rest", "call", params); +---- +The `"call"` parameter specifies the action name to be used for this invocation, and is the standard method signature for the `openidm.action` method in OpenIDM 4.5. + +An external REST call over REST might look something like the following: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "url": "http://www.december.com/html/demo/hello.html", + "method": "GET", + "detectResultFormat": false, + "headers": { "custom-header": "custom-header-value" } + }' \ + "https://localhost:8443/openidm/external/rest?_action=call" +{ + "body": "\r\n + \r\n + \r\n + \r\n Hello World Demonstration Document\r\n \r\n + \r\n + \r\n +

\r\n Hello, World!\r\n

+ ... + \r\n", + "headers": { + "Server": "Apache", + "ETag": "\"299-4175ff09d1140\"", + "Date": "Mon, 28 Jul 2014 08:21:25 GMT", + "Content-Length": "665", + "Last-Modified": "Thu, 29 Jun 2006 17:05:33 GMT", + "Keep-Alive": "timeout=15, max=100", + "Content-Type": "text/html", + "Connection": "Keep-Alive", + "Accept-Ranges": "bytes" + } +} +---- +Note that attributes in the POST body __do not__ have underscore prefixes. This is different to the OpenIDM 2.1 implementation, in which underscores were required. + +HTTP 2xx responses are represented as regular, successful responses to the invocation. All other responses, including redirections, are returned as exceptions, with the HTTP status code in the exception `"code"`, and the response body in the exception `"detail"`, within the "content" element. + +[#invocation-parameters] +=== Invocation Parameters + +The following parameters are passed in the resource API parameters map. These parameters can override the static configuration (if present) on a per-invocation basis. + +* `url`. The target URL to invoke, in string format. + +* `method`. The HTTP action to invoke, in string format. ++ +Possible actions include `"POST"`, `"GET"`, `"PUT"`, `"DELETE"`, and `"OPTIONS"`. + +* `authenticate`. The authentication type, and the details with which to authenticate. ++ +OpenIDM 4.5 supports the following authentication types: ++ + +** `basic` authentication, with a username and password, for example: ++ + +[source] +---- +"authenticate" : {"type": "basic", "user" : "john", "password" : "Passw0rd"} +---- + +** `bearer` authentication, which takes an OAuth `token`, instead of a username and password, for example: ++ + +[source] +---- +"authenticate" : {"type": "bearer", "token" : "ya29.iQDWKpn8AHy09p....."} +---- + ++ +If no `authenticate` parameter is specified, no authentication is used. + +* `headers`. The HTTP headers to set, in a map format from string (__header-name__) to string (__header-value__). For example, `Accept-Language: en-US`. + +* `content-type` / `contentType`. The media type of the data that is sent, for example `Content-Type: application/json` when used in a REST command, or `contentType: JSON` when used in a script. + +* `body`. The body/resource representation to send (for PUT and POST operations), in string format. + +* `detectResultFormat`. Specifies whether JSON or non-JSON results are expected. Boolean, defaults to `true`. ++ +For all responses other than 2xx, the result is returned as an exception, with the HTTP code in the exception `"code"`. Any details are returned in the exception `"detail"` under the `"content"` element. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "url":"http://december.com/non_existing_page", + "method":"GET", + "content-type":"application/xml" + }' \ + "https://localhost:8443/openidm/external/rest?_action=call" +{ + "detail": { + "content": "December Communications, Inc. Missing Page (...) \n" + }, + "message": "Error while processing GET request: Not Found", + "reason": "Not Found", + "code": 404 +} +---- ++ +For more information about non-JSON results, see xref:#non-json-responses["Support for Non-JSON Responses"]. + + + +[#non-json-responses] +=== Support for Non-JSON Responses + +The external REST service supports any arbitrary payload (currently in stringified format). The `"detectResultFormat"` parameter specifies whether the server should attempt to detect the response format and, if the format is known, parse that format. + +Currently, the only known response format is JSON. So, if the service that is requested returns results in JSON format, and `"detectResultFormat"` is set to `true` (the default), the response from the call to external/rest will be the identical JSON data that was returned from the remote system. This enables JSON clients to interact with the external REST service with minimal changes to account for in the response. + +If the service returns results in JSON format and `"detectResultFormat"` is set to `false`, results are represented as a stringified entry. + +If `"detectResultFormat"` is set to `true` and the mime type is not recognized (currently any type other than JSON) the result is the same as if `"detectResultFormat"` were set to `false`. Set `"detectResultFormat"` to `false` if the remote system returns non-JSON data, or if you require details in addition to the literal JSON response body (for example, if you need to access a specific response header, such as a cookie). + +The representation as parsed JSON differs from the stringified format as follows: + +* The parsed JSON representation returns the message payload directly in the body, with no wrapper. Currently, for parsed JSON responses, additional metadata is not returned in the body. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "url": "http://localhost:8080/openidm/info/ping", + "method": "GET", + "detectResultFormat": true, + "headers": { "X-OpenIDM-Username": "anonymous", "X-OpenIDM-Password": "anonymous" } + }' \ + "https://localhost:8443/openidm/external/rest?_action=call" +{ + "shortDesc": "OpenIDM ready", + "state": "ACTIVE_READY" +} +---- + +* The stringified format includes a wrapper that represents other metadata, such as returned headers. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "url": "http://localhost:8080/openidm/info/ping", + "method": "GET", + "detectResultFormat": false, + "headers": { "X-OpenIDM-Username": "anonymous", "X-OpenIDM-Password": "anonymous" } + }' \ + "https://localhost:8443/openidm/external/rest?_action=call" +{ + "body": "{\"state\":\"ACTIVE_READY\",\"shortDesc\":\"OpenIDM ready\"}", + "headers": { + "Cache-Control": "no-cache", + "Server": "Jetty(8.y.z-SNAPSHOT)", + "Content-Type": "application/json;charset=UTF-8", + "Set-Cookie": "session-jwt=eyAiYWxn...-cQ.3QT4zT4ZZTj8LH8Oo_zx3w;Path=/", + "Expires": "Thu, 01 Jan 1970 00:00:00 GMT", + "Content-Length": "52", + "Vary": "Accept-Encoding, User-Agent" + } +} +---- ++ +A sample non-JSON response would be similar: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "url":"http://december.com", + "method":"GET", + "content-type":"application/xml", + "detectResultFormat":false + }' \ + "https://localhost:8443/openidm/external/rest?_action=call" +{ + "body": " \n + December Communications, Inc. + december.com\n + + ..." + "headers": { + "Server": "Apache", + "ETag": "\"4c3c-4f06c64da3980\"", + "Date": "Mon, 28 Jul 2014 19:16:33 GMT", + "Content-Length": "19516", + "Last-Modified": "Mon, 20 Jan 2014 20:04:06 GMT", + "Keep-Alive": "timeout=15, max=100", + "Content-Type": "text/html", + "Connection": "Keep-Alive", + "Accept-Ranges": "bytes" + } +} +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-logs.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-logs.adoc new file mode 100644 index 000000000..27222ffab --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-logs.adoc @@ -0,0 +1,118 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-logs] +== Configuring Server Logs + +In this chapter, you will learn about server logging, that is, the messages that OpenIDM logs related to server activity. + +Server logging is separate from auditing. Auditing logs activity on the OpenIDM system, such as access and synchronization. For information about audit logging, see xref:chap-auditing.adoc#chap-auditing["Using Audit Logs"]. To configure server logging, edit the `logging.properties` file in your `project-dir/conf` directory. + +[#log-messages] +=== Log Message Files + +The default configuration writes log messages in simple format to `openidm/logs/openidm*.log` files, rotating files when the size reaches 5 MB, and retaining up to 5 files. Also by default, OpenIDM writes all system and custom log messages to the files. + +You can modify these limits in the following properties in the `logging.properties` file for your project: + +[source] +---- +# Limiting size of output file in bytes: +java.util.logging.FileHandler.limit = 5242880 + +# Number of output files to cycle through, by appending an +# integer to the base file name: +java.util.logging.FileHandler.count = 5 +---- + + +[#log-levels] +=== Specifying the Logging Level + +By default, OpenIDM logs messages at the `INFO` level. This logging level is specified with the following global property in `conf/logging.properties`: + +[source] +---- +.level=INFO +---- +You can specify different separate logging levels for individual server features which override the global logging level. Set the log level, per package to one of the following: + +[source] +---- +SEVERE (highest value) +WARNING +INFO +CONFIG +FINE +FINER +FINEST (lowest value) +---- +For example, the following setting decreases the messages logged by the embedded PostgreSQL database: + +[source] +---- +# reduce the logging of embedded postgres since it is very verbose +ru.yandex.qatools.embed.postgresql.level = SEVERE +---- +Set the log level to `OFF` to disable logging completely (see in xref:#log-disabling["Disabling Logs"]), or to `ALL` to capture all possible log messages. + +If you use `logger` functions in your JavaScript scripts, set the log level for the scripts as follows: + +[source, ini] +---- +org.forgerock.openidm.script.javascript.JavaScript.level=level +---- +You can override the log level settings, per script, with the following setting: + +[source, ini] +---- +org.forgerock.openidm.script.javascript.JavaScript.script-name.level +---- +For more information about using `logger` functions in scripts, see xref:appendix-scripting.adoc#logger-functions["Logging Functions"]. + +[IMPORTANT] +==== +It is strongly recommended that you do __not__ log messages at the `FINE` or `FINEST` levels in a production environment. Although these levels are useful for debugging issues in a test environment, they can result in accidental exposure of sensitive data. For example, a password change patch request can expose the updated password in the Jetty logs. +==== + + +[#log-disabling] +=== Disabling Logs + +You can also disable logs if desired. For example, before starting OpenIDM, you can disable `ConsoleHandler` logging in your project's `conf/logging.properties` file. + +Just set `java.util.logging.ConsoleHandler.level = OFF`, and comment out other references to `ConsoleHandler`, as shown in the following excerpt: + +[source, ini] +---- +# ConsoleHandler: A simple handler for writing formatted records to System.err + #handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler + handlers=java.util.logging.FileHandler + ... + # --- ConsoleHandler --- + # Default: java.util.logging.ConsoleHandler.level = INFO + java.util.logging.ConsoleHandler.level = OFF + #java.util.logging.ConsoleHandler.formatter = ... + #java.util.logging.ConsoleHandler.filter=... +---- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-mail.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-mail.adoc new file mode 100644 index 000000000..1abff680c --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-mail.adoc @@ -0,0 +1,236 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-mail] +== Sending Email + +This chapter shows you how to configure the outbound email service, so that you can send email through OpenIDM, either by script or through the REST API. + +You can also configure the outbound email service in the Admin UI, by clicking Configure > System Preferences > Email. The fields on that screen correspond to what is described in the following sections. + +[#setup-outbound-email] +.To Set Up Outbound Email +==== +The outbound email service relies on a configuration object to identify the email account that is used to send messages. A sample configuration is provided in `openidm/samples/misc/external.email.json`. To set up the external email service, follow these steps. + +. You do not have to shut down OpenIDM. ++ +If you are setting up outbound email through the UI, start configuring an outbound email server directly from the noted UI screen. + +. Copy the sample email configuration to the `conf` directory of your project. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm/ +$ cp samples/misc/external.email.json conf/ +---- + +. Edit `external.email.json` to reflect the account that is used to send messages, for example: ++ + +[source, json] +---- +{ + "host" : "smtp.gmail.com", + "port" : 587, + "debug" : false, + "auth" : { + "enable" : true, + "username" : "admin", + "password" : "Passw0rd" + }, + "from" : "admin@example.com", + "timeout" : 300000, + "writetimeout" : 300000, + "connectiontimeout" : 300000, + "starttls" : { + "enable" : true + }, + "ssl" : { + "enable" : false + }, + "smtpProperties" : [ + "mail.smtp.ssl.protocols=TLSv1.2", + "mail.smtps.ssl.protocols=TLSv1.2" + ], + "threadPoolSize" : 20 +} +---- ++ +OpenIDM encrypts the password when you restart the server (or if you configure outgoing email through the Admin UI). ++ +-- +You can specify the following outbound email configuration properties: + +`host`:: +The host name or IP address of the SMTP server. This can be the `localhost`, if the mail server is on the same system as OpenIDM. + +`port`:: +SMTP server port number, such as 25, 465, or 587. ++ + +[NOTE] +======== +Many SMTP servers require the use of a secure port such as 465 or 587. Many ISPs flag email from port 25 as spam. +======== + +`debug`:: +When set to `true`, this option outputs diagnostic messages from the JavaMail library. Debug mode can be useful if you are having difficulty configuring the external email endpoint with your mail server. + +`auth`:: +The authentication details for the mail account from which emails will be sent. ++ + +* `enable`—indicates whether you need login credentials to connect to the SMTP server. ++ + +[NOTE] +======== +If `"enable" : false`, you can leave the entries for `"username"` and `"password"` empty: + +[source, json] +---- +"enable" : false, +"username" : "", +"password" : "" +---- +======== + +* `username`—the account used to connect to the SMTP server. + +* `password`—the password used to connect to the SMTP server. + + +`starttls`:: +If `"enable" : true`, enables the use of the STARTTLS command (if supported by the server) to switch the connection to a TLS-protected connection before issuing any login commands. If the server does not support STARTTLS, the connection continues without the use of TLS. + +`from`:: +(Optional) Specifies a default `From:` address, that users see when they receive emails from OpenIDM. + +`ssl`:: +Set `"enable" : true` to use SSL to connect, and to use the SSL port by default. + +`smtpProperties`:: +Specifies the SSL protocols that will be enabled for SSL connections. Protocols are specified as a whitespace-separated list. The default protocol is TLSv1.2. + +`threadPoolSize`:: +(Optional) Emails are sent in separate threads managed by a thread pool. This property sets the number of concurrent emails that can be handled at a specific time. The default thread pool size (if none is specified) is `20`. + +`connectiontimeout` (integer, optional):: +The socket connection timeout, in milliseconds. The default connection timeout (if none is specified) is `300000` milliseconds, or 5 minutes. A setting of 0 disables this timeout. + +`timeout` (integer, optional):: +The socket read timeout, in milliseconds. The default read timeout (if none is specified) is `300000` milliseconds, or 5 minutes. A setting of 0 disables this timeout. + +`writetimeout` (integer, optional):: +The socket write timeout, in milliseconds. The default write timeout (if none is specified) is `300000` milliseconds, or 5 minutes. A setting of 0 disables this timeout. + +-- + +. Start OpenIDM if it is not running. + +. Check that the email service is enabled and active: ++ + +[source, console] +---- +-> scr list +... + [ 130] org.forgerock.openidm.external.email enabled + [ 21] [active ] org.forgerock.openidm.external.email +... +---- + +==== + +[#send-mail-rest] +=== Sending Mail Over REST + +Although you are more likely to send mail from a script in production, you can send email using the REST API by sending an HTTP POST to `/openidm/external/email`, to test that your configuration works. You pass the message parameters as part of the POST payload, URL encoding the content as necessary. + +The following example sends a test email using the REST API. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "from":"openidm@example.com", + "to":"your_email@example.com", + "subject":"Test", + "body":"Test"}' \ + "https://localhost:8443/openidm/external/email?_action=send" +{ + "status": "OK" +} +---- + + +[#send-mail-script] +=== Sending Mail From a Script + +You can send email by using the resource API functions, with the `external/email` context. For more information about these functions, see xref:appendix-scripting.adoc#function-ref["Function Reference"]. In the following example, `params` is an object that contains the POST parameters. + +[source, javascript] +---- +var params = new Object(); +params.from = "openidm@example.com"; +params.to = "your_email@example.com"; +params.cc = "bjensen@example.com,scarter@example.com"; +params.subject = "OpenIDM recon report"; +params.type = "text/html"; +params.body = "

Recon report follows...

"; + +openidm.action("external/email", "send", params); +---- +-- +OpenIDM supports the following POST parameters. + +`from`:: +Sender mail address + +`to`:: +Comma-separated list of recipient mail addresses + +`cc`:: +Optional comma-separated list of copy recipient mail addresses + +`bcc`:: +Optional comma-separated list of blind copy recipient mail addresses + +`subject`:: +Email subject + +`body`:: +Email body text + +`type`:: +Optional MIME type. One of `"text/plain"`, `"text/html"`, or `"text/xml"`. + +-- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-overview.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-overview.adoc new file mode 100644 index 000000000..5fe17d1ec --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-overview.adoc @@ -0,0 +1,205 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-overview] +== Architectural Overview + +This chapter introduces the OpenIDM architecture, and describes the modules and services that make up the OpenIDM product. +In this chapter you will learn: + +* How OpenIDM uses the OSGi framework as a basis for its modular architecture + +* How the infrastructure modules provide the features required for OpenIDM's core services + +* What those core services are and how they fit in to the overall architecture + +* How OpenIDM provides access to the resources it manages + + +[#openidm-modular-framework] +=== OpenIDM Modular Framework + +OpenIDM implements infrastructure modules that run in an OSGi framework. It exposes core services through RESTful APIs to client applications. + +The following figure provides an overview of the OpenIDM architecture, which is covered in more detail in subsequent sections of this chapter. + +[#d0e336] +image::images/openidm2-architecture.png[] +-- +The OpenIDM framework is based on OSGi: + +OSGi:: +OSGi is a module system and service platform for the Java programming language that implements a complete and dynamic component model. For a good introduction to OSGi, see the link:https://www.osgi.org/developer/benefits-of-using-osgi[OSGi, window=\_blank] site. OpenIDM currently runs in link:http://felix.apache.org/[Apache Felix, window=\_blank], an implementation of link:https://www.osgi.org/Specifications/HomePage[the OSGi Framework and Service Platform, window=\_blank]. + +Servlet:: +The Servlet layer provides RESTful HTTP access to the managed objects and services. OpenIDM embeds the Jetty Servlet Container, which can be configured for either HTTP or HTTPS access. + +-- + + +[#openidm-infrastructure-modules] +=== Infrastructure Modules + +-- +OpenIDM infrastructure modules provide the underlying features needed for core services: + +BPMN 2.0 Workflow Engine:: +OpenIDM provides an embedded workflow and business process engine based on Activiti and the Business Process Model and Notation (BPMN) 2.0 standard. + ++ +For more information, see xref:chap-workflow.adoc#chap-workflow["Integrating Business Processes and Workflows"]. + +Task Scanner:: +OpenIDM provides a task-scanning mechanism that performs a batch scan for a specified property in OpenIDM data, on a scheduled interval. The task scanner then executes a task when the value of that property matches a specified value. + ++ +For more information, see xref:chap-scheduler-conf.adoc#task-scanner["Scanning Data to Trigger Tasks"]. + +Scheduler:: +The scheduler provides a `cron`-like scheduling component implemented using the link:http://www.quartz-scheduler.org[Quartz library, window=\_blank]. Use the scheduler, for example, to enable regular synchronizations and reconciliations. + ++ +For more information, see xref:chap-scheduler-conf.adoc#chap-scheduler-conf["Scheduling Tasks and Events"]. + +Script Engine:: +The script engine is a pluggable module that provides the triggers and plugin points for OpenIDM. OpenIDM currently supports JavaScript and Groovy. + +Policy Service:: +OpenIDM provides an extensible policy service that applies validation requirements to objects and properties, when they are created or updated. + ++ +For more information, see xref:chap-policies.adoc#chap-policies["Using Policies to Validate Data"]. + +Audit Logging:: +Auditing logs all relevant system activity to the configured log stores. This includes the data from reconciliation as a basis for reporting, as well as detailed activity logs to capture operations on the internal (managed) and external (system) objects. + ++ +For more information, see xref:chap-auditing.adoc#chap-auditing["Using Audit Logs"]. + +Repository:: +The repository provides a common abstraction for a pluggable persistence layer. OpenIDM 4.5 supports reconciliation and synchronization with several major external repositories in production, including relational databases, LDAP servers, and even flat CSV and XML files. + ++ +The repository API uses a JSON-based object model with RESTful principles consistent with the other OpenIDM services. To facilitate testing, OpenIDM includes an embedded instance of OrientDB, a NoSQL database. You can then incorporate a supported internal repository, as described in xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. + +-- + + +[#openidm-core-services] +=== Core Services + +-- +The core services are the heart of the OpenIDM resource-oriented unified object model and architecture: + +Object Model:: +Artifacts handled by OpenIDM are Java object representations of the JavaScript object model as defined by JSON. The object model supports interoperability and potential integration with many applications, services, and programming languages. + ++ +OpenIDM can serialize and deserialize these structures to and from JSON as required. OpenIDM also exposes a set of triggers and functions that system administrators can define, in either JavaScript or Groovy, which can natively read and modify these JSON-based object model structures. + +Managed Objects:: ++ +A __managed object__ is an object that represents the identity-related data managed by OpenIDM. Managed objects are configurable, JSON-based data structures that OpenIDM stores in its pluggable repository. The default managed object configuration includes users and roles, but you can define any kind of managed object, for example, groups or devices. + ++ +You can access managed objects over the REST interface with a query similar to the following: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/..." +---- + +System Objects:: ++ +__System objects__ are pluggable representations of objects on external systems. For example, a user entry that is stored in an external LDAP directory is represented as a system object in OpenIDM. + ++ +System objects follow the same RESTful resource-based design principles as managed objects. They can be accessed over the REST interface with a query similar to the following: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/..." +---- ++ +There is a default implementation for the OpenICF framework, that allows any connector object to be represented as a system object. + +Mappings:: ++ +__Mappings__ define policies between source and target objects and their attributes during synchronization and reconciliation. Mappings can also define triggers for validation, customization, filtering, and transformation of source and target objects. + ++ +For more information, see xref:chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"]. + +Synchronization and Reconciliation:: ++ ++ +__Reconciliation__ enables on-demand and scheduled resource comparisons between the OpenIDM managed object repository and source or target systems. Comparisons can result in different actions, depending on the mappings defined between the systems. + ++ +__Synchronization__ enables creating, updating, and deleting resources from a source to a target system, either on demand or according to a schedule. + ++ +For more information, see xref:chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"]. + +-- + + +[#commons-rest-commands] +=== Secure Commons REST Commands + +Representational State Transfer (REST) is a software architecture style for exposing resources, using the technologies and protocols of the World Wide Web. For more information on the ForgeRock REST API, see xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + +REST interfaces are commonly tested with a `curl` command. Many of these commands are used in this document. They work with the standard ports associated with Java EE communications, 8080 and 8443. + +To run `curl` over the secure port, 8443, you must include either the `--insecure` option, or follow the instructions shown in xref:chap-security.adoc#rest-over-https["Restrict REST Access to the HTTPS Port"]. You can use those instructions with the self-signed certificate generated when OpenIDM starts, or with a `*.crt` file provided by a certificate authority. + +In many examples in this guide, `curl` commands to the secure port are shown with a `--cacert self-signed.crt` option. Instructions for creating that `self-signed.crt` file are shown in xref:chap-security.adoc#rest-over-https["Restrict REST Access to the HTTPS Port"]. + + +[#openidm-access-layer] +=== Access Layer + +-- +The access layer provides the user interfaces and public APIs for accessing and managing the OpenIDM repository and its functions: + +RESTful Interfaces:: +OpenIDM provides REST APIs for CRUD operations, for invoking synchronization and reconciliation, and to access several other services. + ++ +For more information, see xref:appendix-rest.adoc#appendix-rest["REST API Reference"]. + +User Interfaces:: +User interfaces provide access to most of the functionality available over the REST API. + +-- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-passwords.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-passwords.adoc new file mode 100644 index 000000000..3502bf898 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-passwords.adoc @@ -0,0 +1,1271 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-passwords] +== Managing Passwords + +OpenIDM provides password management features that help you enforce password policies, limit the number of passwords users must remember, and let users reset and change their passwords. + +[#enforce-password-policy] +=== Enforcing Password Policy + +A password policy is a set of rules defining what sequence of characters constitutes an acceptable password. Acceptable passwords generally are too complex for users or automated programs to generate or guess. + +Password policies set requirements for password length, character sets that passwords must contain, dictionary words and other values that passwords must not contain. Password policies also require that users not reuse old passwords, and that users change their passwords on a regular basis. +OpenIDM enforces password policy rules as part of the general policy service. For more information about the policy service, see xref:chap-policies.adoc#chap-policies["Using Policies to Validate Data"]. The default password policy applies the following rules to passwords as they are created and updated: + +* A password property is required for any user object. + +* The value of a password cannot be empty. + +* The password must include at least one capital letter. + +* The password must include at least one number. + +* The minimum length of a password is 8 characters. + +* The password cannot contain the user name, given name, or family name. + +You can remove these validation requirements, or include additional requirements, by configuring the policy for passwords. For more information, see xref:chap-policies.adoc#configuring-default-policy["Configuring the Default Policy for Managed Objects"]. +-- +The password validation mechanism can apply in many situations. + +Password change and password reset:: +Password change involves changing a user or account password in accordance with password policy. Password reset involves setting a new user or account password on behalf of a user. + ++ +By default, OpenIDM controls password values as they are provisioned. + ++ +To change the default administrative user password, `openidm-admin`, see xref:chap-security.adoc#security-replace-defaults["Replace Default Security Settings"]. + +Password recovery:: +Password recovery involves recovering a password or setting a new password when the password has been forgotten. + ++ +OpenIDM provides a self-service end user interface for password changes, password recovery, and password reset. For more information, see xref:chap-ui.adoc#ui-configuring["Configuring User Self-Service"]. + +Password comparisons with dictionary words:: +You can add dictionary lookups to prevent use of password values that match dictionary words. + +Password history:: +You can add checks to prevent reuse of previous password values. For more information, see xref:#password-history["Creating a Password History Policy"]. + +Password expiration:: +You can configure OpenIDM to call a workflow that ensures users are able to change expiring or to reset expired passwords. + +-- + +[#password-history] +==== Creating a Password History Policy + +To create a password history policy, you need to include customized scripts as described in xref:../samples-guide/chap-ldap-samples.adoc#sample-multiple-passwords["Storing Multiple Passwords For Managed Users"] in the __Samples Guide__. Copy these scripts to your`project-dir/script` directory. + +You also need to modify the following configuration files: + +* Modify the `sync.json` file to include connections to the custom `onCreate-onUpdate-sync.js` script: ++ + +[source, javascript] +---- +"onCreate" : { + "type" : "text/javascript", + "file" : "script/onCreate-onUpdate-sync.js" +}, +"onUpdate" : { + "type" : "text/javascript", + "file" : "script/onCreate-onUpdate-sync.js" +} +---- ++ +If you have existing `onCreate` and `onUpdate` code blocks, you may need to consolidate options either in the applicable script file, or in a `source` entry. + +* Modify the `router.json` file to include code blocks for the `managed/user` object and associated policy. These policies apply to the arbitrary `ldapPassword` parameter which you will also add to the `managed.json` file: ++ + +[source, javascript] +---- +{ + "pattern" : "managed/user.*", + "onRequest" : { + "type" : "text/javascript", + "file" : "script/set-additional-passwords.js", + "additionalPasswordFields" : [ + "ldapPassword" + ] + }, + "methods" : [ + "create", + "update" + ] +}, +{ + "pattern" : "policy/managed/user.*", + "onRequest" : { + "type" : "text/javascript", + "file" : "script/set-additional-passwords.js", + "additionalPasswordFields" : [ + "ldapPassword" + ] + }, + "methods" : [ + "action" + ] +} +---- + +* In the `policy.json` file, include the `pwpolicy.js` file from your project's `script/` subdirectory, as `additionalFiles`: ++ + +[source, javascript] +---- +"type" : "text/javascript", +"file" : "policy.js", +"additionalFiles": [ "script/pwpolicy.js" ] +---- + +* Now make the following changes to your project's `managed.json` file. + +** Find the `"name" : "user",` object code block, normally near the start of the file. Include the following code blocks for the `onValidate`, `onCreate`, and `onUpdate` scripts. The value for the `storedFields` and `historyFields` should match the `additionalPasswordFields` that you included in the `router.json` file. ++ +You may vary the value of `historySize`, depending on the number of recent passwords you want to record in the history for each user. A `historySize` of 2 means that users who change their passwords can't use their previous two passwords. ++ + +[source, javascript] +---- +"name" : "user", +"onValidate" : { + "type" : "groovy", + "file" : "script/storeFields.groovy", + "storedFields" : [ + "ldapPassword" + ] +}, +"onCreate" : { + "type" : "text/javascript", + "file" : "script/onCreate-user-custom.js", + "historyFields" : [ + "ldapPassword" + ], + "historySize" : 2 +}, +"onUpdate" : { + "type" : "text/javascript", + "file" : "script/onUpdate-user-custom.js", + "historyFields" : [ + "ldapPassword" + ], + "historySize" : 2 +} +---- + +** In same file under `properties`, add the following code block for `ldapPassword` ++ + +[source, javascript] +---- +"ldapPassword" : { + "title" : "Password", + "type" : "string", + "viewable" : false, + "searchable" : false, + "minLength" : 8, + "userEditable" : true, + "secureHash" : { + "algorithm" : "SHA-256" + }, + "policies" : [ + { + "policyId" : "at-least-X-capitals", + "params" : { + "numCaps" : 2 + } + }, + { + "policyId" : "at-least-X-numbers", + "params" : { + "numNums" : 1 + } + }, + { + "policyId" : "cannot-contain-others", + "params" : { + "disallowedFields" : [ + "userName", + "givenName", + "sn" + ] + } + }, + { + "policyId" : "re-auth-required", + "params" : { + "exceptRoles" : [ + "system", + "openidm-admin", + "openidm-reg", + "openidm-cert" + ] + } + }, + { + "policyId" : "is-new", + "params" : { + "historyLength" : 2 + } + } + ] +} +---- + +** Add the following `fieldHistory` code block, which maps field names to a list of historical values for the field. ++ + +[source, javascript] +---- +"fieldHistory" : { + "title" : "Field History", + "type" : "object", + "viewable" : false, + "searchable" : false, + "minLength" : 8, + "userEditable" : true, + "scope" : "private" +}, +---- + + +After your next reconciliation, the password policies that you just set up in OpenIDM should apply. + + + +[#multiple-passwords] +=== Storing Separate Passwords Per Linked Resource + +OpenIDM supports storing multiple passwords in a managed user entry, to enable synchronization of different passwords on different external resources. + +To store multiple passwords, you must extend the managed user schema to include additional properties for each target resource. You can set separate policies on each of these new properties, to ensure that the stored passwords adhere to the password policies of the specific external resources. + +The following addition to a sample `managed.json` configuration shows an `ldapPassword` property that has been added to managed user objects. This property will be mapped to the password property on an LDAP system: + +[source, javascript] +---- +"ldapPassword" : { + "title" : "Password", + "type" : "string", + "viewable" : false, + "searchable" : false, + "minLength" : 8, + "userEditable" : true, + "scope" : "private", + "secureHash" : { + "algorithm" : "SHA-256" + }, + "policies" : [ + { + "policyId" : "at-least-X-capitals", + "params" : { + "numCaps" : 2 + } + }, + { + "policyId" : "at-least-X-numbers", + "params" : { + "numNums" : 1 + } + }, + { + "policyId" : "cannot-contain-others", + "params" : { + "disallowedFields" : [ + "userName", + "givenName", + "sn" + ] + } + }, + { + "policyId" : "re-auth-required", + "params" : { + "exceptRoles" : [ + "system", + "openidm-admin", + "openidm-reg", + "openidm-cert" + ] + } + }, + { + "policyId" : "is-new", + "params" : { + "historyLength" : 2 + } + } + ] +}, +---- +This property definition shows that the `ldapPassword` will be hashed, with an SHA-256 algorithm, and sets the policy that will be applied to values of this property. + +To use this custom managed object property and its policies to update passwords on an external resource, you must make the corresponding configuration and script changes in your deployment. For a detailed sample that implements multiple passwords, see xref:../samples-guide/chap-ldap-samples.adoc#sample-multiple-passwords["Storing Multiple Passwords For Managed Users"] in the __Samples Guide__. That sample can also help you set up password history policies. + + +[#random-passwords] +=== Generating Random Passwords + +There are many situations when you might want to generate a random password for one or more user objects. + +OpenIDM provides a way to customize your user creation logic to include a randomly generated password that complies with the default password policy. This functionality is included in the default crypto script, `bin/defaults/script/crypto.js`, but is not invoked by default. For an example of how this functionality might be used, see the `openidm/bin/defaults/script/ui/onCreateUser.js` script. The following section of that file (commented out by default) means that users created by using the Admin UI, or directly over the REST interface, will have a randomly generated, password added to their entry: + +[source, javascript] +---- +if (!object.password) { + + // generate random password that aligns with policy requirements + object.password = require("crypto").generateRandomString([ + { "rule": "UPPERCASE", "minimum": 1 }, + { "rule": "LOWERCASE", "minimum": 1 }, + { "rule": "INTEGERS", "minimum": 1 }, + { "rule": "SPECIAL", "minimum": 1 } + ], 16); + +} +---- +Comment out this section to invoke the random password generation when users are created. Note that changes made to scripts take effect after the time set in the `recompile.minimumInterval`, described in xref:chap-configuration.adoc#script-config["Setting the Script Configuration"]. + +The generated password can be encrypted, or hashed, in accordance with the managed user schema, defined in `conf/managed.json`. For more information, see xref:chap-users-groups-roles.adoc#encoding-attribute-values["Encoding Attribute Values"]. + +You can use this random string generation in a number of situations. Any script handler that is implemented in JavaScript can call the `generateRandomString` function. + + +[#password-sync] +=== Synchronizing Passwords Between OpenIDM and an LDAP Server + +Password synchronization ensures uniform password changes across the resources that store the password. After password synchronization, a user can authenticate with the same password on each resource. No centralized directory or authentication server is required for performing authentication. Password synchronization reduces the number of passwords users need to remember, so they can use fewer, stronger passwords. + +OpenIDM can propagate passwords to the resources that store a user's password. In addition, OpenIDM provides two plugins to intercept and synchronize passwords that are changed natively in OpenDJ and Active Directory. + +When you use the password synchronization plugins, set up password policy enforcement on OpenDJ or Active Directory rather than on OpenIDM. Alternatively, ensure that all password policies that are enforced are identical to prevent password updates on one resource from being rejected by OpenIDM or by another resource. + +The password synchronization plugins intercept password changes on the resource before the passwords are stored in encrypted form. The plugins then send intercepted password values to OpenIDM over an encrypted channel. + +If the OpenIDM instance is unavailable when a password is changed in either OpenDJ or Active Directory, the respective password plugin intercepts the change, encrypts the password, and stores the encrypted password in a JSON file. The plugin then checks whether the OpenIDM instance is available, at a predefined interval. When OpenIDM becomes available, the plugin performs a PATCH on the managed user record, to replace the password with the encrypted password stored in the JSON file. + +To be able to synchronize passwords, both password synchronization plugins require that the corresponding managed user object exist in the OpenIDM repository. + +The following sections describe how to use the password synchronization plugin for OpenDJ, and the corresponding plugin for Active Directory. + +[NOTE] +==== +These plugins are available only with a subscription, from the ForgeRock link:https://backstage.forgerock.com/[BackStage, window=\_blank] site. +==== + +[#pwd-sync-opendj] +==== Synchronizing Passwords With OpenDJ + +Password synchronization with OpenDJ requires communication over the secure LDAP protocol (LDAPS). If you have not set up OpenDJ for LDAPS, do this before you start, as described in the link:../../../opendj/3.5/admin-guide/configure-ssl[OpenDJ Administration Guide, window=\_blank]. + +OpenIDM must be installed, and running before you continue with the procedures in this section. + +[#dj-pwd-sync-security] +===== Establishing Secure Communication Between OpenIDM and OpenDJ + +There are three possible modes of communication between OpenIDM and the OpenDJ password synchronization plugin: + +* __SSL Authentication.__ In this case, you must import the OpenIDM certificate into OpenDJ's truststore (either the self-signed certificate that is generated the first time OpenIDM is started, or a CA-signed certificate). ++ +For more information, see xref:#import-openidm-cert["To Import OpenIDM's Certificate into the OpenDJ Truststore"]. + +* __Mutual SSL Authentication.__ In this case, you must import the OpenIDM certificate into OpenDJ's truststore, as described in xref:#import-openidm-cert["To Import OpenIDM's Certificate into the OpenDJ Truststore"], __and__ import the OpenDJ certificate into OpenIDM's truststore, as described in xref:#import-opendj-cert["To Import OpenDJ's Certificate into the OpenIDM Truststore"]. You must also add the OpenDJ certificate DN as a value of the `allowedAuthenticationIdPatterns` property in your project's `conf/authentication.json` file. Mutual SSL authentication is the default configuration of the password synchronization plugin, and the one described in this procedure. + +* __HTTP Basic Authentication.__ In this case, the connection is secured using a username and password, rather than any exchange of certificates. OpenIDM supports basic authentication for testing purposes only. You should __not__ use basic authentication in production. The steps to configure the plugin for basic authentication are described in the general configuration steps in xref:#dj-pwd-sync-install["Installing the OpenDJ Password Synchronization Plugin"]. + + +[NOTE] +==== +Version 1.0.3 of the OpenDJ password synchronization plugin supports mutual SSL authentication only. +==== + +[#import-openidm-cert] +.To Import OpenIDM's Certificate into the OpenDJ Truststore +==== +You must export the certificate from OpenIDM's keystore into OpenDJ's truststore so that the OpenDJ agent can make SSL requests to the OpenIDM endpoints. + +OpenIDM generates a self-signed certificate the first time it starts up. This procedure uses the self-signed certificate to get the password synchronization plugin up and running. In a production environment, you should use a certificate that has been signed by a Certificate Authority. + +. Export OpenIDM's generated self-signed certificate to a file, as follows: ++ + +[source, console] +---- +$ cd /path/to/openidm/security +$ keytool \ + -export \ + -alias openidm-localhost \ + -file openidm-localhost.crt \ + -keystore keystore.jceks \ + -storetype jceks +Enter keystore password: changeit +Certificate stored in file +---- ++ +The default OpenIDM keystore password is `changeit`. + +. Import the self-signed certificate into OpenDJ's truststore: ++ + +[source, console] +---- +$ cd /path/to/opendj/config +$ keytool \ + -importcert \ + -alias openidm-localhost \ + -keystore truststore \ + -storepass `cat keystore.pin` \ + -file /path/to/openidm/security/openidm-localhost.crt +Owner: CN=localhost, O=OpenIDM Self-Signed Certificate, OU=None, L=None, ST=None, C=None +Issuer: CN=localhost, O=OpenIDM Self-Signed Certificate, OU=None, L=None, ST=None, C=None +Serial number: 15413e24ed3 +Valid from: Tue Mar 15 10:27:59 SAST 2016 until: Tue Apr 14 10:27:59 SAST 2026 +Certificate fingerprints: + MD5: 78:81:DE:C0:5D:86:3E:DE:E0:67:C2:2E:9D:48:A0:0E + SHA1: 29:14:FE:30:E7:D8:13:0F:A5:DD:DD:38:B5:D0:98:BA:E8:5B:96:59 + SHA256: F8:F2:F6:56:EF:DC:93:C0:98:36:95:...7D:F4:0D:F8:DC:22:7F:D1:CF:F5:FA:75:62:7A:69 + Signature algorithm name: SHA512withRSA + Version: 3 +Trust this certificate? [no]: yes +Certificate was added to keystore +---- + +==== + +[#import-opendj-cert] +.To Import OpenDJ's Certificate into the OpenIDM Truststore +==== +For mutual authentication, you must import OpenDJ's certificate into the OpenIDM truststore. + +OpenDJ generates a self-signed certificate when you set up communication over LDAPS. This procedure uses the self-signed certificate to get the password synchronization plugin up and running. In a production environment, you should use a certificate that has been signed by a Certificate Authority. + +. Export OpenDJ's generated self-signed certificate to a file, as follows: ++ + +[source, console] +---- +$ cd /path/to/opendj/config +$ keytool \ + -export \ + -alias server-cert \ + -file server-cert.crt \ + -keystore keystore \ + -storepass `cat keystore.pin` +Certificate stored in file +---- + +. Import the OpenDJ self-signed certificate into OpenIDM's truststore: ++ + +[source, console] +---- +$ cd /path/to/openidm/security +$ keytool \ + -importcert \ + -alias server-cert \ + -keystore truststore \ + -storepass changeit \ + -file /path/to/opendj/config/server-cert.crt +Owner: CN=localhost, O=OpenDJ RSA Self-Signed Certificate +Issuer: CN=localhost, O=OpenDJ RSA Self-Signed Certificate +Serial number: 41cefe38 +Valid from: Thu Apr 14 10:17:39 SAST 2016 until: Wed Apr 09 10:17:39 SAST 2036 +Certificate fingerprints: + MD5: 0D:BC:44:B3:C4:98:90:45:97:4A:8D:92:84:2B:FC:60 + SHA1: 35:10:B8:34:DE:38:59:AA:D6:DD:B3:44:C2:14:90:BA:BE:5C:E9:8C + SHA256: 43:66:F7:81:3C:0D:30:26:E2:E2:09:...9F:5E:27:DC:F8:2D:42:79:DC:80:69:73:44:12:87 + Signature algorithm name: SHA1withRSA + Version: 3 +Trust this certificate? [no]: yes +Certificate was added to keystore +---- + +. Add the certificate DN as a value of the `allowedAuthenticationIdPatterns` property for the `CLIENT_CERT` authentication module, in your project's `conf/authentication.json` file. ++ +For example, if you are using the OpenDJ self-signed certificate, add the DN `"CN=localhost, O=OpenDJ RSA Self-Signed Certificate, OU=None, L=None, ST=None, C=None"`, as shown in the following excerpt: ++ + +[source, console] +---- +$ more /path/to/openidm/project-dir/conf/authentication.json +... +{ + "name" : "CLIENT_CERT", + "properties" : { + "queryOnResource" : "security/truststore", + "defaultUserRoles" : [ + "openidm-cert" + ], + "allowedAuthenticationIdPatterns" : [ + "CN=localhost, O=OpenDJ RSA Self-Signed Certificate, OU=None, L=None, ST=None, C=None" + ] + }, + "enabled" : true +} + ... +---- + +==== + + +[#dj-pwd-sync-install] +===== Installing the OpenDJ Password Synchronization Plugin + +The following steps install the password synchronization plugin on an OpenDJ directory server that is running on the same host as OpenIDM (localhost). If you are running OpenDJ on a different host, use the fully qualified domain name instead of `localhost`. + +[#install-opendj-password-sync-plugin] +==== + +. Download the OpenDJ password synchronization plugin from the ForgeRock link:https://backstage.forgerock.com/[BackStage, window=\_blank] site. You must use the plugin version that corresponds to your OpenDJ version. + +. Extract the contents of the `opendj-accountchange-handler-version.zip` file to the directory where OpenDJ is installed: ++ + +[source, console] +---- +$ unzip ~/Downloads/opendj-accountchange-handler-version.zip -d /path/to/opendj/ +Archive: opendj-accountchange-handler-version.zip + creating: opendj/ + ... +---- + +. Restart OpenDJ to load the additional schema from the password synchronization plugin: ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./stop-ds --restart +Stopping Server... +... +[14/Apr/2016:13:19:11 +0200] category=EXTENSIONS severity=NOTICE + msgID=org.opends.messages.extension.571 msg=Loaded extension from file + '/path/to/opendj/lib/extensions/openidm-account-change-handler.jar' (build version, revision 1) +... +[14/Apr/2016:13:19:43 +0200] category=CORE severity=NOTICE msgID=org.opends.messages.core.139 +... The Directory Server has started successfully +---- + +. Configure the password synchronization plugin, if required. ++ +The password plugin configuration is specified in one of the following files: ++ + +* Plugin versions 1.0.3 and 1.1.1 - in `openidm-pwsync-plugin-config.ldif` + +* Plugin version 3.5.0 - in `openidm-accountchange-plugin-sample-config` + ++ +Depending on your plugin version, one of these configuration files should have been extracted to `path/to/opendj/config` when you extracted the plugin. ++ +Use a text editor to update the configuration, for example: ++ + +[source, console] +---- +$ cd /path/to/opendj/config +$ more openidm-pwsync-plugin-config.ldif +dn: cn=OpenIDM Notification Handler,cn=Account Status Notification Handlers,cn=config +objectClass: top +objectClass: ds-cfg-account-status-notification-handler +objectClass: ds-cfg-openidm-account-status-notification-handler +cn: OpenIDM Notification Handler +... +---- ++ +You can configure the following elements of the plugin. Depending on your plugin version, the property names might differ. Applicable version numbers are provided in the following list: ++ +-- + +`ds-cfg-enabled`:: +Specifies whether the plugin is enabled. ++ +Default value: `true` + +`ds-cfg-attribute`:: +The attribute in OpenIDM that stores user passwords. This property is used to construct the patch request on the OpenIDM managed user object. ++ +Default value: `password` + +`ds-cfg-query-id` (3.5) `ds-task-id` (1.x):: +The query-id for the patch-by-query request. This query must be defined in the repository configuration. ++ +Default value: `for-userName` + +`ds-cfg-attribute-type`:: +Specifies zero or more attribute types that the plugin will send along with the password change. If no attribute types are specified, only the DN and the new password will be synchronized to OpenIDM. ++ +Default values: `entryUUID` and `uid` + +`ds-cfg-log-file`:: +The log file location where the changed passwords are written when the plugin cannot contact OpenIDM. The default location is the `logs` directory of the server instance, in the file named `pwsync`. Passwords in this file will be encrypted. ++ +Default value: `logs/pwsync` ++ +Note that this setting has no effect if `ds-cfg-update-interval` is set to `0 seconds`. + +`ds-cfg-update-interval`:: +The interval, in seconds, at which password changes are propagated to OpenIDM. If this value is 0, updates are made synchronously in the foreground, and no encrypted passwords are stored in the `ds-cfg-log-file`. ++ +Default value: `0 seconds` + +`ds-cfg-openidm-url` (3.5) `ds-cfg-referrals-url` (1.x):: +The endpoint at which the plugin should find OpenIDM managed user accounts. ++ +Default value: `\https://localhost:8444/openidm/managed/user` ++ +For HTTP basic authentication, specify the `http` protocol in the URL, and a non-mutual authentication port, for example `\http://localhost:8080/openidm/managed/user`. + +`ds-cfg-ssl-cert-nickname`:: +The alias of the client certificate in the OpenDJ keystore. If LDAPS is configured during the GUI setup of OpenDJ, the default client key alias is `server-cert`. ++ +Default value: `server-cert` + +`ds-cfg-private-key-alias` (3.5) `ds-cfg-realm` (1.x):: +The alias of the private key that should be used by OpenIDM to decrypt the session key. ++ +Default value: `openidm-localhost` + +`ds-cfg-certificate-subject-dn` (3.5) `ds-certificate-subject-dn` (1.x):: +The certificate subject DN of the OpenIDM private key. The default configuration assumes that you are using the self-signed certificate that is generated when OpenIDM first starts. ++ +Default value: `CN=localhost, O=OpenIDM Self-Signed Certificate, OU=None, L=None, ST=None, C=None` + +`ds-cfg-key-manager-provider`:: +The OpenDJ key manager provider. The key manager provider specified here must be enabled. ++ +Default value: `cn=JKS,cn=Key Manager Providers,cn=config` + +`ds-cfg-trust-manager-provider`:: +The OpenDJ trust manager provider. The trust manager provider specified here must be enabled. ++ +Default value: `cn=JKS,cn=Trust Manager Providers,cn=config` + +`ds-cfg-openidm-username` (3.5) `ds-openidm-httpuser` (1.1.1):: +An OpenIDM administrative username that the plugin will use to make REST calls to OpenIDM. ++ +Default value: `openidm-admin` ++ +For SSL authentication and HTTP basic authentication, the user specified here must have the rights to patch managed user objects. ++ +This property is commented out in version of 3.5.0 of the plugin configuration, and must be uncommented if you use HTTP or SSL authentication. ++ +This property does not exist in version 1.0.3 of the plugin, as the plugin supports mutual SSL authentication only. + +`ds-cfg-openidm-password` (3.5) `ds-openidm-httppasswd` (1.1.1):: +The password of the OpenIDM administrative user specified by the previous property. ++ +Default value: `openidm-admin` ++ +This property is commented out in version of 3.5.0 of the plugin configuration, and must be uncommented if you use HTTP or SSL authentication. ++ +This property does not exist in version 1.0.3 of the plugin, as the plugin supports mutual SSL authentication only. + +-- + +. When you have updated the plugin configuration to fit your deployment, add the configuration to OpenDJ's configuration: ++ +For plugin version 3.5.0: ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./ldapmodify \ + --port 1389 \ + --hostname `hostname` \ + --bindDN "cn=Directory Manager" \ + --bindPassword "password" \ + --defaultAdd \ + --filename ../config/openidm-accountchange-plugin-sample-config +---- ++ +For plugin version 1.x: ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./ldapmodify \ + --port 1389 \ + --hostname `hostname` \ + --bindDN "cn=Directory Manager" \ + --bindPassword "password" \ + --defaultAdd \ + --filename ../config/openidm-pwsync-plugin-config.ldif + +Processing ADD request for cn=OpenIDM Notification Handler,cn=Account Status + Notification Handlers,cn=config +ADD operation successful for DN cn=OpenIDM Notification Handler,cn=Account Status + Notification Handlers,cn=config +---- + +. Restart OpenDJ for the new configuration to take effect: ++ + +[source, console] +---- +$ ./stop-ds --restart +Stopping Server... +... +[14/Apr/2016:13:25:50 +0200] category=EXTENSIONS severity=NOTICE + msgID=org.opends.messages.extension.571 msg=Loaded extension from file + '/path/to/opendj/lib/extensions/openidm-account-change-handler.jar' (build 1.1.1, revision 1) +... +[14/Apr/2016:13:26:27 +0200] category=CORE severity=NOTICE msgID=org.opends.messages.core.139 + msg=The Directory Server has sent an alert notification generated by + class org.opends.server.core.DirectoryServer (alert type org.opends.server.DirectoryServerStarted, + alert ID org.opends.messages.core-135): The Directory Server has started successfully +---- + +. Adjust your OpenDJ password policy configuration to use the password synchronization plugin. ++ +The following command adjusts the default password policy: ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./dsconfig \ + set-password-policy-prop \ + --port 4444 \ + --hostname `hostname` \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --policy-name "Default Password Policy" \ + --set account-status-notification-handler:"OpenIDM Notification Handler" \ + --trustStorePath ../config/admin-truststore \ + --no-prompt +Apr 14, 2016 1:28:32 PM org.forgerock.i18n.slf4j.LocalizedLogger info +INFO: Loaded extension from file + '/path/to/opendj/lib/extensions/openidm-account-change-handler.jar' (build 1.1.1, revision 1) +---- + +==== +Password synchronization should now be configured and working. To test that the setup has been successful, change a user password in OpenDJ. That password should be synchronized to the corresponding OpenIDM managed user account, and you should be able to query the user's own entry in OpenIDM using the new password. + + + +[#pwd-sync-ad] +==== Synchronizing Passwords With Active Directory + +Use the Active Directory password synchronization plugin to synchronize passwords between OpenIDM and Active Directory (on systems running at least Microsoft Windows Server 2003). + +Install the plugin on Active Directory domain controllers (DCs) to intercept password changes, and send the password values to OpenIDM over an encrypted channel. You must have Administrator privileges to install the plugin. In a clustered Active Directory environment, you must install the plugin on all DCs. + +[#pwd-sync-openidm-config-ad] +===== Configuring OpenIDM for Password Synchronization With Active Directory + +To support password synchronization with Active Directory, you must make the following configuration changes to your managed user schema (in your project's `conf/managed.json` file): + +* Add a new property, named `userPassword` to the `user` object schema. This new property corresponds with the `userPassword` attribute in an Active Directory user entry. ++ +The following excerpt shows the required addition to the `managed.json` file: ++ + +[source, javascript] +---- +{ + "objects" : [ + { + "name" : "user", + ... + "schema" : { + ... + "properties" : { + "password" : { + ... + "encryption" : { + "key" : "openidm-sym-default" + }, + "scope" : "private" + }, + "userPassword" : { + "description" : "", + "title" : "", + "viewable" : true, + "searchable" : false, + "userEditable" : false, + "policies" : [ ], + "returnByDefault" : false, + "minLength" : "", + "pattern" : "", + "type" : "string", + "encryption" : { + "key" : "openidm-sym-default" + }, + "scope" : "private" + }, + ... + }, + "order" : [ + "_id", + "userName", + "password", + ... + "userPassword" + ] + } + }, + ... + ] +} +---- + +* Add an `onUpdate` script to the managed user object that checks whether the values of the two password properties (`password` and `userPassword`) match, and sets them to the same value if they do not. ++ +The excerpt shows the required addition to the `managed.json` file: ++ + +[source, javascript] +---- +{ + "objects" : [ + { + "name" : "user", + ... + "onUpdate" : { + "type" : "text/javascript", + "source" : "if (newObject.userPassword !== oldObject.userPassword) { newObject.password = newObject.userPassword; }" + }, + ... + ] +} +---- + + + +[#install-ad-password-sync-plugin] +===== Installing the Active Directory Password Synchronization Plugin + +The following steps install the password synchronization on an Active directory server: + +==== + +. Download the Active Directory password synchronization plugin from the ForgeRock link:https://backstage.forgerock.com/#!/downloads/OpenIDM/Password%20Sync%20Plugins/1.1.0/Active%20Directory%20Password%20Sync%20Plugin#list[BackStage, window=\_blank] site. + +. Install the plugin using one of the following methods: ++ + +* Double-click the setup file to launch the installation wizard. + +* Alternatively, from a command line, start the installation wizard with the `idm-setup.exe` command. If you want to save the settings in a configuration file, you can use the /saveinf switch as follows. ++ + +[source, console] +---- +C:\Path\To > idm-setup.exe /saveinf=C:\temp\adsync.inf +---- ++ + +* If you have a configuration file with installation parameters, you can install the password plugin in silent mode as follows: ++ + +[source, console] +---- +C:\Path\To > idm-setup.exe /verysilent /loadinf=C:\temp\adsync.inf +---- ++ + + +. Provide the following information during the installation. You must accept the license agreement shown to proceed with the installation. ++ +-- + +OpenIDM Connection information:: + +* __OpenIDM URL.__ Enter the URL where OpenIDM is deployed, including the query that targets each user account. For example: ++ + +[source, console] +---- +https://localhost:8444/openidm/managed/user?_action=patch&_queryId=for-userName&uid=${samaccountname} +---- + +* __OpenIDM User Password attribute.__ The password attribute for the `managed/user` object, such as `password`. ++ +If the `password` attribute does not exist in the `managed/user` object on OpenIDM, the password sync service will return an error when it attempts to replay a password update that has been made in Active Directory. If your managed user objects do not include passwords, you can add an `onCreate` script to the Active Directory > Managed Users mapping that sets an empty password when managed user accounts are created. The following excerpt of a `sync.json` file shows such a script in the mapping: ++ + +[source, javascript] +---- +"mappings" : [ + { + "name" : "systemAdAccounts_managedUser", + "source" : "system/ad/account", + "target" : "managed/user", + "properties" : [ + { + "source" : "sAMAccountName", + "target" : "userName" + } + ], + "onCreate" : { + "type" : "text/javascript", + "source" : "target.password=''" + }, +... +---- ++ +The onCreate script creates an empty password in the `managed/user` object, so that the password attribute exists and can be patched. + + +OpenIDM Authentication Parameters:: +Provide the following information: ++ + +* __User name.__ Enter name of an administrative user that can authenticate to OpenIDM, for example, `openidm-admin`. + +* __Password.__ Enter the password of the user that authenticates to OpenIDM, for example, `openidm-admin`. + +* __Select authentication type.__ Select the type of authentication that Active Directory will use to authenticate to OpenIDM. ++ +For plain HTTP authentication, select `OpenIDM Header`. For SSL mutual authentication, select `Certificate`. + + +Certificate authentication settings:: +If you selected `Certificate` as the authentication type on the previous screen, specify the details of the certificate that will be used for authentication. ++ + +* __Select Certificate file.__ Browse to select the certificate file that Active Directory will use to authenticate to OpenIDM. The certificate file must be configured with an appropriate encoding, cryptographic hash function, and digital signature. The plugin can read a public or a private key from a PKCS12 archive file. ++ +For production purposes, you should use a certificate that has been issued by a Certificate Authority. For testing purposes, you can generate a self-signed certificate. Whichever certificate you use, you must import that certificate into OpenIDM's trust store. ++ +To generate a self-signed certificate for Active Directory, follow these steps: ++ + +====== + +.. On the Active Directory host, generate a private key, which will be used to generate a self-signed certificate with the alias `ad-pwd-plugin-localhost`: ++ + +[source, console] +---- +> keytool.exe ^ + -genkey ^ + -alias ad-pwd-plugin-localhost ^ + -keyalg rsa ^ + -dname "CN=localhost, O=AD-pwd-plugin Self-Signed Certificate" ^ + -keystore keystore.jceks ^ + -storetype JCEKS +Enter keystore password: changeit +Re-enter new password: changeit +Enter key password for + +---- + +.. Now use the private key, stored in the `keystore.jceks` file, to generate the self-signed certificate: ++ + +[source, console] +---- +> keytool.exe ^ + -selfcert ^ + -alias ad-pwd-plugin-localhost ^ + -validity 365 ^ + -keystore keystore.jceks ^ + -storetype JCEKS ^ + -storepass changeit +---- + +.. Export the certificate. In this case, the `keytool` command exports the certificate in a PKCS12 archive file format, used to store a private key with a certificate: ++ + +[source, console] +---- +> keytool.exe ^ + -importkeystore ^ + -srckeystore keystore.jceks ^ + -srcstoretype jceks ^ + -srcstorepass changeit ^ + -srckeypass changeit ^ + -srcalias ad-pwd-plugin-localhost ^ + -destkeystore ad-pwd-plugin-localhost.p12 ^ + -deststoretype PKCS12 ^ + -deststorepass changeit ^ + -destkeypass changeit ^ + -destalias ad-pwd-plugin-localhost ^ + -noprompt +---- + +.. The PKCS12 archive file is named `ad-pwd-plugin-localhost.p12`. Import the contents of the keystore contained in this file to the system that hosts OpenIDM. To do so, import the PKCS12 file into the OpenIDM keystore file, named `truststore`, in the `/path/to/openidm/security` directory. ++ +On the machine that is running OpenIDM, enter the following command: ++ + +[source, console] +---- +$ keytool \ + -importkeystore \ + -srckeystore /path/to/ad-pwd-plugin-localhost.p12 + -srcstoretype PKCS12 + -destkeystore truststore + -deststoretype JKS +---- + +====== + +* __Password to open the archive file with the private key and certificate.__ Specify the keystore password (`changeit`, in the previous example). + + +Password Encryption settings:: +Provide the details of the certificate that will be used to encrypt password values. ++ + +* __Archive file with certificate.__ Browse to select the archive file that will be used for password encryption. That file is normally set up in PKCS12 format. ++ +For evaluation purposes, you can use a self-signed certificate, as described earlier. For production purposes, you should use a certificate that has been issued by a Certificate Authority. ++ +Whichever certificate you use, the certificate must be imported into OpenIDM's keystore, so that OpenIDM can locate the key with which to decrypt the data. To import the certificate into OpenIDM's keystore, `keystore.jceks`, run the following command on the OpenIDM host (UNIX): ++ + +[source, console] +---- +$ keytool \ + -importkeystore \ + -srckeystore /path/to/ad-pwd-plugin-localhost.p12 \ + -srcstoretype PKCS12 \ + -destkeystore /path/to/openidm/security/keystore.jceks \ + -deststoretype jceks +---- + +* __Private key alias.__ Specify the alias for the certificate, such as `ad-pwd-plugin-localhost`. + +* __Password to open certificate file.__ Specify the password to access the PFX keystore file, such as `changeit`, from the previous example. + +* __Select encryption standard.__ Specify the encryption standard that will be used when encrypting the password value (AES-128, AES-192, or AES-256). + + +Data storage:: +Provide the details for the storage of encrypted passwords in the event that OpenIDM is not available when a password modification is made. ++ + +* Select a secure directory in which the JSON files that contain encrypted passwords are queued. The server should prevent access to this folder, except access by the `Password Sync service`. The path name cannot include spaces. + +* __Directory poll interval (seconds).__ Enter the number of seconds between calls to check whether OpenIDM is available, for example, `60`, to poll OpenIDM every minute. + + +Log storage:: +Provide the details of the messages that should be logged by the plugin. ++ + +* Select the location to which messages should be logged. The path name cannot include spaces. + +* __Select logging level.__ Select the severity of messages that should be logged, either `error`, `info`, `warning`, `fatal`, or `debug`. + + +Select Destination Location:: +Setup installs the plugin in the location you select, by default `C:\Program Files\OpenIDM Password Sync`. + +-- + +. After running the installation wizard, restart the computer. + +. If you selected to authenticate over plain HTTP in the previous step, your setup is now complete. ++ +If you selected to authenticate with mutual authentication, complete this step. ++ + +.. The Password Sync Service uses Windows certificate stores to verify OpenIDM's identity. The certificate that OpenIDM uses must therefore be added to the list of trusted certificates on the Windows machine. ++ +For production purposes, you should use a certificate that has been issued by a certificate authority. For test purposes, you can use the self-signed certificate that is generated by OpenIDM on first startup. ++ +To add the OpenIDM certificate to the list of trusted certificates, use the Microsoft Management Console. ++ + +... Select Start and type `mmc` in the Search field. + +... In the Console window, select File > Add/Remove Snap-in. + +... From the left hand column, select Certificates and click Add. + +... Select My user account, and click Finish. + +... Repeat the previous two steps for Service account and Computer account. ++ +For Service account, select Local computer, then select OpenIDM Password Sync Service. ++ + +[#password-sync-service] +image::images/service-acct.png[] ++ +For Computer account, select Local computer. + +... Click Finish when you have added the three certificate snap-ins. + +... Still in the Microsoft Management Console, expand Certificates - Current User > Personal and select Certificates. + +... Select Action > All Tasks > Import to open the Certificate Import Wizard. + +... Browse for the OpenIDM certificate (`openidm-localhost.crt` by default, if you use OpenIDM's self-signed certificate). + +... Enter the Password for the certificate (`changeit` by default, if you use OpenIDM's self-signed certificate). + +... Accept the default for the Certificate Store. + +... Click Finish to complete the import. + +... Repeat the previous six steps to import the certificate for: ++ +[none] +* `Certificates - Current User > Trusted Root Certification Authorities` +* `Certificates - Service > OpenIDM Password Sync\Personal` +* `Certificates - Service > OpenIDM Password Sync\Trusted Root Certification Authorities` +* `Certificates > Local Computer > Personal` +* `Certificates > Local Computer > Trusted Root Certification Authorities` + + + +==== + + +[#changing-pwd-sync-plugin-conf] +===== Changing the Password Synchronization Plugin Configuration After Installation + +If you need to change any settings after installation, access the settings using the Registry Editor under HKEY_LOCAL_MACHINE > SOFTWARE > ForgeRock > OpenIDM > PasswordSync. + +For information about creating registry keys, see link:https://technet.microsoft.com/en-us/library/cc753092.aspx[Configure a Registry Item, window=\_blank] in the Windows documentation. + +You can change the following registry keys to reconfigure the plugin: +-- + +Keys to set the method of authentication:: + +* `authType` sets the authentication type. ++ +For plain HTTP or SSL authentication using OpenIDM headers, set `authType` to `idm`. ++ +For SSL mutual authentication using a certificate, set `authType` to `cert`. ++ +By default, the plugin does not validate the OpenIDM certificate. To configure this validation, set the following registry key: `netSslVerifyPeer = True`. + +* `authToken0` sets the username or certificate path for authentication. ++ +For example, for plain HTTP or SSL authentication, set `authToken0` to `openidm-admin`. ++ +For SSL mutual authentication, set `authToken0` to the certificate path, for example `path/to/certificate/cert.p12`. Only PKCS12 format certificates are supported. + +* `authToken1` sets the password for authentication. ++ +For example, for plain HTTP or SSL authentication, set `authToken1` to `openidm-admin`. ++ +For SSL mutual authentication, set `authToken1` to the password to the keystore. + + +Keys to set encryption of captured passwords:: + +* `certFile` sets the path to the keystore used for encrypting captured passwords, for example `path/to/keystore.p12`. Only PKCS12 keystores are supported. + +* `certPassword` sets the password to the keystore. + +* `keyAlias` specifies the alias that is used to encrypt passwords. + +* `keyType` sets the cypher algorithm, for example `aes128` + + +Keys to set encryption of sensitive registry values:: +For security reasons, you should encrypt the values of the `authToken1` and `certPassword` keys. These values are encrypted automatically when the plugin is installed, but when you change the settings, you can encrypt the values manually by setting the `encKey` registry key. ++ + +[NOTE] +====== +If you do not want to encrypt the values of the `authToken1` and `certPassword` keys, you __must__ remove the `encKey` from the registry, otherwise the plugin will use the value stored in that key to decrypt those values (even if they include an empty string). +====== ++ +To encrypt the values of the `authToken1` and `certPassword` keys: + +. Optionally, generate a new encryption key by running the following command: ++ + +[source, console] +---- +idmsync.exe --key +---- + +. Encrypt the values of the sensitive registry keys as follows: ++ + +[source, console] +---- +idmsync.exe --encrypt "key-value" "authToken1Value" +idmsync.exe --encrypt "key-value" "certPasswordValue" +---- + +. Replace the existing values of the `encyKey`, `authToken1` and `certPassword` keys with the values you generated in the previous step. ++ +If you do not want to generate a new encryption key, skip the first step and use the existing encryption key from the registry. + + +Keys to set the OpenIDM connection information:: +The password synchronization plugin assumes that the Active Directory user attribute is `sAMAccountName`. The default attribute will work in most deployments. If you cannot use the `sAMAccountName` attribute to identify the Active Directory user, set the following registry keys on your Active Directory server, specifying an alternative attribute. These examples use the `employeeId` attribute instead of `sAMAccountName`: ++ + +* `userAttribute = employeeId` + +* `userSearchFilter = (&(objectClass=user)(sAMAccountName=%s))` + +* `idmURL = https://localhost:8444/openidm/managed/user?_action=patch&_queryId=for-userName&uid=${employeeId}` + + +Keys to set the behavior when OpenIDM is unavailable:: +When OpenIDM is unavailable, or when an update fails, the password synchronization plugin stores the user password change a JSON file on the Active Directory system and attempts to resend the password change at regular intervals. + ++ +After installation, you can change the behaviour by setting the following registry keys: + ++ +Also the netTimeout in milliseconds can be set. ++ + +* `dataPath` - the location where the plugin stores the unsent changes. When any unsent changes have been delivered successfully, files in this path are deleted. The plugin creates one file for each user. This means that if a user changes his password three times in a row, you will see only one file containing the last change. + +* `pollEach` - the interval (in seconds) at which the plugin attempts to resend the changes. + +* `netTimeout` - the length of time (in milliseconds) after which the plugin stops attempting a connection. + + +Keys to set the logging configuration:: + +* `logPath` sets the path to the log file. + +* `logSize` - the maximum log size (in Bytes) before the log is rotated. When the log file reaches this size, it is renamed `idm.log.0` and a new `idm.log` file is created. + +* `logLevel` sets the logging level, `debug`, `info`, `warning`, `error`, or `fatal`. + + +Key to configure support for older OpenIDM versions:: +If the `idm2only` key is set to `true`, the plugin uses an old version of the patch request. This key __must not__ exist in the registry for OpenIDM versions 3.0 and later. + +-- +If you change any of the registry keys associated with the password synchronization plugin, run the `idmsync.exe --validate` command to make sure that all of the keys have appropriate values. + +The password synchronization plugin is installed and run as a service named OpenIDM Password Sync Service. You can use the Windows Service Manager to start and stop the service. To start or stop the plugin manually, run the `idmsync.exe --start` or `idmsync.exe --stop` command. + + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-policies.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-policies.adoc new file mode 100644 index 000000000..a88557a73 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-policies.adoc @@ -0,0 +1,599 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-policies] +== Using Policies to Validate Data + +OpenIDM provides an extensible policy service that enables you to apply specific validation requirements to various components and properties. This chapter describes the policy service, and provides instructions on configuring policies for managed objects. + +The policy service provides a REST interface for reading policy requirements and validating the properties of components against configured policies. Objects and properties are validated automatically when they are created, updated, or patched. Policies are generally applied to user passwords, but can also be applied to any managed or system object, and to internal user objects. + +The policy service enables you to accomplish the following tasks: + +* Read the configured policy requirements of a specific component. + +* Read the configured policy requirements of all components. + +* Validate a component object against the configured policies. + +* Validate the properties of a component against the configured policies. + +The OpenIDM router service limits policy application to managed, system, and internal user objects. To apply policies to additional objects, such as the audit service, you must modify your project's `conf/router.json` file. For more information about the router service, see xref:appendix-router.adoc#appendix-router["Router Service Reference"]. + +A default policy applies to all managed objects. You can configure this default policy to suit your requirements, or you can extend the policy service by supplying your own scripted policies. + +[#configuring-default-policy] +=== Configuring the Default Policy for Managed Objects + +Policies applied to managed objects are configured in two files: + +* A policy script file (`openidm/bin/defaults/script/policy.js`) that defines each policy and specifies how policy validation is performed. For more information, see xref:#policy-script-file["Understanding the Policy Script File"]. + +* A managed object policy configuration element, defined in your project's `conf/managed.json` file, that specifies which policies are applicable to each managed resource. For more information, see xref:#policy-config-element["Understanding the Policy Configuration Element"]. ++ + +[NOTE] +==== +The configuration for determining which policies apply to resources __other than managed objects__ is defined in your project's `conf/policy.json` file. The default `policy.json` file includes policies that are applied to internal user objects, but you can extend the configuration in this file to apply policies to system objects. +==== + + +[#policy-script-file] +==== Understanding the Policy Script File + +The policy script file (`openidm/bin/defaults/script/policy.js`) separates policy configuration into two parts: + +* A policy configuration object, which defines each element of the policy. For more information, see xref:#policy-config-object["Policy Configuration Objects"]. + +* A policy implementation function, which describes the requirements that are enforced by that policy. + +Together, the configuration object and the implementation function determine whether an object is valid in terms of the applied policy. The following excerpt of a policy script file configures a policy that specifies that the value of a property must contain a certain number of capital letters: + +[source, javascript] +---- +... +{ "policyId" : "at-least-X-capitals", + "policyExec" : "atLeastXCapitalLetters", + "clientValidation": true, + "validateOnlyIfPresent":true, + "policyRequirements" : ["AT_LEAST_X_CAPITAL_LETTERS"] +}, +... + +policyFunctions.atLeastXCapitalLetters = function(fullObject, value, params, property) { + var isRequired = _.find(this.failedPolicyRequirements, function (fpr) { + return fpr.policyRequirement === "REQUIRED"; + }), + isNonEmptyString = (typeof(value) === "string" && value.length), + valuePassesRegexp = (function (v) { + var test = isNonEmptyString ? v.match(/[(A-Z)]/g) : null; + return test !== null && test.length >= params.numCaps; + }(value)); + + if ((isRequired || isNonEmptyString) && !valuePassesRegexp) { + return [ { "policyRequirement" : "AT_LEAST_X_CAPITAL_LETTERS", "params" : {"numCaps": params.numCaps} } ]; + } + + return []; +} +... +---- +To enforce user passwords that contain at least one capital letter, the `policyId` from the preceding example is applied to the appropriate resource (`managed/user/*`). The required number of capital letters is defined in the policy configuration element of the managed object configuration file (see xref:#policy-config-element["Understanding the Policy Configuration Element"]. + +[#policy-config-object] +===== Policy Configuration Objects + +Each element of the policy is defined in a policy configuration object. The structure of a policy configuration object is as follows: + +[source, javascript] +---- +{ + "policyId" : "minimum-length", + "policyExec" : "propertyMinLength", + "clientValidation": true, + "validateOnlyIfPresent": true, + "policyRequirements" : ["MIN_LENGTH"] +} +---- + +* `policyId` - a unique ID that enables the policy to be referenced by component objects. + +* `policyExec` - the name of the function that contains the policy implementation. For more information, see xref:#policy-function["Policy Implementation Functions"]. + +* `clientValidation` - indicates whether the policy decision can be made on the client. When `"clientValidation": true`, the source code for the policy decision function is returned when the client requests the requirements for a property. + +* `validateOnlyIfPresent` - notes that the policy is to be validated only if it exists. + +* `policyRequirements` - an array containing the policy requirement ID of each requirement that is associated with the policy. Typically, a policy will validate only one requirement, but it can validate more than one. + + + +[#policy-function] +===== Policy Implementation Functions + +Each policy ID has a corresponding policy implementation function that performs the validation. Implementation functions take the following form: + +[source, javascript] +---- +function (fullObject, value, params, propName) { + +} +---- + +* `fullObject` is the full resource object that is supplied with the request. + +* `value` is the value of the property that is being validated. + +* `params` refers to the `params` array that is specified in the property's policy configuration. + +* `propName` is the name of the property that is being validated. + +The following example shows the implementation function for the `required` policy: + +[source, javascript] +---- +function required(fullObject, value, params, propName) { + if (value === undefined) { + return [ { "policyRequirement" : "REQUIRED" } ]; + } + return []; +} +---- + + + +[#policy-config-element] +==== Understanding the Policy Configuration Element + +The configuration of a managed object property (in the `managed.json` file) can include a `policies` element that specifies how policy validation should be applied to that property. The following excerpt of the default `managed.json` file shows how policy validation is applied to the `password` and `_id` properties of a managed/user object: + +[source] +---- +{ + "objects" : [ + { + "name" : "user", + ... + "schema" : { + "id" : "http://jsonschema.net", + ... + "properties" : { + "_id" : { + "type" : "string", + "viewable" : false, + "searchable" : false, + "userEditable" : false, + "policies" : [ + { + "policyId" : "cannot-contain-characters", + "params" : { + "forbiddenChars" : ["/"] + } + } + ] + }, + "password" : { + "type" : "string", + "viewable" : false, + "searchable" : false, + "minLength" : 8, + "userEditable" : true, + "policies" : [ + { + "policyId" : "at-least-X-capitals", + "params" : { + "numCaps" : 1 + } + }, + { + "policyId" : "at-least-X-numbers", + "params" : { + "numNums" : 1 + } + }, + { + "policyId" : "cannot-contain-others", + "params" : { + "disallowedFields" : [ + "userName", + "givenName", + "sn" + ] + } + }, + { + "policyId" : "re-auth-required", + "params" : { + "exceptRoles" : [ + "system", + "openidm-admin", + "openidm-reg", + "openidm-cert" + ] + } + } + ] + }, +---- +Note that the policy for the `_id` property references the function `cannot-contain-characters`, that is defined in the `policy.js` file. The policy for the `password` property references the `at-least-X-capitals`, `at-least-X-numbers`, `cannot-contain-others`, and `re-auth-required` functions that are defined in the `policy.js` file. The parameters that are passed to these functions (number of capitals required, and so forth) are specified in the same element. + + +[#policy-config-input] +==== Validation of Managed Object Data Types + +The `type` property of a managed object specifies the data type of that property, for example, `array`, `boolean`, `integer`, `number`, `null`, `object`, or `string`. For more information about data types, see the link:http://json-schema.org/latest/json-schema-core.html#anchor8[JSON Schema Primitive Types, window=\_blank] section of the JSON Schema standard. + +From OpenIDM 4.5 onwards, the `type` property is subject to policy validation when a managed object is created or updated. Validation fails if an invalid data type (such as an Array instead of a String) is provided. The `valid-type` policy in the default `policy.js` file ensures that the property values adhere to the `type` that has been defined for that property in the `managed.json` file. + +OpenIDM supports multiple valid property types. For example, you might have a scenario where a managed user can have more than one telephone number, or an __empty__ telephone number (when the user entry is first created and the telephone number is not yet known). In such a case, you could specify the accepted property type as follows in your `managed.json` file: + +[source, javascript] +---- +"telephoneNumber" : { + "type" : [ "array", "null" ], + "title" : "Phone Number", + "viewable" : true, + "userEditable" : true +---- +In this case, the `valid-type` policy would pass, if the `telephoneNumber` property was present, even if it had a null value. + +Because this policy validation is new in OpenIDM 4.5, updating an existing managed object that does not adhere to the `valid-type` policy will fail with a policy validation error. + + +[#policy-config-ui] +==== Configuring Policy Validation in the UI + +The Admin UI provides rudimentary support for applying policy validation to managed object properties. To configure policy validation for a managed object type update the configuration of the object type in the UI. For example, to specify validation policies for specific properties of managed user objects, select Configure > Managed Objects then click on the User object. Scroll down to the bottom of the Managed Object configuration, then update, or add, a validation policy. The `Policy` field here refers to a function that has been defined in the policy script file. For more information, see xref:#policy-script-file["Understanding the Policy Script File"]. You cannot define additional policy functions by using the UI. + +[NOTE] +==== +Take care with Validation Policies. If it relates to an array of relationships, such as between a user and multiple devices, "Return by Default" should always be set to false. You can verify this in the `managed.json` file for your project, with the `"returnByDefault" : false` entry for the applicable managed object, whenever there are `items` of `"type" : "relationship"`. +==== + + + +[#extending-policies] +=== Extending the Policy Service + +You can extend the policy service by adding custom scripted policies, and by adding policies that are applied only under certain conditions. + +[#custom-scripted-policies] +==== Adding Custom Scripted Policies + +If your deployment requires additional validation functionality that is not supplied by the default policies, you can add your own policy scripts to your project's `script` directory, and reference them from your project's `conf/policy.json` file. + +Do not modify the default policy script file (`openidm/bin/defaults/script/policy.js`) as doing so might result in interoperability issues in a future release. To reference additional policy scripts, set the `additionalFiles` property `conf/policy.json`. + +The following example creates a custom policy that rejects properties with null values. The policy is defined in a script named `mypolicy.js`: + +[source, javascript] +---- +var policy = { "policyId" : "notNull", + "policyExec" : "notNull", + "policyRequirements" : ["NOT_NULL"] +} + +addPolicy(policy); + +function notNull(fullObject, value, params, property) { + if (value == null) { + var requireNotNull = [ + {"policyRequirement": "NOT_NULL"} + ]; + return requireNotNull; + } + return []; +} +---- +The `mypolicy.js` policy is referenced in the `policy.json` configuration file as follows: + +[source, javascript] +---- +{ + "type" : "text/javascript", + "file" : "bin/defaults/script/policy.js", + "additionalFiles" : ["script/mypolicy.js"], + "resources" : [ + { +... +---- + + +[#conditional-policy-definitions] +==== Adding Conditional Policy Definitions + +You can extend the policy service to support policies that are applied only under specific conditions. To apply a conditional policy to managed objects, add the policy to your project's `managed.json` file. To apply a conditional policy to other objects, add it to your project's `policy.json` file. + +The following excerpt of a `managed.json` file shows a sample conditional policy configuration for the `"password"` property of managed user objects. The policy indicates that sys-admin users have a more lenient password policy than regular employees: + +[source, javascript] +---- +{ + "objects" : [ + { + "name" : "user", + ... + "properties" : { + ... + "password" : { + "title" : "Password", + "type" : "string", + ... + "conditionalPolicies" : [ + { + "condition" : { + "type" : "text/javascript", + "source" : "(fullObject.org === 'sys-admin')" + }, + "dependencies" : [ "org" ], + "policies" : [ + { + "policyId" : "max-age", + "params" : { + "maxDays" : ["90"] + } + } + ] + }, + { + "condition" : { + "type" : "text/javascript", + "source" : "(fullObject.org === 'employees')" + }, + "dependencies" : [ "org" ], + "policies" : [ + { + "policyId" : "max-age", + "params" : { + "maxDays" : ["30"] + } + } + ] + } + ], + "fallbackPolicies" : [ + { + "policyId" : "max-age", + "params" : { + "maxDays" : ["7"] + } + } + ] + } +---- +To understand how a conditional policy is defined, examine the components of this sample policy. + +There are two distinct scripted conditions (defined in the `condition` elements). The first condition asserts that the user object is a member of the `sys-admin` org. If that assertion is true, the `max-age` policy is applied to the `password` attribute of the user object, and the maximum number of days that a password may remain unchanged is set to `90`. + +The second condition asserts that the user object is a member of the `employees` org. If that assertion is true, the `max-age` policy is applied to the `password` attribute of the user object, and the maximum number of days that a password may remain unchanged is set to `30`. + +In the event that neither condition is met (the user object is not a member of the `sys-admin` org or the `employees` org), an optional fallback policy can be applied. In this example, the fallback policy also references the `max-age` policy and specifies that for such users, their password must be changed after 7 days. + +The `dependencies` field prevents the condition scripts from being run at all, if the user object does not include an `org` attribute. + +[NOTE] +==== +This example assumes that a custom `max-age` policy validation function has been defined, as described in xref:#custom-scripted-policies["Adding Custom Scripted Policies"]. +==== + + + +[#disabling-policies] +=== Disabling Policy Enforcement + +__Policy enforcement__ is the automatic validation of data when it is created, updated, or patched. In certain situations you might want to disable policy enforcement temporarily. You might, for example, want to import existing data that does not meet the validation requirements with the intention of cleaning up this data at a later stage. + +You can disable policy enforcement by setting `openidm.policy.enforcement.enabled` to `false` in your project's `conf/boot/boot.properties` file. This setting disables policy enforcement in the back-end only, and has no impact on direct policy validation calls to the Policy Service (which the UI makes to validate input fields). So, with policy enforcement disabled, data added directly over REST is not subject to validation, but data added with the UI is still subject to validation. + +You should not disable policy enforcement permanently, in a production environment. + + +[#policies-over-REST] +=== Managing Policies Over REST + +You can manage the policy service over the REST interface, by calling the REST endpoint `\https://localhost:8443/openidm/policy`, as shown in the following examples. + +[#listing-policies] +==== Listing the Defined Policies + +The following REST call displays a list of all the policies defined in `policy.json` (policies for objects other than managed objects). The policy objects are returned in JSON format, with one object for each defined policy ID: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/policy" +{ + "_id": "", + "resources": [ + { + "resource": "repo/internal/user/*", + "properties": [ + { + "name": "_id", + "policies": [ + { + "policyId": "cannot-contain-characters", + "params": { + "forbiddenChars": [ + "/" + ] + }, + "policyFunction": "\nfunction (fullObject, value, params, property) +... +---- +To display the policies that apply to a specific resource, include the resource name in the URL. For example, the following REST call displays the policies that apply to managed users: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/policy/managed/user/*" +{ + "_id": "*", + "resource": "managed/user/*", + "properties": [ + { + "name": "_id", + "conditionalPolicies": null, + "fallbackPolicies": null, + "policyRequirements": [ + "CANNOT_CONTAIN_CHARACTERS" + ], + "policies": [ + { + "policyId": "cannot-contain-characters", + "params": { + "forbiddenChars": [ + "/" + ] +... +---- + + +[#policy-validate] +==== Validating Objects and Properties Over REST + +To verify that an object adheres to the requirements of all applied policies, include the `validateObject` action in the request. + +The following example verifies that a new managed user object is acceptable, in terms of the policy requirements: + +[source, console] +---- +$ 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 '{ + "sn":"Jones", + "givenName":"Bob", + "_id":"bjones", + "telephoneNumber":"0827878921", + "passPhrase":null, + "mail":"bjones@example.com", + "accountStatus":"active", + "userName":"bjones@example.com", + "password":"123" + }' \ + "https://localhost:8443/openidm/policy/managed/user/bjones?_action=validateObject" +{ + "result": false, + "failedPolicyRequirements": [ + { + "policyRequirements": [ + { + "policyRequirement": "MIN_LENGTH", + "params": { + "minLength": 8 + } + } + ], + "property": "password" + }, + { + "policyRequirements": [ + { + "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS", + "params": { + "numCaps": 1 + } + } + ], + "property": "password" + } + ] +} +---- +The result (`false`) indicates that the object is not valid. The unfulfilled policy requirements are provided as part of the response - in this case, the user password does not meet the validation requirements. + +Use the `validateProperty` action to verify that a specific property adheres to the requirements of a policy. + +The following example checks whether Barbara Jensen's new password (`12345`) is acceptable: + +[source, console] +---- +$ 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 '{ "password" : "12345" }' \ + "https://localhost:8443/openidm/policy/managed/user/bjensen?_action=validateProperty" +{ + "result": false, + "failedPolicyRequirements": [ + { + "policyRequirements": [ + { + "policyRequirement": "MIN_LENGTH", + "params": { + "minLength": 8 + } + } + ], + "property": "password" + }, + { + "policyRequirements": [ + { + "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS", + "params": { + "numCaps": 1 + } + } + ], + "property": "password" + } + ] +} +---- +The result (`false`) indicates that the password is not valid. The unfulfilled policy requirements are provided as part of the response - in this case, the minimum length and the minimum number of capital letters. + +Validating a property that does fulfil the policy requirements returns a `true` result, for example: + +[source, console] +---- +$ 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 '{ "password" : "1NewPassword" }' \ + "https://localhost:8443/openidm/policy/managed/user/bjensen?_action=validateProperty" +{ + "result": true, + "failedPolicyRequirements": [] +} +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-repo.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-repo.adoc new file mode 100644 index 000000000..81b51af60 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-repo.adoc @@ -0,0 +1,647 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-repo] +== Managing the OpenIDM Repository + +OpenIDM stores managed objects, internal users, and configuration objects in a repository. By default, OpenIDM uses OrientDB for its internal repository. In production, you must replace OrientDB with a supported JDBC repository, as described in xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. + +This chapter describes the JDBC repository configuration, the use of mappings in the repository, and how to configure a connection to the repository over SSL. It also describes how to interact with the OpenIDM repository over the REST interface. + +[#jdbc-repo-config] +=== Understanding the JDBC Repository Configuration File + +OpenIDM provides configuration files for each supported JDBC repository, as well as example configurations for other repositories. These configuration files are located in the `/path/to/openidm/db/database/conf` directory. The configuration is defined in two files: + +* `datasource.jdbc-default.json`, which specifies the connection details to the repository. + +* `repo.jdbc.json`, which specifies the mapping between OpenIDM resources and the tables in the repository, and includes a number of predefined queries. + +Copy the configuration files for your specific database type to your project's `conf/` directory. + +[#datasource-jdbc-json] +==== Understanding the JDBC Connection Configuration File + +The default database connection configuration file for a MySQL database follows: + +[source, javascript] +---- +{ + "driverClass" : "com.mysql.jdbc.Driver", + "jdbcUrl" : "jdbc:mysql://localhost:3306/openidm?allowMultiQueries=true&characterEncoding=utf8", + "databaseName" : "openidm", + "username" : "openidm", + "password" : "openidm", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp", + "partitionCount" : 4, + "maxConnectionsPerPartition" : 25, + "minConnectionsPerPartition" : 5 + } +} +---- +The configuration file includes the following properties: +-- + +`driverClass`, `jndiName`, or `jtaName`:: +Depending on the mechanism you use to acquire the data source, set __one__ of these properties: ++ + +* `"driverClass" : string` ++ +To use the JDBC driver manager to acquire a data source, set this property, as well as `"jdbcUrl"`, `"username"`, and `"password"`. The driver class must be the fully qualified class name of the database driver to use for your database. ++ +Using the JDBC driver manager to acquire a data source is the most likely option, and the only one supported "out of the box". The remaining options in the sample repository configuration file assume that you are using a JDBC driver manager. ++ +Example: `"driverClass" : "com.mysql.jdbc.Driver"` + +* `"jndiName" : string` ++ +If you use JNDI to acquire the data source, set this property to the JNDI name of the data source. ++ +This option might be relevant if you want to run OpenIDM inside your own web container. ++ +Example: `"jndiName" : "jdbc/my-datasource"` + +* `"jtaName" : string` ++ +If you use an OSGi service to acquire the data source, set this property to a stringified version of the OsgiName. ++ +This option would only be relevant in a highly customized deployment, for example, if you wanted to develop your own connection pool. ++ +Example: `"jtaName" : "osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/openidm)"` + + +`jdbcUrl`:: +The connection URL to the JDBC database. The URL should include all of the parameters required by your database. For example, to specify the encoding in MySQL use `'characterEncoding=utf8'`. + ++ +Example: `"jdbcUrl" : "jdbc:mysql://localhost:3306/openidm?characterEncoding=utf8"` + +`databaseName`:: +The name of the database to which OpenIDM connects. By default, this is `openidm`. + +`username`:: +The username with which to access the JDBC database. + +`password`:: +The password with which to access the JDBC database. OpenIDM automatically encrypts clear string passwords. To replace an existing encrypted value, replace the whole `crypto-object` value, including the brackets, with a string of the new password. + +`connectionTimeout`:: +The period of time, in milliseconds, after which OpenIDM should consider an attempted connection to the database to have failed. The default period is 30000 milliseconds (30 seconds). + +`connectionPool`:: +Database connection pooling configuration. Currently OpenIDM supports the BoneCP pool library only (`"type" : "bonecp"`). + ++ +OpenIDM uses the default BoneCP configuration, except for the following parameters. You might need to adjust these parameters, according to your database workload: ++ + +* `partitionCount` ++ +The partition count determines the lock segmentation in the connection pool. Each incoming connection request acquires a connection from a pool that has thread-affinity. Threads are dispatched to the appropriate lock by using a value of `threadId % partitionCount`. A partition count that is greater than 1 protects the connection pool with more than a single lock, thereby reducing lock contention. ++ +By default, BoneCP creates a single partition. The JDBC Connection Configuration Files provided with OpenIDM set the partition count to `4`. + +* `maxConnectionsPerPartition` ++ +The maximum number of connections to create per partition. The maximum number of database connections is equal to `partitionCount * maxConnectionsPerPartition`. BoneCP does not create all these connections at once, but starts off with the `minConnectionsPerPartition` and gradually increases connections as required. ++ +By default, BoneCP creates a maximum of `20` connections per partition. The JDBC Connection Configuration Files provided with OpenIDM set the maximum connections per partition to `25`. + +* `minConnectionsPerPartition` ++ +The number of connections to start off with, per partition. The minimum number of database connections is equal to `partitionCount * minConnectionsPerPartition`. ++ +By default, BoneCP starts with a minimum of `1` connection per partition. The JDBC Connection Configuration Files provided with OpenIDM set the minimum connections per partition to `5`. + ++ +For more information about the BoneCP configuration parameters, see link:http://www.jolbox.com/configuration.html[http://www.jolbox.com/configuration.html, window=\_blank]. + +-- + + +[#repo-jdbc-json] +==== Understanding the Database Table Configuration + +An excerpt from an database table configuration file follows: + +[source, javascript] +---- +{ + "dbType" : "MYSQL", + "useDataSource" : "default", + "maxBatchSize" : 100, + "maxTxRetry" : 5, + "queries" : {...}, + "commands" : {...}, + "resourceMapping" : {...} +} +---- +The configuration file includes the following properties: +-- + +`"dbType"` : string, optional:: +The type of database. The database type might affect the queries used and other optimizations. Supported database types include MYSQL, SQLSERVER, ORACLE, MS SQL, and DB2. + +`"useDataSource"` : string, optional:: +This option refers to the connection details that are defined in the configuration file, described previously. The default configuration file is named `datasource.jdbc-default.json`. This is the file that is used by default (and the value of the `"useDataSource"` is therefore `"default"`). You might want to specify a different connection configuration file, instead of overwriting the details in the default file. In this case, set your connection configuration file `datasource.jdbc-name.json` and set the value of `"useDataSource"` to whatever __name__ you have used. + +`"maxBatchSize"`:: +The maximum number of SQL statements that will be batched together. This parameter allows you to optimize the time taken to execute multiple queries. Certain databases do not support batching, or limit how many statements can be batched. A value of `1` disables batching. + +`"maxTxRetry"`:: +The maximum number of times that a specific transaction should be attempted before that transaction is aborted. + +`"queries"`:: +Enables you to create predefined queries that can be referenced from the configuration. For more information about predefined queries, see xref:chap-data.adoc#parameterized-queries["Parameterized Queries"]. The queries are divided between those for `"genericTables"` and those for `"explicitTables"`. + ++ +The following sample extract from the default MySQL configuration file shows two credential queries, one for a generic mapping, and one for an explicit mapping. Note that the lines have been broken here for legibility only. In a real configuration file, the query would be all on one line. ++ + +[source, javascript] +---- +"queries" : { + "genericTables" : { + "credential-query" : "SELECT fullobject FROM ${_dbSchema}.${_mainTable} + obj INNER JOIN ${_dbSchema}.${_propTable} prop ON + obj.id = prop.${_mainTable}_id INNER JOIN ${_dbSchema}.objecttypes + objtype ON objtype.id = obj.objecttypes_id WHERE prop.propkey='/userName' + AND prop.propvalue = ${username} AND objtype.objecttype = ${_resource}", + ... + "explicitTables" : { + "credential-query" : "SELECT * FROM ${_dbSchema}.${_table} + WHERE objectid = ${username} and accountStatus = 'active'", + ... + } +} +---- ++ +Options supported for query parameters include the following: + +* A default string parameter, for example: ++ + +[source] +---- +openidm.query("managed/user", { "_queryId": "for-userName", "uid": "jdoe" }); +---- ++ +For more information about the query function, see xref:appendix-scripting.adoc#function-query["openidm.query(resourceName, params, fields)"]. + +* A list parameter (`${list:propName}`). ++ +Use this parameter to specify a set of indeterminate size as part of your query. For example: ++ + +[source] +---- +WHERE targetObjectId IN (${list:filteredIds}) +---- + +* An integer parameter (`${int:propName}`). ++ +Use this parameter if you need query for non-string values in the database. This is particularly useful with explicit tables. + + +`"commands"`:: +Specific commands configured for to managed the database over the REST interface. Currently, only two default commands are included in the configuration: ++ + +* `purge-by-recon-expired` + +* `purge-by-recon-number-of` + ++ +Both of these commands assist with removing stale reconciliation audit information from the repository, and preventing the repository from growing too large. For more information about repository commands, see xref:#repo-commands["Running Queries and Commands on the Repository"]. + +`"resourceMapping"`:: +Defines the mapping between OpenIDM resource URIs (for example, `managed/user`) and JDBC tables. The structure of the resource mapping is as follows: ++ + +[source, javascript] +---- +"resourceMapping" : { + "default" : { + "mainTable" : "genericobjects", + "propertiesTable" : "genericobjectproperties", + "searchableDefault" : true + }, + "genericMapping" : {...}, + "explicitMapping" : {...} +} +---- ++ +The default mapping object represents a default generic table in which any resource that does not have a more specific mapping is stored. + ++ +The generic and explicit mapping objects are described in the following section. + +-- + + + +[#explicit-generic-mapping] +=== Using Explicit or Generic Object Mapping With a JDBC Repository + +For JDBC repositories, there are two ways of mapping OpenIDM objects to the database tables: + +* __Generic mapping__, which allows arbitrary objects to be stored without special configuration or administration. + +* __Explicit mapping__, which allows for optimized storage and queries by explicitly mapping objects to tables and columns in the database. + +These two mapping strategies are discussed in the following sections. + +[#generic-mappings] +==== Using Generic Mappings + +Generic mapping speeds up development, and can make system maintenance more flexible by providing a more stable database structure. However, generic mapping can have a performance impact and does not take full advantage of the database facilities (such as validation within the database and flexible indexing). In addition, queries can be more difficult to set up. + +In a generic table, the entire object content is stored in a single large-character field named `fullobject` in the `mainTable` for the object. To search on specific fields, you can read them by referring to them in the corresponding `properties table` for that object. The disadvantage of generic objects is that, because every property you might like to filter by is stored in a separate table, you must join to that table each time you need to filter by anything. + +The following diagram shows a pared down database structure for the default generic table, and indicates the relationship between the main table and the corresponding properties table for each object. + +[#d0e5048] +image::images/generic-tables-erd.png[] +These separate tables can make the query syntax particularly complex. For example, a simple query to return user entries based on a user name would need to be implemented as follows: + +[source] +---- +SELECT fullobject FROM ${_dbSchema}.${_mainTable} obj INNER JOIN ${_dbSchema}.${_propTable} prop + ON obj.id = prop.${_mainTable}_id INNER JOIN ${_dbSchema}.objecttypes objtype + ON objtype.id = obj.objecttypes_id WHERE prop.propkey='/userName' AND prop.propvalue = ${uid} + AND objtype.objecttype = ${_resource}", +---- +The query can be broken down as follows: + +. Select the full object from the main table: ++ + +[source] +---- +SELECT fullobject FROM ${_dbSchema}.${_mainTable} obj +---- + +. Join to the properties table and locate the object with the corresponding ID: ++ + +[source] +---- +INNER JOIN ${_dbSchema}.${_propTable} prop ON obj.id = prop.${_mainTable}_id +---- + +. Join to the object types table to restrict returned entries to objects of a specific type. For example, you might want to restrict returned entries to `managed/user` objects, or `managed/role` objects: ++ + +[source] +---- +INNER JOIN ${_dbSchema}.objecttypes objtype ON objtype.id = obj.objecttypes_id +---- + +. Filter records by the `userName` property, where the userName is equal to the specified `uid` and the object type is the specified type (in this case, managed/user objects): ++ + +[source] +---- +WHERE prop.propkey='/userName' +AND prop.propvalue = ${uid} +AND objtype.objecttype = ${_resource}", +---- ++ +The value of the `uid` field is provided as part of the query call, for example: ++ + +[source] +---- +openidm.query("managed/user", { "_queryId": "for-userName", "uid": "jdoe" }); +---- + +Tables for user definable objects use a generic mapping by default. + +The following sample generic mapping object illustrates how `managed/` objects are stored in a generic table: + +[source, javascript] +---- +"genericMapping" : { + "managed/*" : { + "mainTable" : "managedobjects", + "propertiesTable" : "managedobjectproperties", + "searchableDefault" : true, + "properties" : { + "/picture" : { + "searchable" : false + } + } + } + }, +---- +-- + +`mainTable` (string, mandatory):: +Indicates the main table in which data is stored for this resource. + ++ +The complete object is stored in the `fullobject` column of this table. The table includes an `entityType` foreign key that is used to distinguish the different objects stored within the table. In addition, the revision of each stored object is tracked, in the `rev` column of the table, enabling multi version concurrency control (MVCC). For more information, see xref:appendix-objects.adoc#managed-objects-programmatic["Manipulating Managed Objects Programmatically"]. + +`propertiesTable` (string, mandatory):: +Indicates the properties table, used for searches. + ++ +The contents of the properties table is a defined subset of the properties, copied from the character large object (CLOB) that is stored in the `fullobject` column of the main table. The properties are stored in a one-to-many style separate table. The set of properties stored here is determined by the properties that are defined as `searchable`. + ++ +The stored set of searchable properties makes these values available as discrete rows that can be accessed with SQL queries, specifically, with `WHERE` clauses. It is not otherwise possible to query specific properties of the full object. ++ +The properties table includes the following columns: + +* `${_mainTable}_id` corresponds to the `id` of the full object in the main table, for example, `manageobjects_id`, or `genericobjects_id`. + +* `propkey` is the name of the searchable property, stored in JSON pointer format (for example `/mail`). + +* `proptype` is the data type of the property, for example `java.lang.String`. The property type is obtained from the Class associated with the value. + +* `propvalue` is the value of property, extracted from the full object that is stored in the main table. ++ +Regardless of the property data type, this value is stored as a string, so queries against it should treat it as such. + + +`searchableDefault` (boolean, optional):: +Specifies whether all properties of the resource should be searchable by default. Properties that are searchable are stored and indexed. You can override the default for individual properties in the `properties` element of the mapping. The preceding example indicates that all properties are searchable, with the exception of the `picture` property. + ++ +For large, complex objects, having all properties searchable implies a substantial performance impact. In such a case, a separate insert statement is made in the properties table for each element in the object, every time the object is updated. Also, because these are indexed fields, the recreation of these properties incurs a cost in the maintenance of the index. You should therefore enable `searchable` only for those properties that must be used as part of a WHERE clause in a query. + +`properties`:: +Lists any individual properties for which the searchable default should be overridden. + ++ +Note that if an object was originally created with a subset of `searchable` properties, changing this subset (by adding a new `searchable` property in the configuration, for example) will not cause the existing values to be updated in the properties table for that object. To add the new property to the properties table for that object, you must update or recreate the object. + +-- + + +[#searches-with-generic-mappings] +==== Improving Search Performance for Generic Mappings + +All properties in a generic mapping are searchable by default. In other words, the value of the `searchableDefault` property is `true` unless you explicitly set it to false. Although there are no individual indexes in a generic mapping, you can improve search performance by setting only those properties that you need to search as `searchable`. Properties that are searchable are created within the corresponding properties table. The properties table exists only for searches or look-ups, and has a composite index, based on the resource, then the property name. + +The sample JDBC repository configuration files (`db/database/conf/repo.jdbc.json`) restrict searches to specific properties by setting the `searchableDefault` to `false` for `managed/user` mappings. You must explicitly set `searchable` to true for each property that should be searched. The following sample extract from `repo.jdbc.json` indicates searches restricted to the `userName` property: + +[source, javascript] +---- +"genericMapping" : { + "managed/user" : { + "mainTable" : "manageduserobjects", + "propertiesTable" : "manageduserobjectproperties", + "searchableDefault" : false, + "properties" : { + "/userName" : { + "searchable" : true + } + } + } +}, +---- +With this configuration, OpenIDM creates entries in the properties table only for `userName` properties of managed user objects. + +If the global `searchableDefault` is set to false, properties that do not have a searchable attribute explicitly set to true are not written in the properties table. + + +[#explicit-mappings] +==== Using Explicit Mappings + +Explicit mapping is more difficult to set up and maintain, but can take complete advantage of the native database facilities. + +An explicit table offers better performance and simpler queries. There is less work in the reading and writing of data, since the data is all in a single row of a single table. In addition, it is easier to create different types of indexes that apply to only specific fields in an explicit table. The disadvantage of explicit tables is the additional work required in creating the table in the schema. Also, because rows in a table are inherently more simple, it is more difficult to deal with complex objects. Any non-simple key:value pair in an object associated with an explicit table is converted to a JSON string and stored in the cell in that format. This makes the value difficult to use, from the perspective of a query attempting to search within it. + +Note that it is possible to have a generic mapping configuration for most managed objects, __and__ to have an explicit mapping that overrides the default generic mapping in certain cases. The sample configuration provided in `/path/to/openidm/db/mysql/conf/repo.jdbc-mysql-explicit-managed-user.json` has a generic mapping for managed objects, but an explicit mapping for managed user objects. + +OpenIDM uses explicit mapping for internal system tables, such as the tables used for auditing. + +Depending on the types of usage your system is supporting, you might find that an explicit mapping performs better than a generic mapping. Operations such as sorting and searching (such as those performed in the default UI) tend to be faster with explicitly-mapped objects, for example. + +The following sample explicit mapping object illustrates how `internal/user` objects are stored in an explicit table: + +[source, javascript] +---- +"explicitMapping" : { + "internal/user" : { + "table" : "internaluser", + "objectToColumn" : { + "_id" : "objectid", + "_rev" : "rev", + "password" : "pwd", + "roles" : "roles" + } + }, + ... +} +---- +-- + +`` (string, mandatory):: +Indicates the URI for the resources to which this mapping applies, for example, `"internal/user"`. + +`table` (string, mandatory):: +The name of the database table in which the object (in this case internal users) is stored. + +`objectToColumn` (string, mandatory):: +The way in which specific managed object properties are mapped to columns in the table. + ++ +The mapping can be a simple one to one mapping, for example `"userName": "userName",` or a more complex JSON map or list. When a column is mapped to a JSON map or list, the syntax is as shown in the following examples: ++ + +[source] +---- +"messageDetail" : { "column" : "messagedetail", "type" : "JSON_MAP" } +---- ++ +or ++ + +[source] +---- +"roles": { "column" : "roles", "type" : "JSON_LIST" } +---- + +-- + +[CAUTION] +==== +Support for data types in columns is restricted to `String` (`VARCHAR` in the case of MySQL). If you use a different data type, such as `DATE` or `TIMESTAMP`, your database must attempt to convert from `String` to the other data type. This conversion is not guaranteed to work. + +If the conversion does work, the format might not be the same when it is read from the database as it was when it was saved. For example, your database might parse a date in the format `12/12/2012` and return the date in the format `2012-12-12` when the property is read. +==== + + + +[#jdbc-repos-ssl] +=== Configuring SSL with a JDBC Repository + +To configure SSL with a JDBC repository, you need to import the CA certificate file for the server into the OpenIDM truststore. That certificate file could have a name like `ca-cert.pem`. If you have a different genuine or self-signed certificate file, substitute accordingly. + +To import the CA certificate file into the OpenIDM truststore, use the `keytool` command native to the Java environment, typically located in the `/path/to/jre-version/bin` directory. On some UNIX-based systems, `/usr/bin/keytool` may link to that command. + +[#import-cacert-jdbc] +.Preparing OpenIDM for SSL with a JDBC Repository +==== + +. Import the `ca-cert.pem` certificate into the OpenIDM truststore file with the following command: ++ + +[source, console] +---- +$ keytool \ + -importcert \ + -trustcacerts \ + -file ca-cert.pem \ + -alias "DB cert" \ + -keystore /path/to/openidm/security/truststore +---- ++ +You are prompted for a keystore password. You must use the same password as is shown in the your project's `conf/boot/boot.properties` file. The default truststore password is: ++ + +[source, console] +---- +openidm.truststore.password=changeit +---- ++ +After entering a keystore password, you are prompted with the following question. Assuming you have included an appropriate `ca-cert.pem` file, enter `yes`. ++ + +[source, console] +---- +Trust this certificate? [no]: +---- + +. Open the repository connection configuration file, `datasource.jdbc-default.json`. ++ +Look for the `jdbcUrl` properties. You should see a `jdbc` URL. Add a `?characterEncoding=utf8&useSSL=true` to the end of that URL. ++ +The `jdbcUrl` that you configure depends on your JDBC repository. The following entries correspond to appropriate `jdbcURL` properties for MySQL, MSSQL, PostgreSQL, and Oracle DB, respectively: ++ + +[source, console] +---- +"jdbcUrl" : "jdbc:mysql://localhost:3306/openidm?characterEncoding=utf8&useSSL=true" +---- ++ + +[source, console] +---- +"jdbcUrl" : "jdbc:sqlserver://localhost:1433;instanceName=default; + databaseName=openidm;applicationName=OpenIDM?characterEncoding=utf8&useSSL=true" +---- ++ + +[source, console] +---- +"jdbcUrl" : "jdbc:postgresql://localhost:5432/openidm?characterEncoding=utf8&useSSL=true" +---- ++ + +[source, console] +---- +"jdbcUrl" : "jdbc:oracle:thin:@//localhost:1521/openidm?characterEncoding=utf8&useSSL=true" +---- + +. Open your project's `conf/config.properties` file. Find the `org.osgi.framework.bootdelegation` property. Make sure that property includes a reference to the `javax.net.ssl` option. If you started with the default version of `config.properties` that line should now read as follows: ++ + +[source, console] +---- +org.osgi.framework.bootdelegation=sun.*,com.sun.*,apple.*,com.apple.*,javax.net.ssl +---- + +. Open your project's `conf/system.properties` file. Add the following line to that file. If appropriate, substitute the path to your own truststore: ++ + +[source, console] +---- +# Set the truststore +javax.net.ssl.trustStore=&{launcher.install.location}/security/truststore +---- ++ +Even if you are setting up this instance of OpenIDM as part of a cluster, you still need to configure this initial truststore. After this instance joins a cluster, the SSL keys in this particular truststore are replaced. For more information on clustering, see xref:chap-cluster.adoc#chap-cluster["Configuring OpenIDM for High Availability"]. + +==== + + +[#repo-over-rest] +=== Interacting With the Repository Over REST + +The OpenIDM repository is accessible over the REST interface, at the `openidm/repo` endpoint. + +In general, you must ensure that external calls to the `openidm/repo` endpoint are protected. Native queries and free-form command actions on this endpoint are disallowed by default, as the endpoint is vulnerable to injection attacks. For more information, see xref:#repo-commands["Running Queries and Commands on the Repository"]. + +[#repo-pwd-change] +==== Changing the Repository Password + +In the case of an embedded OrientDB repository, the default username and password are `admin` and `admin`. You can change the default password, by sending the following POST request on the `repo` endpoint: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/repo?_action=updateDbCredentials&user=admin&password=newPassword" +---- +You must restart OpenIDM for the change to take effect. + + +[#repo-commands] +==== Running Queries and Commands on the Repository + +Free-form commands and native queries on the repository are disallowed by default and should remain so in production to reduce the risk of injection attacks. + +Common filter expressions, called with the `_queryFilter` keyword, enable you to form arbitrary queries on the repository, using a number of supported filter operations. For more information on these filter operations, see xref:chap-data.adoc#constructing-queries["Constructing Queries"]. Parameterized or predefined queries and commands (using the `_queryId` and `_commandId` keywords) can be authorized on the repository for external calls if necessary. For more information, see xref:chap-data.adoc#parameterized-queries["Parameterized Queries"]. + +Running commands on the repository is supported primarily from scripts. Certain scripts that interact with the repository are provided by default, for example, the scripts that enable you to purge the repository of reconciliation audit records. + +You can define your own commands, and specify them in the database table configuration file (either `repo.orientdb.json` or `repo.jdbc.json`). In the following simple example, a command is called to clear out UI notification entries from the repository, for specific users. + +The command is defined in the repository configuration file, as follows: + +[source, javascript] +---- +"commands" : { +"delete-notifications-by-id" : "DELETE FROM ui_notification WHERE receiverId = ${username}" +... +}, +---- +The command can be called from a script, as follows: + +[source, javascript] +---- +openidm.action("repo/ui/notification", "command", {}, +{ "commandId" : "delete-notifications-by-id", "userName" : "scarter"}); +---- +Exercise caution when allowing commands to be run on the repository over the REST interface, as there is an attached risk to the underlying data. + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-resource-conf.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-resource-conf.adoc new file mode 100644 index 000000000..d9fb18ef1 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-resource-conf.adoc @@ -0,0 +1,2119 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-resource-conf] +== Connecting to External Resources + +This chapter describes how to connect to external resources such as LDAP, Active Directory, flat files, and others. Configurations shown here are simplified to show essential aspects. Not all resources support all OpenIDM operations; however, the resources shown here support most of the CRUD operations, and also reconciliation and LiveSync. + +In OpenIDM, __resources__ are external systems, databases, directory servers, and other sources of identity data that are managed and audited by the identity management system. To connect to resources, OpenIDM loads the Identity Connector Framework, link:https://forgerock.org/openicf/[OpenICF, window=\_blank]. OpenICF aims to avoid the need to install agents to access resources, instead using the resources' native protocols. For example, OpenICF connects to database resources using the database's Java connection libraries or JDBC driver. It connects to directory servers over LDAP. It connects to UNIX systems by using `ssh`. + +[#openidm-openicf] +=== About OpenIDM and OpenICF + +OpenICF provides a common interface to allow identity services access to the resources that contain user information. OpenIDM loads the OpenICF API as one of its OSGi modules. OpenICF uses __connectors__ to separate the OpenIDM implementation from the dependencies of the resource to which OpenIDM is connecting. A specific connector is required for each remote resource. Connectors can run either locally or remotely. + +__Local__ connectors are loaded by OpenICF as regular bundles in the OSGi container. Remote connectors must be executed on a remote __connector server__. Most connectors can be run locally. However, a remote connector server is required when access libraries that cannot be included as part of the OpenIDM process are needed. If a resource, such as Microsoft Active Directory, does not provide a connection library that can be included inside the Java Virtual Machine, OpenICF can use the native .dll with a remote .NET connector server. In other words, OpenICF connects to Active Directory through a remote connector server that is implemented as a .NET service. + +Connections to remote connector servers are configured in a single __connector info provider__ configuration file, located in your project's `conf/` directory. + +Connectors themselves are configured through __provisioner__ files. One provisioner file must exist for each connector. Provisioner files are named `provisioner.openicf-name` where __name__ corresponds to the name of the connector, and are also located in the `conf/` directory. + +A number of sample connector configurations are available in the `openidm/samples/provisioners` directory. To use these connectors, edit the configuration files as required, and copy them to your project's `conf/` directory. + +The following figure shows how OpenIDM connects to resources by using connectors and remote connector servers. The figure shows one local connector (LDAP) and two remote connectors (Scripted SQL and PowerShell). In this example, the remote Scripted SQL connector uses a remote Java connector server. The remote PowerShell connector always requires a remote .NET connector server. + +[#d0e10434] +image::images/OpenICFArch.png[] + +[TIP] +==== +Connectors that use the .NET framework __must__ run remotely. Java connectors can be run locally or remotely. You might run a Java connector remotely for security reasons (firewall constraints), for geographical reasons, or if the JVM version that is required by the connector conflicts with the JVM version that is required by OpenIDM. +==== + + +[#connector-info-provider-conf] +=== Accessing Remote Connectors + +When you configure a remote connector, you use the __connector info provider service__ to connect through a remote connector server. The connector info provider service configuration is stored in the file `project-dir/conf/provisioner.openicf.connectorinfoprovider.json`. A sample configuration file is provided in the `openidm/samples/provisioners/` directory. To use this sample configuration, edit the file as required, and copy it to your project's `conf/` directory. + +The sample connector info provider configuration is as follows: + +[source, javascript] +---- +{ + "remoteConnectorServers" : + [ + { + "name" : "dotnet", + "host" : "127.0.0.1", + "port" : 8759, + "useSSL" : false, + "timeout" : 0, + "protocol" : "websocket", + "key" : "Passw0rd" + } + ] +} +---- +You can configure the following remote connector server properties: +-- + +`name`:: +string, required + ++ +The name of the remote connector server object. This name is used to identify the remote connector server in the list of connector reference objects. + +`host`:: +string, required + ++ +The remote host to connect to. + +`port`:: +integer, optional + ++ +The remote port to connect to. The default remote port is 8759. + +`heartbeatInterval`:: +integer, optional + ++ +The interval, in seconds, at which heartbeat packets are transmitted. If the connector server is unreachable based on this heartbeat interval, all services that use the connector server are made unavailable until the connector server can be reached again. The default interval is 60 seconds. + +`useSSL`:: +boolean, optional + ++ +Specifies whether to connect to the connector server over SSL. The default value is `false`. + +`timeout`:: +integer, optional + ++ +Specifies the timeout (in milliseconds) to use for the connection. The default value is `0`, which means that there is no timeout. + +`protocol`:: +string + ++ +Version 1.5.0.0 of the OpenICF framework supports a new communication protocol with remote connector servers. This protocol is enabled by default, and its value is `websocket` in the default configuration. + ++ +For compatibility reasons, you might want to enable the legacy protocol for specific remote connectors. For example, if you deploy the connector server on a Java 5 or 6 JVM, you must use the old protocol. In this case, remove the `protocol` property from the connector server configuration. + ++ +For the .NET connector server, the service with the new protocol listens on port 8759 and the service with the legacy protocol listens on port 8760 by default. + ++ +For the Java connector server, the service listens on port 8759 by default, for both the new and legacy protocols. The new protocol runs by default. To run the service with the legacy protocol, you must change the main class that is executed in the `ConnectorServer.sh` or `ConnectorServer.bat` file. The class that starts the websocket protocol is `MAIN_CLASS=org.forgerock.openicf.framework.server.Main`. The class that starts the legacy protocol is `MAIN_CLASS=org.identityconnectors.framework.server.Main`. To change the port on which the Java connector server listens, change the `connectorserver.port` property in the `openicf/conf/ConnectorServer.properties` file. ++ + +[CAUTION] +====== +Currently, the new, default protocol has specific known issues. You should therefore run the 1.5 .NET Connector Server in legacy mode, with the old protocol, as described in xref:#run-_NET-in-legacy-mode["Running the .NET Connector Server in Legacy Mode"]. +====== + +`key`:: +string, required + ++ +The secret key, or password, to use to authenticate to the remote connector server. + +-- +To run remotely, the connector .jar itself must be copied to the `openicf/bundles` directory, on the remote machine. + +The following example provides a configuration for reconciling managed users with objects in a remote CSV file. + +[#example-remote-csv-connector] +.Using the CSV Connector to Reconcile Users in a Remote CSV Data Store +==== +This example demonstrates reconciliation of users stored in a CSV file on a remote machine. The remote Java Connector Server enables OpenIDM to synchronize the internal OpenIDM repository with the remote CSV repository. + +The example assumes that a remote Java Connector Server is installed on a host named `remote-host`. For instructions on setting up the remote Java Connector Server, see xref:#java-connector-server-unix["Installing a Remote Java Connector Server for Unix/Linux"] or xref:#java-connector-server-windows["Installing a Remote Java Connector Server for Windows"]. + +[#configuring-remote-connector-server] +.Configuring the Remote Connector Server for the CSV Connector Example +====== +This example assumes that the Java Connector Server is running on the machine named `remote-host`. The example uses the small CSV data set provided with the __Getting Started__ sample (`hr.csv`). The CSV connector runs as a __remote connector__, that is, on the remote host on which the Java Connector Server is installed. Before you start, copy the sample data file, and the CSV connector itself over to the remote machine. + +. Shut down the remote connector server, if it is running. In the connector server terminal window, type `q`: ++ + +[source, console] +---- +q +INFO: Stopped listener bound to [0.0.0.0:8759] +May 30, 2016 12:33:24 PM INFO o.f.o.f.server.ConnectorServer: Server is + shutting down org.forgerock.openicf.framework.server.ConnectorServer@171ba877 +---- + +. Copy the CSV data file from the __Getting Started__ sample (`/path/to/openidm/samples/getting-started/data/hr.csv`) to an accessible location on the machine that hosts the remote Java Connector Server. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm/samples/getting-started/data/ +$ scp hr.csv testuser@remote-host:/home/testuser/csv-sample/data/ +Password:******** +hr.csv 100% 651 0.6KB/s 00:00 +---- + +. Copy the CSV connector .jar from the OpenIDM installation to the `openicf/bundles` directory on the remote host: ++ + +[source, console] +---- +$ cd path/to/openidm +$ scp connectors/csvfile-connector-1.5.1.4.jar testuser@remote-host:/path/to/openicf/bundles/ +Password:******** +csvfile-connector-1.5.1.4.jar 100% 40KB 39.8KB/s 00:00 +---- + +. The CSV connector depends on the Super CSV library, that is bundled with OpenIDM. Copy the Super CSV library `super-csv-2.4.0.jar` from the `openicf/bundle` directory to the `openicf/lib` directory on the remote server: ++ + +[source, console] +---- +$ cd path/to/openidm +$ scp bundle/super-csv-2.4.0.jar testuser@remote-host:/path/to/openicf/lib/ +Password:******** +super-csv-2.4.0.jar 100% 96KB 95.8KB/s 00:00 +---- + +. On the remote host, restart the Connector Server so that it picks up the new CSV connector and its dependent libraries: ++ + +[source, console] +---- +$ cd /path/to/openicf +$ bin/ConnectorServer.sh /run +... +May 30, 2016 3:58:29 PM INFO o.i.f.i.a.l.LocalConnectorInfoManagerImpl: Add ConnectorInfo ConnectorKey( + bundleName=org.forgerock.openicf.connectors.csvfile-connector bundleVersion=1.5.1.4 + connectorName=org.forgerock.openicf.csvfile.CSVFileConnector ) to Local Connector Info Manager from + file:/path/to/openicf/bundles/csvfile-connector-1.5.1.4.jar +May 30, 2016 3:58:30 PM org.glassfish.grizzly.http.server.NetworkListener start +INFO: Started listener bound to [0.0.0.0:8759] +May 30, 2016 3:58:30 PM org.glassfish.grizzly.http.server.HttpServer start +INFO: [OpenICF Connector Server] Started. +May 30, 2016 3:58:30 PM INFO o.f.openicf.framework.server.Main: ConnectorServer + listening on: ServerListener[0.0.0.0:8759 - plain] +---- ++ +The connector server logs are noisy by default. You should, however, notice the addition of the CSV connector. + +====== + +[#configuring-openidm-for-remote-csv-connector] +.Configuring OpenIDM for the Remote CSV Connector Example +====== +Before you start, copy the following files to your `/path/to/openidm/conf` directory: + +* link:../resources/sync.json[sync.json, window=\_blank] ++ +A customised mapping file required for this example. + +* `/openidm/samples/provisioners/provisioner.openicf.connectorinfoprovider.json` The sample connector server configuration file. + +* `/openidm/samples/provisioners/provisioner.openicf-csv.json` ++ +The sample connector configuration file. + + +. Edit the remote connector server configuration file (`provisioner.openicf.connectorinfoprovider.json`) to match your network setup. ++ +The following example indicates that the Java connector server is running on the host `remote-host`, listening on the default port, and configured with a secret key of `Passw0rd`: ++ + +[source] +---- +{ + "remoteConnectorServers" : [ + { + "name" : "csv", + "host" : "remote-host", + "port" : 8759, + "useSSL" : false, + "timeout" : 0, + "protocol" : "websocket", + "key" : "Passw0rd" + } + ] +} +---- ++ +The `name` that you set in this file will be referenced in the `connectorHostRef` property of the connector configuration, in the next step. ++ +The `key` that you specify here must match the password that you set when you installed the Java connector server. + +. Edit the CSV connector configuration file (`provisioner.openicf-csv.json`) as follows: ++ + +[source] +---- +{ + "name" : "csvfile", + "connectorRef" : { + "connectorHostRef" : "csv", + "bundleName" : "org.forgerock.openicf.connectors.csvfile-connector", + "bundleVersion" : "1.5.1.4", + "connectorName" : "org.forgerock.openicf.connectors.csv.CSVFileConnector" + }, + ... + "configurationProperties" : { + "csvFile" : "/home/testuser/csv-sample/data/hr.csv" + }, +} +---- ++ + +* The `connectorHostRef` property indicates which remote connector server to use, and refers to the `name` property you specified in the `provisioner.openicf.connectorinfoprovider.json` file. + +* The `bundleVersion : 1.5.1.4` must be exactly the same as the version of the CSV connector that you are using. If you specify a range here, the CSV connector version must be included in this range. + +* The `csvFile` property must specify the absolute path to the CSV data file that you copied to the remote host on which the Java Connector Server is running. + + +. Start OpenIDM: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh +---- + +. Verify that OpenIDM can reach the remote connector server and that the CSV connector has been configured correctly: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=test" +[ + { + "name": "csv", + "enabled": true, + "config": "config/provisioner.openicf/csv", + "objectTypes": [ + "__ALL__", + "account" + ], + "connectorRef": { + "bundleName": "org.forgerock.openicf.connectors.csvfile-connector", + "connectorName": "org.forgerock.openicf.csvfile.CSVFileConnector", + "bundleVersion": "1.5.1.4" + }, + "displayName": "CSV File Connector", + "ok": true + } +] +---- ++ +The connector must return `"ok": true`. ++ +Alternatively, use the Admin UI to verify that OpenIDM can reach the remote connector server and that the CSV connector is active. Log in to the Admin UI (`\https://localhost:8443/openidm/admin`) and select Configure > Connectors. The CSV connector should be listed on the Connectors page, and its status should be Active. + + +[#d0e10854] +image::images/remote-csv.png[] + + +. To test that the connector has been configured correctly, run a reconciliation operation as follows: ++ + +.. Select Configure > Mappings and click the systemCsvAccounts_managedUser mapping. + +.. Click Reconcile Now. + ++ +If the reconciliation is successful, the three users from the remote CSV file should have been added to the managed user repository. ++ +To check this, select Manage > User. + +====== +==== + +[#remote-connector-server-ha] +==== Configuring Failover Between Remote Connector Servers + +Starting with OpenIDM 4.5.0 you can specify a list of remote connector servers that the connector can target, to prevent the connector server from being a single point of failure. This failover configuration is included in your project's `conf/provisioner.openicf.connectorinfoprovider.json` file. The connector attempts to contact the first connector server in the list. If that connector server is down, it proceeds to the next connector server. + +The following sample configuration defines two remote connector servers, on hosts `remote-host-1` and `remote-host-2`. These servers are listed, by their `name` property in a group, specified in the `remoteConnectorServersGroups` property. You can configure multiple servers per group, and multiple groups in a single remote connector server configuration file. + +[source, javascript] +---- +{ + "connectorsLocation" : "connectors", + "remoteConnectorServers" : [ + { + "name" : "dotnet1", + "host" : "remote-host-1", + "port" : 8759, + "protocol" : "websocket", + "useSSL" : false, + "timeout" : 0, + "key" : "password" + }, + { + "name" : "dotnet2", + "host" : "remote-host-2", + "port" : 8759, + "protocol" : "websocket", + "useSSL" : false, + "timeout" : 0, + "key" : "password" + } + ], + "remoteConnectorServersGroups" : [ + { + "name" : "dotnet-ha", + "algorithm" : "failover", + "serversList" : [ + {"name": "dotnet1"}, + {"name": "dotnet2"} + ] + } + ] +} +---- +The `algorithm` can be either `failover` or `roundrobin`. If the algorithm is `failover`, requests are always sent to the first connector server in the list, unless it is unavailable, in which case requests are sent to the next connector server in the list. If the algorithm is `roundrobin`, requests are distributed equally between the connector servers in the list, in the order in which they are received. + +Your connector configuration file (`provisioner.openicf-connector-name.json`) references the remote connector server group, rather than a single remote connector server. For example, the following excerpt of a PowerShell connector configuration file references the `dotnet-ha` connector server group from the previous configuration: + +[source, javascript] +---- +{ + "connectorRef" : { + "bundleName" : "MsPowerShell.Connector", + "connectorName" : "Org.ForgeRock.OpenICF.Connectors.MsPowerShell.MsPowerShellConnector", + "connectorHostRef" : "dotnet-ha", + "bundleVersion" : "${openicf.powershell.version}" + }, + ... +---- + +[NOTE] +==== +Failover is not supported between connector servers that are running in legacy mode. Therefore, the configuration of each connector server that is part of the failover group must have the `protocol` property set to `websocket`. +==== + + + +[#openicf-provisioner-conf] +=== Configuring Connectors + +Connectors are configured through the OpenICF provisioner service. Each connector configuration is stored in a file in your project's `conf/` directory, and accessible over REST at the `openidm/conf` endpoint. Configuration files are named `project-dir/conf/provisioner.openicf-name` where __name__ corresponds to the name of the connector. A number of sample connector configurations are available in the `openidm/samples/provisioners` directory. To use these connector configurations, edit the configuration files as required, and copy them to your project's `conf` directory. + +If you are creating your own connector configuration files, __do not include additional dash characters ( - ) in the connector name__, as this might cause problems with the OSGi parser. For example, the name `provisioner.openicf-hrdb.json` is fine. The name `provisioner.openicf-hr-db.json` is not. + +The following example shows a connector configuration for an XML file resource: + +[source, javascript] +---- +{ + "name" : "xml", + "connectorRef" : connector-ref-object, + "producerBufferSize" : integer, + "connectorPoolingSupported" : boolean, true/false, + "poolConfigOption" : pool-config-option-object, + "operationTimeout" : operation-timeout-object, + "configurationProperties" : configuration-properties-object, + "syncFailureHandler" : sync-failure-handler-object, + "resultsHandlerConfig" : results-handler-config-object, + "objectTypes" : object-types-object, + "operationOptions" : operation-options-object +} +---- +The `name` property specifies the name of the system to which you are connecting. This name __must__ be alphanumeric. + +[#connector-reference] +==== Setting the Connector Reference Properties + +The following example shows a connector reference object: + +[source, javascript] +---- +{ + "bundleName" : "org.forgerock.openicf.connectors.xml-connector", + "bundleVersion" : "1.1.0.3", + "connectorName" : "org.forgerock.openicf.connectors.xml.XMLConnector", + "connectorHostRef" : "host" +} +---- +-- + +`bundleName`:: +string, required + ++ +The `ConnectorBundle-Name` of the OpenICF connector. + +`bundleVersion`:: +string, required + ++ +The `ConnectorBundle-Version` of the OpenICF connector. The value can be a single version (such as`1.4.0.0`) or a range of versions, which enables you to support multiple connector versions in a single project. + ++ +You can specify a range of versions as follows: ++ + +* `[1.1.0.0,1.4.0.0]` indicates that all connector versions from 1.1 to 1.4, inclusive, are supported. + +* `[1.1.0.0,1.4.0.0)` indicates that all connector versions from 1.1 to 1.4, including 1.1 but excluding 1.4, are supported. + +* `(1.1.0.0,1.4.0.0]` indicates that all connector versions from 1.1 to 1.4, excluding 1.1 but including 1.4, are supported. + +* `(1.1.0.0,1.4.0.0)` indicates that all connector versions from 1.1 to 1.4, exclusive, are supported. + ++ +When a range of versions is specified, OpenIDM uses the latest connector that is available within that range. If your project requires a specific connector version, you must explicitly state the version in your connector configuration file, or constrain the range to address only the version that you need. + +`connectorName`:: +string, required + ++ +The connector implementation class name. + +`connectorHostRef`:: +string, optional + ++ +If the connector runs remotely, the value of this field must match the `name` field of the `RemoteConnectorServers` object in the connector server configuration file (`provisioner.openicf.connectorinfoprovider.json`). For example: ++ + +[source] +---- +... + "remoteConnectorServers" : + [ + { + "name" : "dotnet", +... +---- ++ +If the connector runs locally, the value of this field can be one of the following: + +* If the connector .jar is installed in `openidm/connectors/`, the value must be `"#LOCAL"`. This is currently the default, and recommended location. + +* If the connector .jar is installed in `openidm/bundle/` (not recommended), the value must be `"osgi:service/org.forgerock.openicf.framework.api.osgi.ConnectorManager"`. + + +-- + + +[#pool-configuration-option] +==== Setting the Pool Configuration + +The `poolConfigOption` specifies the pool configuration for poolable connectors only (connectors that have `"connectorPoolingSupported" : true`). Non-poolable connectors ignore this parameter. + +The following example shows a pool configuration option object for a poolable connector: + +[source, javascript] +---- +{ + "maxObjects" : 10, + "maxIdle" : 10, + "maxWait" : 150000, + "minEvictableIdleTimeMillis" : 120000, + "minIdle" : 1 +} +---- +-- + +`maxObjects`:: +The maximum number of idle and active instances of the connector. + +`maxIdle`:: +The maximum number of idle instances of the connector. + +`maxWait`:: +The maximum time, in milliseconds, that the pool waits for an object before timing out. A value of `0` means that there is no timeout. + +`minEvictableIdleTimeMillis`:: +The maximum time, in milliseconds, that an object can be idle before it is removed. A value of `0` means that there is no idle timeout. + +`minIdle`:: +The minimum number of idle instances of the connector. + +-- + + +[#operation-timeout] +==== Setting the Operation Timeouts + +The operation timeout property enables you to configure timeout values per operation type. By default, no timeout is configured for any operation type. A sample configuration follows: + +[source, javascript] +---- +{ + "CREATE" : -1, + "TEST" : -1, + "AUTHENTICATE" : -1, + "SEARCH" : -1, + "VALIDATE" : -1, + "GET" : -1, + "UPDATE" : -1, + "DELETE" : -1, + "SCRIPT_ON_CONNECTOR" : -1, + "SCRIPT_ON_RESOURCE" : -1, + "SYNC" : -1, + "SCHEMA" : -1 +} +---- +-- + +__operation-name__:: +Timeout in milliseconds + ++ +A value of `-1` disables the timeout. + +-- + + +[#configuration-properties] +==== Setting the Connection Configuration + +The `configurationProperties` object specifies the configuration for the connection between the connector and the resource, and is therefore resource specific. + +The following example shows a configuration properties object for the default XML sample resource connector: + +[source, javascript] +---- +"configurationProperties" : { + "xsdIcfFilePath" : "&{launcher.project.location}/data/resource-schema-1.xsd", + "xsdFilePath" : "&{launcher.project.location}/data/resource-schema-extension.xsd", + "xmlFilePath" : "&{launcher.project.location}/data/xmlConnectorData.xml" +} +---- +-- + +__property__:: +Individual properties depend on the type of connector. + +-- + + +[#sync-failure-handler] +==== Setting the Synchronization Failure Configuration + +The `syncFailureHandler` object specifies what should happen if a LiveSync operation reports a failure for an operation. The following example shows a synchronization failure configuration: + +[source, javascript] +---- +{ + "maxRetries" : 5, + "postRetryAction" : "logged-ignore" +} +---- +-- + +`maxRetries`:: +positive integer or `-1`, required + ++ +The number of attempts that OpenIDM should make to process a failed modification. A value of zero indicates that failed modifications should not be reattempted. In this case, the post retry action is executed immediately when a LiveSync operation fails. A value of -1 (or omitting the `maxRetries` property, or the entire `syncFailureHandler` object) indicates that failed modifications should be retried an infinite number of times. In this case, no post retry action is executed. + +`postRetryAction`:: +string, required + ++ +The action that should be taken if the synchronization operation fails after the specified number of attempts. The post retry action can be one of the following: ++ + +* `logged-ignore` indicates that OpenIDM should ignore the failed modification, and log its occurrence. + +* `dead-letter-queue` indicates that OpenIDM should save the details of the failed modification in a table in the repository (accessible over REST at `repo/synchronisation/deadLetterQueue/provisioner-name`). + +* `script` specifies a custom script that should be executed when the maximum number of retries has been reached. + ++ +For more information, see xref:chap-synchronization.adoc#livesync-retry-strategy["Configuring the LiveSync Retry Policy"]. + +-- + + +[#results-handler-config] +==== Configuring How Results Are Handled + +The `resultsHandlerConfig` object specifies how OpenICF returns results. These configuration properties depend on the connector type and on the interfaces that are implemented by that connector type. For information the interfaces that each connector supports, see the link:http://openicf.forgerock.org/doc/config-reference[OpenICF Connector Configuration Reference, window=\_blank]. + +The following example shows a results handler configuration object: + +[source, javascript] +---- +{ + "enableNormalizingResultsHandler" : true, + "enableFilteredResultsHandler" : false, + "enableCaseInsensitiveFilter" : false, + "enableAttributesToGetSearchResultsHandler" : false +} +---- +-- + +`enableNormalizingResultsHandler`:: +boolean + ++ +If the connector implements the attribute normalizer interface, you can enable this interface by setting this configuration property to `true`. If the connector does not implement the attribute normalizer interface, the value of this property has no effect. + +`enableFilteredResultsHandler`:: +boolean + ++ +If the connector uses the filtering and search capabilities of the remote connected system, you can set this property to `false`. If the connector does not use the remote system's filtering and search capabilities (for example, the CSV file connector), you __must__ set this property to `true`, otherwise the connector performs an additional, case-sensitive search, which can cause problems. + +`enableCaseInsensitiveFilter`:: +boolean + ++ +By default, the filtered results handler (described previously) is case-sensitive. If the filtered results handler is enabled, you can use this property to enable case-insensitive filtering. If you do not enable case-insensitive filtering, a search will not return results unless the case matches exactly. For example, a search for `lastName = "Jensen"` will not match a stored user with `lastName : jensen`. + +`enableAttributesToGetSearchResultsHandler`:: +boolean + ++ +By default, OpenIDM determines which attributes should be retrieved in a search. If the `enableAttributesToGetSearchResultsHandler` property is set to `true` the OpenICF framework removes all attributes from the READ/QUERY response, except for those that are specifically requested. For performance reasons, you should set this property to `false` for local connectors and to `true` for remote connectors. + +-- + + +[#object-types] +==== Specifying the Supported Object Types + +The `object-types` configuration specifies the objects (user, group, and so on) that are supported by the connector. The property names set here define the `objectType` that is used in the URI. For example: + +[source] +---- +system/systemName/objectType +---- +This configuration is based on the link:http://tools.ietf.org/html/draft-zyp-json-schema-03[JSON Schema, window=\_blank] with the extensions described in the following section. + +Attribute names that start or end with `__` are regarded as __special attributes__ by OpenICF. The purpose of the special attributes in OpenICF is to enable someone who is developing a __new__ connector to create a contract regarding how a property can be referenced, regardless of the application that is using the connector. In this way, the connector can map specific object information between an arbitrary application and the resource, without knowing how that information is referenced in the application. + +These attributes have no specific meaning in the context of OpenIDM, although some of the connectors that are bundled with OpenIDM use these attributes. The generic LDAP connector, for example, can be used with OpenDJ, Active Directory, OpenLDAP, and other LDAP directories. Each of these directories might use a different attribute name to represent the same type of information. For example, Active Directory uses `unicodePassword` and OpenDJ uses `userPassword` to represent the same thing, a user's password. The LDAP connector uses the special OpenICF `__PASSWORD__` attribute to abstract that difference. In the same way, the LDAP connector maps the `__NAME__` attribute to an LDAP `dn`. + +The OpenICF `__UID__` is a special case. The `__UID__` __must not__ be included in the OpenIDM configuration or in any update or create operation. This attribute denotes the unique identity attribute of an object and OpenIDM always maps it to the `_id` of the object. + +The following excerpt shows the configuration of an `account` object type: + +[source, javascript] +---- +{ + "account" : + { + "$schema" : "http://json-schema.org/draft-03/schema", + "id" : "__ACCOUNT__", + "type" : "object", + "nativeType" : "__ACCOUNT__", + "properties" : + { + "name" : + { + "type" : "string", + "nativeName" : "__NAME__", + "nativeType" : "JAVA_TYPE_PRIMITIVE_LONG", + "flags" : + [ + "NOT_CREATABLE", + "NOT_UPDATEABLE", + "NOT_READABLE", + "NOT_RETURNED_BY_DEFAULT" + ] + }, + "groups" : + { + "type" : "array", + "items" : + { + "type" : "string", + "nativeType" : "string" + }, + "nativeName" : "__GROUPS__", + "nativeType" : "string", + "flags" : + [ + "NOT_RETURNED_BY_DEFAULT" + ] + }, + "givenName" : { + "type" : "string", + "nativeName" : "givenName", + "nativeType" : "string" + }, + } + } +} +---- +OpenICF supports an `__ALL__` object type that ensures that objects of every type are included in a synchronization operation. The primary purpose of this object type is to prevent synchronization errors when multiple changes affect more than one object type. + +For example, imagine a deployment synchronizing two external systems. On system A, the administrator creates a user, `jdoe`, then adds the user to a group, `engineers`. When these changes are synchronized to system B, if the `__GROUPS__` object type is synchronized first, the synchronization will fail, because the group contains a user that does not yet exist on system B. Synchronizing the `__ALL__` object type ensures that user `jdoe` is created on the external system before he is added to the group `engineers`. + +The `__ALL__` object type is assumed by default - you do not need to declare it in your provisioner configuration file. If it is not declared, the object type is named `__ALL__`. If you want to map a different name for this object type, declare it in your provisioner configuration. The following excerpt from a sample provisioner configuration uses the name `allobjects`: + +[source, javascript] +---- +"objectTypes": { + "allobjects": { + "$schema": "http://json-schema.org/draft-03/schema", + "id": "__ALL__", + "type": "object", + "nativeType": "__ALL__" + }, +... +---- +A LiveSync operation invoked with no object type assumes an object type of `__ALL__`. For example, the following call invokes a LiveSync operation on all defined object types in an LDAP system: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ldap?_action=liveSync" +---- + +[NOTE] +==== +Using the `__ALL__` object type requires a mechanism to ensure the order in which synchronization changes are processed. Servers that use the `cn=changelog` mechanism to order sync changes (such as OpenDJ, Oracle DSEE, and the legacy Sun Directory Server) cannot use the `__ALL__` object type by default, and must be forced to use time stamps to order their sync changes. For these LDAP server types, set `useTimestampsForSync` to `true` in the provisioner configuration. + +LDAP servers that use timestamps by default (such as Active Directory GCs and OpenLDAP) can use the `__ALL__` object type without any additional configuration. Active Directory and Active Directory LDS, which use Update Sequence Numbers, can also use the `__ALL__` object type without additional configuration. +==== + +[#object-level-extensions] +===== Extending the Object Type Configuration + +-- + +`nativeType`:: +string, optional + ++ +The native OpenICF object type. + ++ +The list of supported native object types is dependent on the resource, or on the connector. For example, an LDAP connector might have object types such as `__ACCOUNT__` and `__GROUP__`. + +-- + + +[#property-level-extensions] +===== Extending the Property Type Configuration + +-- + +`nativeType`:: +string, optional + ++ +The native OpenICF attribute type. + ++ +The following native types are supported: ++ + +[source, console] +---- +JAVA_TYPE_BIGDECIMAL +JAVA_TYPE_BIGINTEGER +JAVA_TYPE_BYTE +JAVA_TYPE_BYTE_ARRAY +JAVA_TYPE_CHAR +JAVA_TYPE_CHARACTER +JAVA_TYPE_DATE +JAVA_TYPE_DOUBLE +JAVA_TYPE_FILE +JAVA_TYPE_FLOAT +JAVA_TYPE_GUARDEDBYTEARRAY +JAVA_TYPE_GUARDEDSTRING +JAVA_TYPE_INT +JAVA_TYPE_INTEGER +JAVA_TYPE_LONG +JAVA_TYPE_OBJECT +JAVA_TYPE_PRIMITIVE_BOOLEAN +JAVA_TYPE_PRIMITIVE_BYTE +JAVA_TYPE_PRIMITIVE_DOUBLE +JAVA_TYPE_PRIMITIVE_FLOAT +JAVA_TYPE_PRIMITIVE_LONG +JAVA_TYPE_STRING +---- ++ + +[NOTE] +====== +The `JAVA_TYPE_DATE` property is deprecated. Functionality may be removed in a future release. This property-level extension is an alias for `string`. Any dates assigned to this extension should be formatted per ISO 8601. +====== + +`nativeName`:: +string, optional + ++ +The native OpenICF attribute name. + +`flags`:: +string, optional + ++ +The native OpenICF attribute flags. OpenICF supports the following attribute flags: ++ + +* `MULTIVALUED` - specifies that the property can be multivalued. This flag sets the `type` of the attribute as follows: ++ + +[source] +---- +"type" : "array" +---- ++ +If the attribute type is `array`, an additional `items` field specifies the supported type for the objects in the array. For example: ++ + +[source, javascript] +---- +"groups" : + { + "type" : "array", + "items" : + { + "type" : "string", + "nativeType" : "string" + }, + .... +---- + +* `NOT_CREATABLE`, `NOT_READABLE`, `NOT_RETURNED_BY_DEFAULT`, `NOT_UPDATEABLE` ++ +In some cases, the connector might not support manipulating an attribute because the attribute can only be changed directly on the remote system. For example, if the `name` attribute of an account can only be created by Active Directory, and __never__ changed by OpenIDM, you would add `NOT_CREATABLE` and `NOT_UPDATEABLE` to the provisioner configuration for that attribute. ++ +Certain attributes such as LDAP groups or other calculated attributes might be expensive to read. You might want to avoid returning these attributes in a default read of the object, unless they are explicitly requested. In this case, you would add the `NOT_RETURNED_BY_DEFAULT` flag to the provisioner configuration for that attribute. + +* `REQUIRED` - specifies that the property is required in create operations. This flag sets the `required` property of an attribute as follows: ++ + +[source] +---- +"required" : true +---- + + +-- + +[NOTE] +==== +Do not use the dash character ( `-` ) in property names, like `last-name`. Dashes in names make JavaScript syntax more complex. If you cannot avoid the dash, write `source['last-name']` instead of `source.last-name` in your JavaScript scripts. +==== + + + +[#operation-options] +==== Configuring the Operation Options + +The `operationOptions` object enables you to deny specific operations on a resource. For example, you can use this configuration object to deny `CREATE` and `DELETE` operations on a read-only resource to avoid OpenIDM accidentally updating the resource during a synchronization operation. + +The following example defines the options for the `"SYNC"` operation: + +[source, javascript] +---- +"operationOptions" : { + { + "SYNC" : + { + "denied" : true, + "onDeny" : "DO_NOTHING", + "objectFeatures" : + { + "__ACCOUNT__" : + { + "denied" : true, + "onDeny" : "THROW_EXCEPTION", + "operationOptionInfo" : + { + "$schema" : "http://json-schema.org/draft-03/schema", + "id" : "FIX_ME", + "type" : "object", + "properties" : + { + "_OperationOption-float" : + { + "type" : "number", + "nativeType" : "JAVA_TYPE_PRIMITIVE_FLOAT" + } + } + } + }, + "__GROUP__" : + { + "denied" : false, + "onDeny" : "DO_NOTHING" + } + } + } + } +... +---- +The OpenICF Framework supports the following operations: + +* `AUTHENTICATE`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/AuthenticationApiOp.html[AuthenticationApiOp, window=\_blank] + +* `CREATE`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/CreateApiOp.html[CreateApiOp, window=\_blank] + +* `DELETE`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/DeleteApiOp.html[DeleteApiOp, window=\_blank] + +* `GET`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/GetApiOp.html[GetApiOp, window=\_blank] + +* `RESOLVEUSERNAME`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/ResolveUsernameApiOp.html[ResolveUsernameApiOp, window=\_blank] + +* `SCHEMA`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/SchemaApiOp.html[SchemaApiOp, window=\_blank] + +* `SCRIPT_ON_CONNECTOR`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/ScriptOnConnectorApiOp.html[ScriptOnConnectorApiOp, window=\_blank] + +* `SCRIPT_ON_RESOURCE`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/ScriptOnResourceApiOp.html[ScriptOnResourceApiOp, window=\_blank] + +* `SEARCH`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/SearchApiOp.html[SearchApiOp, window=\_blank] + +* `SYNC`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/SyncApiOp.html[SyncApiOp, window=\_blank] + +* `TEST`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/TestApiOp.html[TestApiOp, window=\_blank] + +* `UPDATE`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/UpdateApiOp.html[UpdateApiOp, window=\_blank] + +* `VALIDATE`: link:http://openicf.forgerock.org/connector-framework/apidocs/org/identityconnectors/framework/api/operations/ValidateApiOp.html[ValidateApiOp, window=\_blank] + +-- +The `operationOptions` object has the following configurable properties: + +`denied`:: +boolean, optional + ++ +This property prevents operation execution if the value is `true`. + +`onDeny`:: +string, optional + ++ +If `denied` is `true`, then the service uses this value. Default value: `DO_NOTHING`. ++ + +* `DO_NOTHING`: On operation the service does nothing. + +* `THROW_EXCEPTION`: On operation the service throws a `ForbiddenException` exception. + + +-- + + + +[#installing-connector-servers] +=== Installing and Configuring Remote Connector Servers + +Connectors that use the .NET framework __must__ run remotely. Java connectors can run locally or remotely. Connectors that run remotely require a connector server to enable OpenIDM to access the connector. + +This section describes the steps to install a .NET connector server and a remote Java Connector Server. + +[#install-_net-connector] +==== Installing and Configuring a .NET Connector Server + +A .NET connector server is useful when an application is written in Java, but a connector bundle is written using C#. Because a Java application (for example, a J2EE application) cannot load C# classes, you must deploy the C# bundles under a .NET connector server. The Java application can communicate with the C# connector server over the network, and the C# connector server acts as a proxy to provide access to the C# bundles that are deployed within the C# connector server, to any authenticated application. + +By default, the connector server outputs log messages to a file named `connectorserver.log`, in the `C:\path\to\openicf` directory. To change the location of the log file set the `initializeData` parameter in the configuration file, before you install the connector server. For example, the following excerpt sets the log directory to `C:\openicf\logs\connectorserver.log`: + +[source, xml] +---- + + + +---- + +[IMPORTANT] +==== +Version 1.5 of the .NET connector server includes a new communication protocol that is enabled by default. Currently the new protocol has specific known stability issues. You should therefore run the 1.5 .NET connector server in legacy mode, with the old protocol, as described in xref:#run-_NET-in-legacy-mode["Running the .NET Connector Server in Legacy Mode"]. +==== + +[#net-connector-install] +.Installing the .NET Connector Server +==== + +. Download the OpenICF .NET Connector Server from the ForgeRock link:https://backstage.forgerock.com/[BackStage, window=\_blank] site. ++ +The .NET connector server is distributed in two formats. The `.msi` file is a wizard that installs the Connector Server as a Windows Service. The `.zip` file is simply a bundle of all the files required to run the Connector Server. ++ + +* If you do __not__ want to run the Connector Server as a Windows service, download and extract the `.zip` file, then move on to xref:#net-connector-configure["Configuring the .NET Connector Server"]. + +* If you have deployed the `.zip` file and then decide to run the Connector Server as a service, install the service manually with the following command: ++ + +[source, console] +---- +.\ConnectorServerService.exe /install /serviceName service-name +---- ++ +Then proceed to xref:#net-connector-configure["Configuring the .NET Connector Server"]. + +* To install the Connector Server as a Windows service automatically, follow the remaining steps in this section. + + +. Execute the `openicf-zip-1.5.0.1-dotnet.msi` installation file and complete the wizard. ++ +You must run the wizard as a user who has permissions to start and stop a Windows service, otherwise the service will not start. ++ +When you choose the Setup Type, select Typical unless you require backward compatibility with the 1.4.0.0 connector server. If you need backward compatibility, select Custom, and install the Legacy Connector Service. ++ +When the wizard has completed, the Connector Server is installed as a Windows Service. + +. Open the Microsoft Services Console and make sure that the Connector Server is listed there. ++ +The name of the service is `OpenICF Connector Server`, by default. ++ + +image::images/dotnet-service.png[] + +==== + +[#run-_NET-in-legacy-mode] +.Running the .NET Connector Server in Legacy Mode +==== + +. If you are installing the .NET Connector Server from the `.msi` distribution, select Custom for the Setup Type, and install the Legacy Connector Service. + +. If you are installing the .NET Connector Server from the `.zip` distribution, launch the Connector Server by running the `ConnectorServer.exe` command, and __not__ the `ConnectorServerService.exe` command. + +. Adjust the `port` parameter in your OpenIDM remote connector server configuration file. In legacy mode, the connector server listens on port `8760` by default. + +. Remove the `"protocol" : "websocket",` from your OpenIDM remote connector server configuration file to specify that the connector server should use the legacy protocol. + +. In the commands shown in xref:#net-connector-configure["Configuring the .NET Connector Server"], replace `ConnectorServerService.exe` with `ConnectorServer.exe`. + +==== + +[#net-connector-configure] +.Configuring the .NET Connector Server +==== +After you have installed the .NET Connector Server, as described in the previous section, follow these steps to configure the Connector Server: + +. Make sure that the Connector Server is not currently running. If it is running, use the Microsoft Services Console to stop it. + +. At the command prompt, change to the directory where the Connector Server was installed: ++ + +[source, console] +---- +c:\> cd "c:\Program Files (x86)\ForgeRock\OpenICF" +---- + +. Run the `ConnectorServerService /setkey` command to set a secret key for the Connector Server. The key can be any string value. This example sets the secret key to `Passw0rd`: ++ + +[source, console] +---- +ConnectorServerService /setkey Passw0rd +Key has been successfully updated. +---- ++ +This key is used by clients connecting to the Connector Server. The key that you set here must also be set in the OpenIDM connector info provider configuration file (`conf/provisioner.openicf.connectorinfoprovider.json`). For more information, see xref:#net-connector-openidm["Configuring OpenIDM to Connect to the .NET Connector Server"]. + +. Edit the Connector Server configuration. ++ +The Connector Server configuration is saved in a file named `ConnectorServerService.exe.Config` (in the directory in which the Connector Server is installed). ++ +Check and edit this file, as necessary, to reflect your installation. Specifically, verify that the `baseAddress` reflects the host and port on which the connector server is installed: ++ + +[source, console] +---- + + + + + + + + + + + +---- ++ +The `baseAddress` specifies the host and port on which the Connector Server listens, and is set to `\http://0.0.0.0:8759/openicf` by default. If you set a host value other than the default `0.0.0.0`, connections from all IP addresses other than the one specified are denied. ++ +If Windows firewall is enabled, you must create an inbound port rule to open the TCP port for the connector server (8759 by default). If you do not open the TCP port, OpenIDM will be unable to contact the Connector Server. For more information, see the Microsoft documentation on link:http://technet.microsoft.com/en-us/library/cc947814(v=ws.10).aspx[creating an inbound port rule, window=\_blank]. + +. Optionally, configure the Connector Server to use SSL: ++ + +.. Use an existing CA certificate, or use the `makecert` utility to create an exportable self-signed Root CA Certificate: ++ + +[source, console] +---- +c:\"Program Files (x86)"\"Windows Kits"\8.1\bin\x64\makecert.exe ^ +-pe -r -sky signature -cy authority -a sha1 -n "CN=Dev Certification Authority" ^ +-ss Root -sr LocalMachine -sk RootCA signroot.cer +---- + +.. Create an exportable server authentication certificate: ++ + +[source, console] +---- +c:\"Program Files (x86)"\"Windows Kits"\8.1\bin\x64\makecert.exe ^ +-pe -sky exchange -cy end -n "CN=localhost" -b 01/01/2015 -e 01/01/2050 -eku 1.3.6.1.5.5.7.3.1 ^ +-ir LocalMachine -is Root -ic signroot.cer -ss My -sr localMachine -sk server ^ +-sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 server.cer +---- + +.. Retrieve and set the certificate thumbprint: ++ + +[source, console] +---- +c:\Program Files (x86)\ForgeRock\OpenICF>ConnectorServerService.exe /setCertificate +Select certificate you want to use: +Index Issued To Thumbprint +----- --------- ------------------------- + 0) localhost 4D01BE385BF079DD4B9C5A416E7B535904855E0A + +Certificate Thumbprint has been successfully updated to 4D01BE385BF079DD4B9C5A416E7B535904855E0A. +---- + +.. Bind the certificate to the Connector Server port. For example: ++ + +[source, console] +---- +netsh http add sslcert ipport=0.0.0.0:8759 ^ +certhash=4D01BE385BF079DD4B9C5A416E7B535904855E0A ^ +appid={bca0631d-cab1-48c8-bd2a-eb049d7d3c55} +---- + +.. Execute Service as a non-administrative user: ++ + +[source, console] +---- +netsh http add urlacl url=https://+:8759/ user=EVERYONE +---- + +.. Change the Connector Server configuration to use HTTPS and not HTTP: ++ + +[source, console] +---- + +---- + + +. Check the trace settings, in the same Connector Server configuration file, under the `system.diagnostics` item: ++ + +[source, console] +---- + + + + + + + + + + + + + + + + + + + + + + + + + + +---- ++ +The Connector Server uses the standard .NET trace mechanism. For more information about tracing options, see link:http://msdn.microsoft.com/en-us/library/15t15zda(v=vs.71).aspx[Microsoft's .NET documentation, window=\_blank] for `System.Diagnostics`. ++ +The default trace settings are a good starting point. For less tracing, set the EventTypeFilter's `initializeData` to `Warning` or `Error`. For very verbose logging set the value to `Verbose` or `All`. The logging level has a direct effect on the performance of the Connector Servers, so take care when setting this level. + +==== + +[#net-connector-start] +.Starting the .NET Connector Server +==== +Start the .NET Connector Server in one of the following ways: + +. Start the server as a Windows service, by using the Microsoft Services Console. ++ +Locate the connector server service (`OpenICF Connector Server`), and click `Start the service` or `Restart the service`. ++ +The service is executed with the credentials of the "run as" user (`System`, by default). + +. Start the server as a Windows service, by using the command line. ++ +In the Windows Command Prompt, run the following command: ++ + +[source, console] +---- +net start ConnectorServerService +---- ++ +To stop the service in this manner, run the following command: ++ + +[source, console] +---- +net stop ConnectorServerService +---- + +. Start the server without using Windows services. ++ +In the Windows Command Prompt, change directory to the location where the Connector Server was installed. The default location is `c:\> cd "c:\Program Files (x86)\ForgeRock\OpenICF"`. ++ +Start the server with the following command: ++ + +[source, console] +---- +ConnectorServerService.exe /run +---- ++ +Note that this command starts the Connector Server with the credentials of the current user. It does not start the server as a Windows service. + +==== + +[#net-connector-openidm] +.Configuring OpenIDM to Connect to the .NET Connector Server +==== +The connector info provider service configures one or more remote connector servers to which OpenIDM can connect. The connector info provider configuration is stored in a file named `project-dir/conf/provisioner.openicf.connectorinfoprovider.json`. A sample connector info provider configuration file is located in `openidm/samples/provisioners/`. + +To configure OpenIDM to use the remote .NET connector server, follow these steps: + +. Start OpenIDM, if it is not already running. + +. Copy the sample connector info provider configuration file to your project's `conf/` directory: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ cp samples/provisioners/provisioner.openicf.connectorinfoprovider.json project-dir/conf/ +---- + +. Edit the connector info provider configuration, specifying the details of the remote connector server: ++ + +[source, javascript] +---- +"remoteConnectorServers" : [ + { + "name" : "dotnet", + "host" : "192.0.2.0", + "port" : 8759, + "useSSL" : false, + "timeout" : 0, + "protocol" : "websocket", + "key" : "Passw0rd" + } +---- ++ +Configurable properties are as follows: ++ +-- + +`name`:: +Specifies the name of the connection to the .NET connector server. The name can be any string. This name is referenced in the `connectorHostRef` property of the connector configuration file (`provisioner.openicf-ad.json`). + +`host`:: +Specifies the IP address of the host on which the Connector Server is installed. + +`port`:: +Specifies the port on which the Connector Server listens. This property matches the `connectorserver.port` property in the `ConnectorServerService.exe.config` file. ++ +For more information, see xref:#net-connector-configure["Configuring the .NET Connector Server"]. + +`useSSL`:: +Specifies whether the connection to the Connector Server should be secured. This property matches the `"connectorserver.usessl"` property in the `ConnectorServerService.exe.config` file. + +`timeout`:: +Specifies the length of time, in seconds, that OpenIDM should attempt to connect to the Connector Server before abandoning the attempt. To disable the timeout, set the value of this property to `0`. + +`protocol`:: +Version 1.5.0.0 of the OpenICF framework supports a new communication protocol with remote connector servers. This protocol is enabled by default, and its value is `websocket` in the default configuration. ++ +Currently, the new, default protocol has specific known issues. You should therefore run the 1.5 .NET Connector Server in legacy mode, with the old protocol, as described in xref:#run-_NET-in-legacy-mode["Running the .NET Connector Server in Legacy Mode"]. + +`key`:: +Specifies the connector server key. This property matches the `key` property in the `ConnectorServerService.exe.config` file. For more information, see xref:#net-connector-configure["Configuring the .NET Connector Server"]. ++ +The string value that you enter here is encrypted as soon as the file is saved. + +-- + +==== + + +[#install-standalone-connector] +==== Installing and Configuring a Remote Java Connector Server + +In certain situations, it might be necessary to set up a remote Java Connector Server. This section provides instructions for setting up a remote Java Connector Server on Unix/Linux and Windows. + +[#java-connector-server-unix] +.Installing a Remote Java Connector Server for Unix/Linux +==== + +. Download the OpenICF Java Connector Server from the ForgeRock link:https://backstage.forgerock.com[Backstage, window=\_blank] site. + +. Change to the appropriate directory and unpack the zip file. The following command unzips the file in the current directory: ++ + +[source, console] +---- +$ unzip openicf-zip-1.5.0.1.zip +---- + +. Change to the `openicf` directory: ++ + +[source, console] +---- +$ cd path/to/openicf +---- + +. The Java Connector Server uses a `key` property to authenticate the connection. The default key value is `changeit`. To change the value of the secret key, run a command similar to the following. This example sets the key value to `Passw0rd`: ++ + +[source, console] +---- +$ cd /path/to/openicf +$ bin/ConnectorServer.sh /setkey Passw0rd +Key has been successfully updated. +---- + +. Review the `ConnectorServer.properties` file in the `/path/to/openicf/conf` directory, and make any required changes. By default, the configuration file has the following properties: ++ + +[source] +---- +connectorserver.port=8759 +connectorserver.libDir=lib +connectorserver.usessl=false +connectorserver.bundleDir=bundles +connectorserver.loggerClass=org.forgerock.openicf.common.logging.slf4j.SLF4JLog +connectorserver.key=xOS4IeeE6eb/AhMbhxZEC37PgtE\= +---- ++ +The `connectorserver.usessl` parameter indicates whether client connections to the connector server should be over SSL. This property is set to `false` by default. ++ +To secure connections to the connector server, set this property to `true` and set the following properties before you start the connector server: ++ + +[source, console] +---- +java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=Passw0rd +---- + +. Start the Java Connector Server: ++ + +[source, console] +---- +$ bin/ConnectorServer.sh /run +---- ++ +The connector server is now running, and listening on port 8759, by default. ++ +Log files are available in the `/path/to/openicf/logs` directory. ++ + +[source, console] +---- +$ ls logs/ +Connector.log ConnectorServer.log ConnectorServerTrace.log +---- + +. If required, stop the Java Connector Server by pressing CTRL-C. + +==== + +[#java-connector-server-windows] +.Installing a Remote Java Connector Server for Windows +==== + +. Download the OpenICF Java Connector Server from the ForgeRock link:https://backstage.forgerock.com[Backstage, window=\_blank] site. + +. Change to the appropriate directory and unpack the zip file. + +. In a Command Prompt window, change to the `openicf` directory: ++ + +[source, console] +---- +C:\>cd C:\path\to\openicf\bin +---- + +. If required, secure the communication between OpenIDM and the Java Connector Server. The Java Connector Server uses a `key` property to authenticate the connection. The default key value is `changeit`. ++ +To change the value of the secret key, use the `bin\ConnectorServer.bat /setkey` command. The following example sets the key to `Passw0rd`: ++ + +[source, console] +---- +c:\path\to\openicf>bin\ConnectorServer.bat /setkey Passw0rd +lib\framework\connector-framework.jar;lib\framework\connector-framework-internal +.jar;lib\framework\groovy-all.jar;lib\framework\icfl-over-slf4j.jar;lib\framework +\slf4j-api.jar;lib\framework\logback-core.jar;lib\framework\logback-classic.jar +---- + +. Review the `ConnectorServer.properties` file in the `path\to\openicf\conf` directory, and make any required changes. By default, the configuration file has the following properties: ++ + +[source] +---- +connectorserver.port=8759 +connectorserver.libDir=lib +connectorserver.usessl=false +connectorserver.bundleDir=bundles +connectorserver.loggerClass=org.forgerock.openicf.common.logging.slf4j.SLF4JLog +connectorserver.key=xOS4IeeE6eb/AhMbhxZEC37PgtE\= +---- + +. You can either run the Java Connector Server as a Windows service, or start and stop it from the command-line. ++ + +* To install the Java Connector Server as a Windows service, run the following command: ++ + +[source, console] +---- +c:\path\to\openicf>bin\ConnectorServer.bat /install +---- ++ +If you install the connector server as a Windows service you can use the Microsoft Services Console to start, stop and restart the service. The Java Connector Service is named `OpenICFConnectorServerJava`. ++ +To uninstall the Java Connector Server as a Windows service, run the following command: ++ + +[source, console] +---- +c:\path\to\openicf>bin\ConnectorServer.bat /uninstall +---- + + +. To start the Java Connector Server from the command line, enter the following command: ++ + +[source, console] +---- +c:\path\to\openicf>bin\ConnectorServer.bat /run +---- ++ +The connector server is now running, and listening on port 8759, by default. ++ +Log files are available in the `\path\to\openicf\logs` directory. + +. If required, stop the Java Connector Server by pressing `^C`. + +==== + + + +[#connectors-with-openidm] +=== Connectors Supported With OpenIDM 4.5 + +OpenIDM 4.5 provides several connectors by default, in the `path/to/openidm/connectors` directory. The supported connectors that are not bundled with OpenIDM, and a number of additional connectors, can be downloaded from the link:http://openicf.forgerock.org/connectors/[OpenICF community site, window=\_blank]. + +For details about the connectors that are supported for use with OpenIDM 4.5, see xref:../connectors-guide/index.adoc[Connectors Guide]. + + +[#connector-wiz] +=== Creating Default Connector Configurations + +You have three ways to create provisioner files: + +* Start with the sample provisioner files in the `/path/to/openidm/samples/provisioners` directory. For more information, see xref:#connectors-with-openidm["Connectors Supported With OpenIDM 4.5"]. + +* Set up connectors with the help of the Admin UI. To start this process, navigate to `\https://localhost:8443/admin` and log in to OpenIDM. Continue with xref:#connector-wiz-adminui["Adding New Connectors from the Admin UI"]. + +* Use the service that OpenIDM exposes through the REST interface to create basic connector configuration files, or use the `cli.sh` or `cli.bat` scripts to generate a basic connector configuration. To see how this works continue with xref:#connector-wiz-cli["Adding New Connectors from the Command Line"]. + + +[#connector-wiz-adminui] +==== Adding New Connectors from the Admin UI + +You can include several different connectors in an OpenIDM configuration. In the Admin UI, select Configure > Connector. Try some of the different connector types in the screen that appears. Observe as the Admin UI changes the configuration options to match the requirements of the connector type. + +The list of connectors shown in the Admin UI does not include all supported connectors. For information and examples of how each supported connector is configured, see xref:#connectors-with-openidm["Connectors Supported With OpenIDM 4.5"]. + +When you have filled in all required text boxes, the Admin UI allows you to validate the connector configuration. + +If you want to configure a different connector through the Admin UI, you could copy the provisioner file from the `/path/to/openidm/samples/provisioners` directory. However, additional configuration may be required, as described in xref:#connectors-with-openidm["Connectors Supported With OpenIDM 4.5"]. + +Alternatively, some connectors are included with the configuration of a specific sample. For example, if you want to build a ScriptedSQL connector, read xref:../samples-guide/chap-groovy-samples.adoc#more-sample3["Sample 3 - Using the Custom Scripted Connector Bundler to Build a ScriptedSQL Connector"] in the __Samples Guide__. + + +[#connector-wiz-cli] +==== Adding New Connectors from the Command Line + +This section describes how to create connector configurations over the REST interface. For instructions on how to create connector configurations from the command line, see xref:chap-cli.adoc#cli-configureconnector["Using the configureconnector Subcommand"]. +You create a new connector configuration file in three stages: + +. List the available connectors. + +. Generate the core configuration. + +. Connect to the target system and generate the final configuration. + +List the available connectors by using the following command: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=availableConnectors" +---- +Available connectors are installed in `openidm/connectors`. OpenIDM 4.5 bundles the following connectors: + +* CSV File Connector + +* Database Table Connector + +* Scripted Groovy Connector Toolkit, which includes the following sample implementations: ++ + +** Scripted SQL Connector + +** Scripted CREST Connector + +** Scripted REST Connector + + +* LDAP Connector + +* XML Connector + +* GoogleApps Connector (OpenIDM Enterprise only) + +* Salesforce Connector (OpenIDM Enterprise only) + +The preceding command therefore returns the following output: + +[source, javascript] +---- +{ + "connectorRef": [ + { + "connectorName": "org.forgerock.openicf.connectors.xml.XMLConnector", + "displayName": "XML Connector", + "bundleName": "org.forgerock.openicf.connectors.xml-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.1.0.3" + }, + { + "connectorName": "org.identityconnectors.ldap.LdapConnector", + "displayName": "LDAP Connector", + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.4.1.2" + }, + { + "connectorName": "org.forgerock.openicf.connectors.scriptedsql.ScriptedSQLConnector", + "displayName": "Scripted SQL Connector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.4.2.1" + }, + { + "connectorName": "org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConnector", + "displayName": "Scripted REST Connector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.4.2.1" + }, + { + "connectorName": "org.forgerock.openicf.connectors.scriptedcrest.ScriptedCRESTConnector", + "displayName": "Scripted CREST Connector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.4.2.1" + }, + { + "connectorName": "org.forgerock.openicf.connectors.groovy.ScriptedPoolableConnector", + "displayName": "Scripted Poolable Groovy Connector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.4.2.1" + }, + { + "connectorName": "org.forgerock.openicf.connectors.groovy.ScriptedConnector", + "displayName": "Scripted Groovy Connector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.4.2.1" + }, + { + "connectorName": "org.identityconnectors.databasetable.DatabaseTableConnector", + "displayName": "Database Table Connector", + "bundleName": "org.forgerock.openicf.connectors.databasetable-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.1.0.2" + }, + { + "connectorName": "org.forgerock.openicf.csvfile.CSVFileConnector", + "displayName": "CSV File Connector", + "bundleName": "org.forgerock.openicf.connectors.csvfile-connector", + "systemType": "provisioner.openicf", + "bundleVersion": "1.5.1.4" + } + ] +} +---- +To generate the core configuration, choose one of the available connectors by copying one of the JSON objects from the generated list into the body of the REST command, as shown in the following command for the XML connector: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--request POST \ +--data '{"connectorRef": + {"connectorName": "org.forgerock.openicf.connectors.xml.XMLConnector", + "displayName": "XML Connector", + "bundleName": "org.forgerock.openicf.connectors.xml-connector", + "bundleVersion": "1.1.0.3"} + }' \ + "http//localhost:8080/openidm/system?_action=createCoreConfig" +---- +This command returns a core connector configuration, similar to the following: + +[source, javascript] +---- +{ + "poolConfigOption": { + "minIdle": 1, + "minEvictableIdleTimeMillis": 120000, + "maxWait": 150000, + "maxIdle": 10, + "maxObjects": 10 + }, + "resultsHandlerConfig": { + "enableAttributesToGetSearchResultsHandler": true, + "enableFilteredResultsHandler": true, + "enableNormalizingResultsHandler": true + }, + "operationTimeout": { + "SCHEMA": -1, + "SYNC": -1, + "VALIDATE": -1, + "SEARCH": -1, + "AUTHENTICATE": -1, + "CREATE": -1, + "UPDATE": -1, + "DELETE": -1, + "TEST": -1, + "SCRIPT_ON_CONNECTOR": -1, + "SCRIPT_ON_RESOURCE": -1, + "GET": -1, + "RESOLVEUSERNAME": -1 + }, + "configurationProperties": { + "xsdIcfFilePath": null, + "xsdFilePath": null, + "createFileIfNotExists": false, + "xmlFilePath": null + }, + "connectorRef": { + "bundleVersion": "1.1.0.3", + "bundleName": "org.forgerock.openicf.connectors.xml-connector", + "displayName": "XML Connector", + "connectorName": "org.forgerock.openicf.connectors.xml.XMLConnector" + } +} +---- +The configuration that is returned is not yet functional. Notice that it does not contain the required system-specific `configurationProperties`, such as the host name and port, or the `xmlFilePath` for the XML file-based connector. In addition, the configuration does not include the complete list of `objectTypes` and `operationOptions`. + +To generate the final configuration, add values for the `configurationProperties` to the core configuration, and use the updated configuration as the body for the next command: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--request POST \ +--data '{ + "configurationProperties": + { + "xsdIcfFilePath" : "samples/sample1/data/resource-schema-1.xsd", + "xsdFilePath" : "samples/sample1/data/resource-schema-extension.xsd", + "xmlFilePath" : "samples/sample1/data/xmlConnectorData.xml", + "createFileIfNotExists": false + }, + "operationTimeout": { + "SCHEMA": -1, + "SYNC": -1, + "VALIDATE": -1, + "SEARCH": -1, + "AUTHENTICATE": -1, + "CREATE": -1, + "UPDATE": -1, + "DELETE": -1, + "TEST": -1, + "SCRIPT_ON_CONNECTOR": -1, + "SCRIPT_ON_RESOURCE": -1, + "GET": -1, + "RESOLVEUSERNAME": -1 + }, + "resultsHandlerConfig": { + "enableAttributesToGetSearchResultsHandler": true, + "enableFilteredResultsHandler": true, + "enableNormalizingResultsHandler": true + }, + "poolConfigOption": { + "minIdle": 1, + "minEvictableIdleTimeMillis": 120000, + "maxWait": 150000, + "maxIdle": 10, + "maxObjects": 10 + }, + "connectorRef": { + "bundleVersion": "1.1.0.3", + "bundleName": "org.forgerock.openicf.connectors.xml-connector", + "displayName": "XML Connector", + "connectorName": "org.forgerock.openicf.connectors.xml.XMLConnector" + } + }' \ +"http://localhost:8080/openidm/system?_action=createFullConfig" +---- + +[NOTE] +==== +Notice the single quotes around the argument to the `--data` option in the preceding command. For most UNIX shells, single quotes around a string prevent the shell from executing the command when encountering a new line in the content. You can therefore pass the `--data '...'` option on a single line, or including line feeds. +==== +OpenIDM attempts to read the schema, if available, from the external resource in order to generate output. OpenIDM then iterates through schema objects and attributes, creating JSON representations for `objectTypes` and `operationOptions` for supported objects and operations. + +The output includes the basic `--data` input, along with `operationOptions` and `objectTypes`. + +Because OpenIDM produces a full property set for all attributes and all object types in the schema from the external resource, the resulting configuration can be large. For an LDAP server, OpenIDM can generate a configuration containing several tens of thousands of lines, for example. You might therefore want to reduce the schema to a minimum on the external resource before you run the `createFullConfig` command. + +When you have the complete connector configuration, save that configuration in a file named `provisioner.openicf-name.json` (where name corresponds to the name of the connector) and place it in the `conf` directory of your project. For more information, see xref:#openicf-provisioner-conf["Configuring Connectors"]. + + + +[#systems-over-rest] +=== Checking the Status of External Systems Over REST + +After a connection has been configured, external systems are accessible over the REST interface at the URL `\http://localhost:8080/openidm/system/connector-name`. Aside from accessing the data objects within the external systems, you can test the availability of the systems themselves. + +To list the external systems that are connected to an OpenIDM instance, use the `test` action on the URL `\http://localhost:8080/openidm/system/`. The following example shows the connector configuration for an external LDAP system: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=test" +[ + { + "ok": true, + "displayName": "LDAP Connector", + "connectorRef": { + "bundleVersion": "[1.4.0.0,2.0.0.0)", + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "connectorName": "org.identityconnectors.ldap.LdapConnector" + }, + "objectTypes": [ + "__ALL__", + "group", + "account" + ], + "config": "config/provisioner.openicf/ldap", + "enabled": true, + "name": "ldap" + } +] +---- +The status of the system is provided by the `ok` parameter. If the connection is available, the value of this parameter is `true`. + +To obtain the status for a single system, include the name of the connector in the URL, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ldap?_action=test" +{ + "ok": true, + "displayName": "LDAP Connector", + "connectorRef": { + "bundleVersion": "[1.4.0.0,2.0.0.0)", + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "connectorName": "org.identityconnectors.ldap.LdapConnector" + }, + "objectTypes": [ + "__ALL__", + "group", + "account" + ], + "config": "config/provisioner.openicf/ldap", + "enabled": true, + "name": "ldap" +} +---- +If there is a problem with the connection, the `ok` parameter returns `false`, with an indication of the error. In the following example, the LDAP server named `ldap`, running on `localhost:1389`, is down: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/ldap?_action=test" +{ + "ok": false, + "error": "localhost:1389", + "displayName": "LDAP Connector", + "connectorRef": { + "bundleVersion": "[1.4.0.0,2.0.0.0)", + "bundleName": "org.forgerock.openicf.connectors.ldap-connector", + "connectorName": "org.identityconnectors.ldap.LdapConnector" + }, + "objectTypes": [ + "__ALL__", + "group", + "account" + ], + "config": "config/provisioner.openicf/ldap", + "enabled": true, + "name": "ldap" +} +---- +To test the validity of a connector configuration, use the `testConfig` action and include the configuration in the command. For example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --data '{ + "name" : "xmlfile", + "connectorRef" : { + "bundleName" : "org.forgerock.openicf.connectors.xml-connector", + "bundleVersion" : "1.1.0.3", + "connectorName" : "org.forgerock.openicf.connectors.xml.XMLConnector" + }, + "producerBufferSize" : 100, + "connectorPoolingSupported" : true, + "poolConfigOption" : { + "maxObjects" : 10, + "maxIdle" : 10, + "maxWait" : 150000, + "minEvictableIdleTimeMillis" : 120000, + "minIdle" : 1 + }, + "operationTimeout" : { + "CREATE" : -1, + "TEST" : -1, + "AUTHENTICATE" : -1, + "SEARCH" : -1, + "VALIDATE" : -1, + "GET" : -1, + "UPDATE" : -1, + "DELETE" : -1, + "SCRIPT_ON_CONNECTOR" : -1, + "SCRIPT_ON_RESOURCE" : -1, + "SYNC" : -1, + "SCHEMA" : -1 + }, + "configurationProperties" : { + "xsdIcfFilePath" : "samples/sample1/data/resource-schema-1.xsd", + "xsdFilePath" : "samples/sample1/data/resource-schema-extension.xsd", + "xmlFilePath" : "samples/sample1/data/xmlConnectorData.xml" + }, + "syncFailureHandler" : { + "maxRetries" : 5, + "postRetryAction" : "logged-ignore" + }, + "objectTypes" : { + "account" : { + "$schema" : "http://json-schema.org/draft-03/schema", + "id" : "__ACCOUNT__", + "type" : "object", + "nativeType" : "__ACCOUNT__", + "properties" : { + "description" : { + "type" : "string", + "nativeName" : "__DESCRIPTION__", + "nativeType" : "string" + }, + "firstname" : { + "type" : "string", + "nativeName" : "firstname", + "nativeType" : "string" + }, + "email" : { + "type" : "string", + "nativeName" : "email", + "nativeType" : "string" + }, + "_id" : { + "type" : "string", + "nativeName" : "__UID__" + }, + "password" : { + "type" : "string", + "nativeName" : "password", + "nativeType" : "string" + }, + "name" : { + "type" : "string", + "required" : true, + "nativeName" : "__NAME__", + "nativeType" : "string" + }, + "lastname" : { + "type" : "string", + "required" : true, + "nativeName" : "lastname", + "nativeType" : "string" + }, + "mobileTelephoneNumber" : { + "type" : "string", + "required" : true, + "nativeName" : "mobileTelephoneNumber", + "nativeType" : "string" + }, + "securityQuestion" : { + "type" : "string", + "required" : true, + "nativeName" : "securityQuestion", + "nativeType" : "string" + }, + "securityAnswer" : { + "type" : "string", + "required" : true, + "nativeName" : "securityAnswer", + "nativeType" : "string" + }, + "roles" : { + "type" : "string", + "required" : false, + "nativeName" : "roles", + "nativeType" : "string" + } + } + } + }, + "operationOptions" : { } +}' \ + --request POST \ + "http://localhost:8080/openidm/system?_action=testConfig" +---- +If the configuration is valid, the command returns `"ok": true`, for example: + +[source, console] +---- +{ + "ok": true, + "name": "xmlfile" +} +---- +If the configuration is not valid, the command returns an error, indicating the problem with the configuration. For example, the following result is returned when the LDAP connector configuration is missing a required property (in this case, the `baseContexts` to synchronize): + +[source, console] +---- +{ + "error": "org.identityconnectors.framework.common.exceptions.ConfigurationException: + The list of base contexts cannot be empty", + "name": "OpenDJ", + "ok": false +} +---- +The `testConfig` action requires a running OpenIDM instance, as it uses the REST API, but does not require an active connector instance for the connector whose configuration you want to test. + + +[#adding-to-connector-config] +=== Adding Attributes to Connector Configurations + +You can add the attributes of your choice to a connector configuration file. Specifically, if you want to set up xref:#property-level-extensions["Extending the Property Type Configuration"] to one of the `objectTypes` such as `account`, use the format shown under xref:#object-types["Specifying the Supported Object Types"]. + +You can configure connectors to enable provisioning of arbitrary property level extensions (such as image files) to system resources. For example, if you want to set up image files such as account avatars, open the appropriate provisioner file. Look for an `account` section similar to: + +[source, javascript] +---- +"account" : { + "$schema" : "http://json-schema.org/draft-03/schema", + "id" : "__ACCOUNT__", + "type" : "object", + "nativeType" : "__ACCOUNT__", + "properties" : {... +---- +Under `properties`, add one of the following code blocks. The first block works for a single photo encoded as a base64 string. The second block would address multiple photos encoded in the same way: + +[source, javascript] +---- +"attributeByteArray" : { + "type" : "string", + "nativeName" : "attributeByteArray", + "nativeType" : "JAVA_TYPE_BYTE_ARRAY" +}, +---- + +[source, javascript] +---- +"attributeByteArrayMultivalue": { + "type": "array", + "items": { + "type": "string", + "nativeType": "JAVA_TYPE_BYTE_ARRAY" + }, + "nativeName": "attributeByteArrayMultivalue" +}, +---- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-scheduler-conf.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-scheduler-conf.adoc new file mode 100644 index 000000000..b3cd3dc44 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-scheduler-conf.adoc @@ -0,0 +1,900 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-scheduler-conf] +== Scheduling Tasks and Events + +The OpenIDM scheduler enables you to schedule reconciliation and synchronization tasks, trigger scripts, collect and run reports, trigger workflows, and perform custom logging. + +OpenIDM supports `cron`-like syntax to schedule events and tasks, based on expressions supported by the Quartz Scheduler (bundled with OpenIDM). + +If you use configuration files to schedule tasks and events, you must place the schedule files in your project's `conf/` directory. By convention, OpenIDM uses file names of the form `schedule-schedule-name.json`, where __schedule-name__ is a logical name for the scheduled operation, for example, `schedule-reconcile_systemXmlAccounts_managedUser.json`. There are several example schedule configuration files in the `openidm/samples/schedules` directory. + +You can configure OpenIDM to pick up changes to scheduled tasks and events dynamically, during initialization and also at runtime. For more information, see xref:chap-configuration.adoc#changing-configuration["Changing the Default Configuration"]. + +In addition to the fine-grained scheduling facility, you can perform a scheduled batch scan for a specified date in OpenIDM data, and then automatically run a task when this date is reached. For more information, see xref:#task-scanner["Scanning Data to Trigger Tasks"]. + +[#scheduler-configuration-file] +=== Scheduler Configuration + +Schedules are configured through JSON objects. The schedule configuration involves three files: + +* The `boot.properties` file, where you can enable persistent schedules. + +* The `scheduler.json` file, that configures the overall scheduler service. + +* One `schedule-schedule-name.json` file for each configured schedule. + +In the boot properties configuration file (`project-dir/conf/boot/boot.properties`), the instance type is standalone and persistent schedules are enabled by default: + +[source] +---- +# valid instance types for node include standalone, clustered-first, and clustered-additional +openidm.instance.type=standalone + +# enables the execution of persistent schedulers +openidm.scheduler.execute.persistent.schedules=true +---- +The scheduler service configuration file (`project-dir/conf/scheduler.json`) governs the configuration for a specific scheduler instance, and has the following format: + +[source, javascript] +---- +{ + "threadPool" : { + "threadCount" : "10" + }, + "scheduler" : { + "executePersistentSchedules" : "&{openidm.scheduler.execute.persistent.schedules}" + } +} +---- +The properties in the `scheduler.json` file relate to the configuration of the Quartz Scheduler: + +* `threadCount` specifies the maximum number of threads that are available for running scheduled tasks concurrently. + +* `executePersistentSchedules` allows you to disable persistent schedules for a specific node. If this parameter is set to `false`, the Scheduler Service will support the management of persistent schedules (CRUD operations) but it will not run any persistent schedules. The value of this property can be a string or boolean and is `true` by default. ++ +Note that changing the value of the `openidm.scheduler.execute.persistent.schedules` property in the `boot.properties` file changes the scheduler that manages scheduled tasks on that node. Because the persistent and in-memory schedulers are managed separately, a situation can arise where two separate schedules have the same schedule name. + +* `advancedProperties` (optional) enables you to configure additional properties for the Quartz Scheduler. + + +[NOTE] +==== +In clustered environments, the scheduler service obtains an `instanceID`, and checkin and timeout settings from the cluster management service (defined in the `project-dir/conf/cluster.json` file). +==== +For details of all the configurable properties for the Quartz Scheduler, see the link:http://www.quartz-scheduler.org/documentation/quartz-2.1.x/configuration/ConfigMain[Quartz Scheduler Configuration Reference, window=\_blank]. + +Each schedule configuration file (`project-dir/conf/schedule-schedule-name.json`) has the following format: + +[source, javascript] +---- +{ + "enabled" : true, + "persisted" : false, + "concurrentExecution" : false, + "type" : "cron", + "startTime" : "(optional) time", + "endTime" : "(optional) time", + "schedule" : "cron expression", + "misfirePolicy" : "optional, string", + "timeZone" : "(optional) time zone", + "invokeService" : "service identifier", + "invokeContext" : "service specific context info", + "invokeLogLevel" : "(optional) level" +} +---- +-- +The schedule configuration properties are defined as follows: + +`enabled`:: +Set to `true` to enable the schedule. When this property is `false`, OpenIDM considers the schedule configuration dormant, and does not allow it to be triggered or launched. + ++ +If you want to retain a schedule configuration, but do not want it used, set `enabled` to `false` for task and event schedulers, instead of changing the configuration or `cron` expressions. + +`persisted` (optional):: +Specifies whether the schedule state should be persisted or stored in RAM. Boolean (`true` or `false`), `false` by default. + ++ +In a clustered environment, this property must be set to `true` to have the schedule fire only once across the cluster. For more information, see xref:#persistent-schedules["Configuring Persistent Schedules"]. + +`concurrentExecution`:: +Specifies whether multiple instances of the same schedule can run concurrently. Boolean (`true` or `false`), `false` by default. Multiple instances of the same schedule cannot run concurrently by default. This setting prevents a new scheduled task from being launched before the same previously launched task has completed. For example, under normal circumstances you would want a LiveSync operation to complete before the same operation was launched again. To enable multiple schedules to run concurrently, set this parameter to `true`. The behavior of missed scheduled tasks is governed by the `misfirePolicy`. + +`type`:: +Currently OpenIDM supports only `cron`. + +`startTime` (optional):: +Used to start the schedule at some time in the future. If this parameter is omitted, empty, or set to a time in the past, the task or event is scheduled to start immediately. + ++ +Use ISO 8601 format to specify times and dates (`YYYY-MM-DD Thh:mm :ss`). + +`endTime` (optional):: +Used to plan the end of scheduling. + +`schedule`:: +Takes `cron` expression syntax. For more information, see the link:http://www.quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/crontrigger.html[CronTrigger Tutorial, window=\_blank] and link:http://www.quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/tutorial-lesson-06.html[Lesson 6: CronTrigger, window=\_blank]. + +`misfirePolicy`:: +For persistent schedules, this optional parameter specifies the behavior if the scheduled task is missed, for some reason. Possible values are as follows: ++ + +* `fireAndProceed`. The first run of a missed schedule is immediately launched when the server is back online. Subsequent runs are discarded. After this, the normal schedule is resumed. + +* `doNothing`. All missed schedules are discarded and the normal schedule is resumed when the server is back online. + + +`timeZone` (optional):: +If not set, OpenIDM uses the system time zone. + +`invokeService`:: +Defines the type of scheduled event or action. The value of this parameter can be one of the following: ++ + +* `sync` for reconciliation + +* `provisioner` for LiveSync + +* `script` to call some other scheduled operation defined in a script + +* `taskScanner` to define a scheduled task that queries a set of objects. For more information, see xref:#task-scanner["Scanning Data to Trigger Tasks"]. + + +`invokeContext`:: +Specifies contextual information, depending on the type of scheduled event (the value of the `invokeService` parameter). + ++ +The following example invokes reconciliation: ++ + +[source, javascript] +---- +{ + "invokeService": "sync", + "invokeContext": { + "action": "reconcile", + "mapping": "systemLdapAccount_managedUser" + } +} +---- ++ +For a scheduled reconciliation task, you can define the mapping in one of two ways: + +* Reference a mapping by its name in `sync.json`, as shown in the previous example. The mapping must exist in your project's `conf/sync.json` file. + +* Add the mapping definition inline by using the `mapping` property, as shown in xref:chap-synchronization.adoc#alternative-mapping["Specifying the Mapping as Part of the Schedule"]. + ++ +The following example invokes a LiveSync operation: ++ + +[source, javascript] +---- +{ + "invokeService": "provisioner", + "invokeContext": { + "action": "liveSync", + "source": "system/OpenDJ/__ACCOUNT__" + } +} +---- ++ +For scheduled LiveSync tasks, the `source` property follows OpenIDM's convention for a pointer to an external resource object and takes the form `system/resource-name/object-type`. + ++ +The following example invokes a script, which prints the string `Hello World` to the OpenIDM log (`/openidm/logs/openidm0.log.X`). ++ + +[source, javascript] +---- +{ + "invokeService": "script", + "invokeContext": { + "script": { + "type": "text/javascript", + "source": "console.log('Hello World');" + } + } +} +---- ++ +Note that these are sample configurations only. Your own schedule configuration will differ according to your specific requirements. + +`invokeLogLevel` (optional):: +Specifies the level at which the invocation will be logged. Particularly for schedules that run very frequently, such as LiveSync, the scheduled task can generate significant output to the log file, and you should adjust the log level accordingly. The default schedule log level is `info`. The value can be set to any one of the link:http://www.slf4j.org/apidocs/org/apache/commons/logging/Log.html[SLF4J, window=\_top] log levels: ++ + +* `trace` + +* `debug` + +* `info` + +* `warn` + +* `error` + +* `fatal` + + +-- + + +[#schedules-dst] +=== Schedules and Daylight Savings Time + +The schedule service uses Quartz `cronTrigger` syntax. CronTrigger schedules jobs to fire at specific times with respect to a calendar (rather than every __N__ milliseconds). This scheduling can cause issues when clocks change for daylight savings time (DST) if the trigger time falls around the clock change time in your specific time zone. + +Depending on the trigger schedule, and on the daylight event, the trigger might be skipped or might appear not to fire for a short period. This interruption can be particularly problematic for liveSync where schedules execute continuously. In this case, the time change (for example, from 02:00 back to 01:00) causes an hour break between each liveSync execution. + +To prevent DST from having an impact on your schedules, set the time zone of the schedule to Coordinated Universal Time (UTC). UTC is never subject to DST, so schedules will continue to fire as normal. + + +[#persistent-schedules] +=== Configuring Persistent Schedules + +By default, scheduling information, such as schedule state and details of the schedule run, is stored in RAM. This means that such information is lost when OpenIDM is rebooted. The schedule configuration itself (defined in your project's `conf/schedule-schedule-name.json` file) is not lost when OpenIDM is shut down, and normal scheduling continues when the server is restarted. However, there are no details of missed schedule runs that should have occurred during the period the server was unavailable. + +You can configure schedules to be persistent, which means that the scheduling information is stored in the internal repository rather than in RAM. With persistent schedules, scheduling information is retained when OpenIDM is shut down. Any previously scheduled jobs can be rescheduled automatically when OpenIDM is restarted. + +Persistent schedules also enable you to manage scheduling across a cluster (multiple OpenIDM instances). When scheduling is persistent, a particular schedule will be launched only once across the cluster, rather than once on every OpenIDM instance. For example, if your deployment includes a cluster of OpenIDM nodes for high availability, you can use persistent scheduling to start a reconciliation operation on only one node in the cluster, instead of starting several competing reconciliation operations on each node. + +[IMPORTANT] +==== +Persistent schedules rely on timestamps. In a deployment where OpenIDM instances run on separate machines, you __must__ synchronize the system clocks of these machines using a time synchronization service that runs regularly. The clocks of all machines involved in persistent scheduling must be within one second of each other. For information on how you can achieve this using the Network Time Protocol (NTP) daemon, see the link:https://tools.ietf.org/html/rfc7822[NTP RFC, window=\_blank]. +==== +To configure persistent schedules, set `persisted` to `true` in the schedule configuration file (`schedule-schedule-name.json)`. + +If OpenIDM is down when a scheduled task was set to occur, one or more runs of that schedule might be missed. To specify what action should be taken if schedules are missed, set the `misfirePolicy` in the schedule configuration file. The `misfirePolicy` determines what OpenIDM should do if scheduled tasks are missed. Possible values are as follows: + +* `fireAndProceed`. The first run of a missed schedule is immediately implemented when the server is back online. Subsequent runs are discarded. After this, the normal schedule is resumed. + +* `doNothing`. All missed schedules are discarded and the normal schedule is resumed when the server is back online. + + + +[#scheduler-examples] +=== Schedule Examples + +The following example shows a schedule for reconciliation that is not enabled. When the schedule is enabled (`"enabled" : true,`), reconciliation runs every 30 minutes, starting on the hour: + +[source, javascript] +---- +{ + "enabled": false, + "persisted": false, + "type": "cron", + "schedule": "0 0/30 * * * ?", + "invokeService": "sync", + "invokeContext": { + "action": "reconcile", + "mapping": "systemLdapAccounts_managedUser" + } +} +---- +The following example shows a schedule for LiveSync enabled to run every 15 seconds, starting at the beginning of the minute. The schedule is persisted, that is, stored in the internal repository rather than in memory. If one or more LiveSync runs are missed, as a result of OpenIDM being unavailable, the first run of the LiveSync operation is implemented when the server is back online. Subsequent runs are discarded. After this, the normal schedule is resumed: + +[source, javascript] +---- +{ + "enabled": true, + "persisted": true, + "misfirePolicy" : "fireAndProceed", + "type": "cron", + "schedule": "0/15 * * * * ?", + "invokeService": "provisioner", + "invokeContext": { + "action": "liveSync", + "source": "system/ldap/account" + } +} +---- + + +[#schedules-over-rest] +=== Managing Schedules Over REST + +OpenIDM exposes the scheduler service under the `/openidm/scheduler` context path. The following examples show how schedules can be created, read, updated, and deleted, over REST, by using the scheduler service. The examples also show how to pause and resume scheduled tasks, when an OpenIDM instance is placed in maintenance mode. For information about placing OpenIDM in maintenance mode, see xref:../install-guide/chap-update.adoc#maintenance-mode["Placing an OpenIDM Instance in Maintenance Mode"] in the __Installation Guide__. + +[NOTE] +==== +When you configure schedules in this way, changes made to the schedules are not pushed back into the configuration service. Managing schedules by using the `/openidm/scheduler` context path essentially bypasses the configuration service and sends the request directly to the scheduler. + +If you need to perform an operation on a schedule that was created by using the configuration service (by placing a schedule file in the `conf/` directory), you must direct your request to the `/openidm/config` endpoint, and not to the `/openidm/scheduler` endpoint. +==== + +[#creating-schedules] +==== Creating a Schedule + +You can create a schedule with a PUT request, which allows you to specify the ID of the schedule, or with a POST request, in which case the server assigns an ID automatically. + +The following example uses a PUT request to create a schedule that fires a script (`script/testlog.js`) every second. The schedule configuration is as described in xref:#scheduler-configuration-file["Scheduler Configuration"]: + +[source, console] +---- +$ 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 '{ + "enabled":true, + "type":"cron", + "schedule":"0/1 * * * * ?", + "persisted":true, + "misfirePolicy":"fireAndProceed", + "invokeService":"script", + "invokeContext": { + "script": { + "type":"text/javascript", + "file":"script/testlog.js" + } + } + }' \ + "https://localhost:8443/openidm/scheduler/testlog-schedule" +{ + "type": "cron", + "invokeService": "script", + "persisted": true, + "_id": "testlog-schedule", + "schedule": "0/1 * * * * ?", + "misfirePolicy": "fireAndProceed", + "enabled": true, + "invokeContext": { + "script": { + "file": "script/testlog.js", + "type": "text/javascript" + } + } +} +---- +The following example uses a POST request to create an identical schedule to the one created in the previous example, but with a server-assigned ID: + +[source, console] +---- +$ 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 '{ + "enabled":true, + "type":"cron", + "schedule":"0/1 * * * * ?", + "persisted":true, + "misfirePolicy":"fireAndProceed", + "invokeService":"script", + "invokeContext": { + "script": { + "type":"text/javascript", + "file":"script/testlog.js" + } + } + }' \ + "https://localhost:8443/openidm/scheduler?_action=create" +{ + "type": "cron", + "invokeService": "script", + "persisted": true, + "_id": "d6d1b256-7e46-486e-af88-169b4b1ad57a", + "schedule": "0/1 * * * * ?", + "misfirePolicy": "fireAndProceed", + "enabled": true, + "invokeContext": { + "script": { + "file": "script/testlog.js", + "type": "text/javascript" + } + } +} +---- +The output includes the `_id` of the schedule, in this case `"_id": "d6d1b256-7e46-486e-af88-169b4b1ad57a"`. + + +[#schedule-details] +==== Obtaining the Details of a Schedule + +The following example displays the details of the schedule created in the previous section. Specify the schedule ID in the URL: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/scheduler/d6d1b256-7e46-486e-af88-169b4b1ad57a" +{ + "_id": "d6d1b256-7e46-486e-af88-169b4b1ad57a", + "schedule": "0/1 * * * * ?", + "misfirePolicy": "fireAndProceed", + "startTime": null, + "invokeContext": { + "script": { + "file": "script/testlog.js", + "type": "text/javascript" + } + }, + "enabled": true, + "concurrentExecution": false, + "persisted": true, + "timeZone": null, + "type": "cron", + "invokeService": "org.forgerock.openidm.script", + "endTime": null, + "invokeLogLevel": "info" +} +---- + + +[#updating-schedules] +==== Updating a Schedule + +To update a schedule definition, use a PUT request and update all properties of the object. Note that PATCH requests are currently supported only for managed and system objects. + +The following example disables the schedule created in the previous section: + +[source, console] +---- +$ 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 '{ + "enabled":false, + "type":"cron", + "schedule":"0/1 * * * * ?", + "persisted":true, + "misfirePolicy":"fireAndProceed", + "invokeService":"script", + "invokeContext": { + "script": { + "type":"text/javascript", + "file":"script/testlog.js" + } + } + }' \ + "https://localhost:8443/openidm/scheduler/d6d1b256-7e46-486e-af88-169b4b1ad57a" + null +---- + + +[#listing-schedules] +==== Listing Configured Schedules + +To display a list of all configured schedules, query the `openidm/scheduler` context path as shown in the following example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/scheduler?_queryId=query-all-ids" +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "resultCount": 2, + "result": [ + { + "_id": "d6d1b256-7e46-486e-af88-169b4b1ad57a" + }, + { + "_id": "recon" + } + ] +} +---- + + +[#deleting-schedules] +==== Deleting a Schedule + +To deleted a configured schedule, call a DELETE request on the schedule ID. For example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "https://localhost:8443/openidm/scheduler/d6d1b256-7e46-486e-af88-169b4b1ad57a" +null +---- + + +[#schedules-listing-current-tasks] +==== Obtaining a List of Running Scheduled Tasks + +The following command returns a list of tasks that are currently executing. This list enables you to decide whether to wait for specific tasks to complete before you place an OpenIDM instance in maintenance mode. + +Note that this list is accurate only at the moment the request was issued. The list can change at any time after the response is received. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/scheduler?_action=listCurrentlyExecutingJobs" +[ + { + "concurrentExecution": false, + "enabled": true, + "endTime": null, + "invokeContext": { + "script": { + "file": "script/testlog.js", + "type": "text/javascript" + } + }, + "invokeLogLevel": "info", + "invokeService": "org.forgerock.openidm.script", + "misfirePolicy": "doNothing", + "persisted": false, + "schedule": "0/10 * * * * ?", + "startTime": null, + "timeZone": null, + "type": "cron" + } +] +---- + + +[#schedules-pausing-current-tasks] +==== Pausing Scheduled Tasks + +In preparation for placing an OpenIDM instance into maintenance mode, you can temporarily suspend all scheduled tasks. This action does not cancel or interrupt tasks that are already in progress - it simply prevents any scheduled tasks from being invoked during the suspension period. + +The following command suspends all scheduled tasks and returns `true` if the tasks could be suspended successfully. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/scheduler?_action=pauseJobs" +{ + "success": true +} +---- + + +[#schedules-resuming-current-tasks] +==== Resuming All Running Scheduled Tasks + +When an update has been completed, and your instance is no longer in maintenance mode, you can resume scheduled tasks to start them up again. Any tasks that were missed during the downtime will follow their configured misfire policy to determine whether they should be reinvoked. + +The following command resumes all scheduled tasks and returns `true` if the tasks could be resumed successfully. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/scheduler?_action=resumeJobs" +{ + "success": true +} +---- + + + +[#task-scanner] +=== Scanning Data to Trigger Tasks + +In addition to the fine-grained scheduling facility described previously, OpenIDM provides a task scanning mechanism. The task scanner enables you to perform a batch scan on a specified property in OpenIDM, at a scheduled interval, and then to launch a task when the value of that property matches a specified value. + +When the task scanner identifies a condition that should trigger the task, it can invoke a script created specifically to handle the task. + +For example, the task scanner can scan all `managed/user` objects for a "sunset date" and can invoke a script that launches a "sunset task" on the user object when this date is reached. + +[#task-scanner-config] +==== Configuring the Task Scanner + +The task scanner is essentially a scheduled task that queries a set of managed users. The task scanner is configured in the same way as a regular scheduled task in a schedule configuration file named (`schedule-task-name.json)`, with the `invokeService` parameter set to `taskscanner`. The `invokeContext` parameter defines the details of the scan, and the task that should be launched when the specified condition is triggered. + +The following example defines a scheduled scanning task that triggers a sunset script. The schedule configuration file is provided in `openidm/samples/taskscanner/conf/schedule-taskscan_sunset.json`. To use this sample file, copy it to the `openidm/conf` directory. + +[source, javascript] +---- +{ + "enabled" : true, + "type" : "cron", + "schedule" : "0 0 * * * ?", + "concurrentExecution" : false, + "invokeService" : "taskscanner", + "invokeContext" : { + "waitForCompletion" : false, + "maxRecords" : 2000, + "numberOfThreads" : 5, + "scan" : { + "_queryId" : "scan-tasks", + "object" : "managed/user", + "property" : "sunset/date", + "condition" : { + "before" : "${Time.now}" + }, + "taskState" : { + "started" : "sunset/task-started", + "completed" : "sunset/task-completed" + }, + "recovery" : { + "timeout" : "10m" + } + }, + "task" : { + "script" : { + "type" : "text/javascript", + "file" : "script/sunset.js" + } + } + } +} +---- +The schedule configuration calls a script (`script/sunset.js`). To test the sample, copy this script file from `openidm/samples/taskscanner/script/sunset.js` to the `openidm/script` directory. The remaining properties in the schedule configuration are as follows: +-- +The `invokeContext` parameter takes the following properties: + +`waitForCompletion` (optional):: +This property specifies whether the task should be performed synchronously. Tasks are performed asynchronously by default (with `waitForCompletion` set to false). A task ID (such as `{"_id":"354ec41f-c781-4b61-85ac-93c28c180e46"}`) is returned immediately. If this property is set to true, tasks are performed synchronously and the ID is not returned until all tasks have completed. + +`maxRecords` (optional):: +The maximum number of records that can be processed. This property is not set by default so the number of records is unlimited. If a maximum number of records is specified, that number will be spread evenly over the number of threads. + +`numberOfThreads` (optional):: +By default, the task scanner runs in a multi-threaded manner, that is, numerous threads are dedicated to the same scanning task run. Multi-threading generally improves the performance of the task scanner. The default number of threads for a single scanning task is ten. To change this default, set the `numberOfThreads` property. + +`scan`:: +Defines the details of the scan. The following properties are defined: ++ +[open] +==== + +`_queryId`:: +Specifies the predefined query that is performed to identify the entries for which this task should be run. + ++ +The query that is referenced here must be defined in the database table configuration file (`conf/repo.orientdb.json` or `conf/repo.jdbc.json`). A sample query for a scanned task (`scan-tasks`) is defined in the JDBC repository configuration file as follows: ++ + +[source, console] +---- +"scan-tasks" : "SELECT fullobject FROM ${_dbSchema}.${_mainTable} + obj INNER JOIN ${_dbSchema}.${_propTable} + prop ON obj.id = prop.${_mainTable}_id + LEFT OUTER JOIN ${_dbSchema}.${_propTable} + complete ON obj.id = complete.${_mainTable}_id + AND complete.propkey=${taskState.completed} + INNER JOIN ${_dbSchema}.objecttypes objtype + ON objtype.id = obj.objecttypes_id + WHERE ( prop.propkey=${property} AND prop.propvalue < ${condition.before} + AND objtype.objecttype = ${_resource} ) + AND ( complete.propvalue is NULL )", +---- ++ +Note that this query identifies records for which the value of the specified `property` is smaller than the condition. The sample query supports only time-based conditions, with the time specified in ISO 8601 format (Zulu time). You can write any query to target the records that you require. + +`object`:: +Defines the managed object type against which the query should be performed, as defined in the `managed.json` file. + +`property`:: +Defines the property of the managed object, against which the query is performed. In the previous example, the `"property" : "sunset/date"` indicates a JSON pointer that maps to the object attribute, and can be understood as `sunset: {"date" : "date"}`. + ++ +If you are using a JDBC repository, with a generic mapping, you must explicitly set this property as searchable so that it can be queried by the task scanner. For more information, see xref:chap-repo.adoc#generic-mappings["Using Generic Mappings"]. + +`condition` (optional):: +Indicates the conditions that must be matched for the defined property. + ++ +In the previous example, the scanner scans for users whose `sunset/date` is prior to the current timestamp (at the time the script is run). + ++ +You can use these fields to define any condition. For example, if you wanted to limit the scanned objects to a specified location, say, London, you could formulate a query to compare against object locations and then set the condition to be: ++ + +[source, javascript] +---- +"condition" : { + "location" : "London" +}, +---- ++ +For time-based conditions, the `condition` property supports macro syntax, based on the `Time.now` object (which fetches the current time). You can specify any date/time in relation to the current time, using the `+` or `-` operator, and a duration modifier. For example: `${Time.now + 1d}` would return all user objects whose `sunset/date` is the following day (current time plus one day). You must include space characters around the operator (`+` or `-`). The duration modifier supports the following unit specifiers: ++ +[none] +* `s` - second +* `m` - minute +* `h` - hour +* `d` - day +* `M` - month +* `y` - year + +`taskState`:: +Indicates the names of the fields in which the start message and the completed message are stored. These fields are used to track the status of the task. ++ +[none] +* `started` specifies the field that stores the timestamp for when the task begins. +* `completed` specifies the field that stores the timestamp for when the task completes its operation. The `completed` field is present as soon as the task has started, but its value is `null` until the task has completed. + +`recovery` (optional):: +Specifies a configurable timeout, after which the task scanner process ends. For clustered OpenIDM instances, there might be more than one task scanner running at a time. A task cannot be launched by two task scanners at the same time. When one task scanner "claims" a task, it indicates that the task has been started. That task is then unavailable to be claimed by another task scanner and remains unavailable until the end of the task is indicated. In the event that the first task scanner does not complete the task by the specified timeout, for whatever reason, a second task scanner can pick up the task. + +==== + +`task`:: +Provides details of the task that is performed. Usually, the task is invoked by a script, whose details are defined in the `script` property: ++ + +* `type` ‒ the type of script, either JavaScript or Groovy. + +* `file` ‒ the path to the script file. The script file takes at least two objects (in addition to the default objects that are provided to all OpenIDM scripts): ++ + +** `input` ‒ the individual object that is retrieved from the query (in the example, this is the individual user object). + +** `objectID` ‒ a string that contains the full identifier of the object. The `objectID` is useful for performing updates with the script as it allows you to target the object directly. For example: `openidm.update(objectID, input['_rev'], input);`. + ++ +A sample script file is provided in `openidm/samples/taskscanner/script/sunset.js`. To use this sample file, copy it to your project's `script/` directory. The sample script marks all user objects that match the specified conditions as inactive. You can use this sample script to trigger a specific workflow, or any other task associated with the sunset process. + ++ +For more information about using scripts in OpenIDM, see xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"]. + +-- + + +[#task-scanner-rest] +==== Managing Scanning Tasks Over REST + +You can trigger, cancel, and monitor scanning tasks over the REST interface, using the REST endpoint `\https://localhost:8443/openidm/taskscanner`. + +[#triggering-task-scanner] +===== Triggering a Scanning Task + +The following REST command runs a task named "taskscan_sunset". The task itself is defined in a file named `conf/schedule-taskscan_sunset.json`: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/taskscanner?_action=execute&name=schedule/taskscan_sunset" +---- +By default, a scanning task ID is returned immediately when the task is initiated. Clients can make subsequent calls to the task scanner service, using this task ID to query its state and to call operations on it. + +For example, the scanning task initiated previously would return something similar to the following, as soon as it was initiated: + +[source, console] +---- +{"_id":"edfaf59c-aad1-442a-adf6-3620b24f8385"} +---- +To have the scanning task complete before the ID is returned, set the `waitForCompletion` property to `true` in the task definition file (`schedule-taskscan_sunset.json`). You can also set the property directly over the REST interface when the task is initiated. For example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/taskscanner?_action=execute&name=schedule/taskscan_sunset&waitForCompletion=true" +---- + + +[#canceling-task-scanner] +===== Canceling a Scanning Task + +You can cancel a scanning task by sending a REST call with the `cancel` action, specifying the task ID. For example, the following call cancels the scanning task initiated in the previous section: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/taskscanner/edfaf59c-aad1-442a-adf6-3620b24f8385?_action=cancel" +{ + "_id":"edfaf59c-aad1-442a-adf6-3620b24f8385", + "action":"cancel", + "status":"SUCCESS" +} +---- + + +[#listing-task-scanner] +===== Listing Scanning Tasks + +You can display a list of scanning tasks that have completed, and those that are in progress, by running a RESTful GET on the `openidm/taskscanner` context path. The following example displays all scanning tasks: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/taskscanner" +{ + "tasks": [ + { + "ended": 1352455546182 + "started": 1352455546149, + "progress": { + "failures": 0 + "successes": 2400, + "total": 2400, + "processed": 2400, + "state": "COMPLETED", + }, + "_id": "edfaf59c-aad1-442a-adf6-3620b24f8385", + } + ] +} +---- +-- +Each scanning task has the following properties: + +`ended`:: +The time at which the scanning task ended. + +`started`:: +The time at which the scanning task started. + +`progress`:: +The progress of the scanning task, summarised in the following fields: ++ +[none] +* `failures` - the number of records not able to be processed +* `successes` - the number of records processed successfully +* `total` - the total number of records +* `processed` - the number of processed records +* `state` - the overall state of the task, `INITIALIZED`, `ACTIVE`, `COMPLETED`, `CANCELLED`, or `ERROR` + +`_id`:: +The ID of the scanning task. + +-- +The number of processed tasks whose details are retained is governed by the `openidm.taskscanner.maxcompletedruns` property in the `conf/system.properties` file. By default, the last one hundred completed tasks are retained. + + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-scripting.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-scripting.adoc new file mode 100644 index 000000000..e0ae9be7a --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-scripting.adoc @@ -0,0 +1,330 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-scripting] +== Extending OpenIDM Functionality By Using Scripts + +Scripting enables you to customize various aspects of OpenIDM functionality, for example, by providing custom logic between source and target mappings, defining correlation rules, filters, and triggers, and so on. + +OpenIDM 4.5 supports scripts written in JavaScript and Groovy. Script options, and the locations in which OpenIDM expects to find scripts, are configured in the `conf/script.json` file for your project. For more information, see xref:chap-configuration.adoc#script-config["Setting the Script Configuration"]. + +OpenIDM includes several default scripts in the following directory `install-dir/bin/defaults/script/`. Do not modify or remove any of the scripts in this directory. OpenIDM needs these scripts to run specific services. Scripts in this folder are not guaranteed to remain constant between product releases. + +If you develop custom scripts, copy them to the `script/` directory for your project, for example, `path/to/openidm/samples/sample2/script/`. + +[#script-endpoint] +=== Validating Scripts Over REST + +OpenIDM exposes a `script` endpoint over which scripts can be validated, by specifying the script parameters as part of the JSON payload. This functionality enables you to test how a script will operate in your deployment, with complete control over the inputs and outputs. Testing scripts in this way can be useful in debugging. + +In addition, the script registry service supports calls to other scripts. For example, you might have logic written in JavaScript, but also some code available in Groovy. Ordinarily, it would be challenging to interoperate between these two environments, but this script service enables you to call one from the other on the OpenIDM router. + +The `script` endpoint supports two actions - `eval` and `compile`. + +The `eval` action evaluates a script, by taking any actions referenced in the script, such as router calls to affect the state of an object. For JavaScript scripts, the last statement that is executed is the value produced by the script, and the expected result of the REST call. + +The following REST call attempts to evaluate the `autoPurgeAuditRecon.js` script (provided in `openidm/bin/defaults/script/audit`), but provides an incorrect purge type (`"purgeByNumOfRecordsToKeep"` instead of `"purgeByNumOfReconsToKeep"`). The error is picked up in the evaluation. The example assumes that the script exists in the directory reserved for custom scripts (`openidm/script`). + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "type": "text/javascript", + "file": "script/autoPurgeAuditRecon.js", + "globals": { + "input": { + "mappings": ["%"], + "purgeType": "purgeByNumOfRecordsToKeep", + "numOfRecons": 1 + } + } + }' \ + "http://localhost:8080/openidm/script?_action=eval" + +"Must choose to either purge by expired or number of recons to keep" +---- + +[TIP] +==== +The variables passed into this script are namespaced with the `"globals"` map. It is preferable to namespace variables passed into scripts in this way, to avoid collisions with the top-level reserved words for script maps, such as `file`, `source`, and `type`. +==== +The `compile` action compiles a script, but does not execute it. This action is used primarily by the UI, to validate scripts that are entered in the UI. A successful compilation returns `true`. An unsuccessful compilation returns the reason for the failure. + +The following REST call tests whether a transformation script will compile. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "type":"text/javascript", + "source":"source.mail ? source.mail.toLowerCase() : null" + }' \ + "http://localhost:8080/openidm/script?_action=compile" +True +---- +If the script is not valid, the action returns an indication of the error, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "type":"text/javascript", + "source":"source.mail ? source.mail.toLowerCase()" + }' \ + "http://localhost:8080/openidm/script?_action=compile" +{ + "code": 400, + "reason": "Bad Request", + "message": "missing : in conditional expression + (3864142CB836831FAB8EAB662F566139CDC22BF2#1) + in 3864142CB836831FAB8EAB662F566139CDC22BF2 + at line number 1 at column number 39" +} +---- + + +[#custom-endpoints] +=== Creating Custom Endpoints to Launch Scripts + +__Custom endpoints__ enable you to run arbitrary scripts through the OpenIDM REST URI. + +Custom endpoints are configured in files named `conf/endpoint-name.json`, where __name__ generally describes the purpose of the endpoint. The endpoint configuration file includes an inline script or a reference to a script file, in either JavaScript or Groovy. The referenced script provides the endpoint functionality. + +A sample custom endpoint configuration is provided in the `openidm/samples/customendpoint` directory. The sample includes three files: +-- + +conf/endpoint-echo.json:: +Provides the configuration for the endpoint. + +script/echo.js:: +Provides the endpoint functionality in JavaScript. + +script/echo.groovy:: +Provides the endpoint functionality in Groovy. + +-- +This sample endpoint is described in detail in xref:../samples-guide/chap-endpoint-sample.adoc#chap-endpoint-sample["Custom Endpoint Sample"] in the __Samples Guide__. + +Endpoint configuration files and scripts are discussed further in the following sections. + +[#adding-custom-endpoints-structure] +==== Creating a Custom Endpoint Configuration File + +An endpoint configuration file includes the following elements: + +[source, javascript] +---- +{ + "context" : "endpoint/linkedView/*", + "type" : "text/javascript", + "source" : "require('linkedView').fetch(request.resourcePath);" +} +---- +-- + +`context`:: +string, optional + ++ +The context path under which the custom endpoint is registered, in other words, the __route__ to the endpoint. An endpoint with the context `endpoint/test` is addressable over REST at the URL `\http://localhost:8080/openidm/endpoint/test` or by using a script such as `openidm.read("endpoint/test")`. + ++ +Endpoint contexts support wild cards, as shown in the preceding example. The `endpoint/linkedview/*` route matches the following patterns: ++ + +[source, console] +---- +endpoint/linkedView/managed/user/bjensen +endpoint/linkedView/system/ldap/account/bjensen +endpoint/linkedView/ +endpoint/linkedView +---- ++ +The `context` parameter is not mandatory in the endpoint configuration file. If you do not include a `context`, the route to the endpoint is identified by the name of the file. For example, in the sample endpoint configuration provided in `openidm/samples/customendpoint/conf/endpoint-echo.json`, the route to the endpoint is `endpoint/echo`. + ++ +Note that this `context` path is not the same as the __context chain__ of the request. For information about the request context chain, see xref:appendix-router.adoc#understanding-request-context["Understanding the Request Context Chain"]. + +`type`:: +string, required + ++ +The type of script to be executed, either `text/javascript` or `groovy`. + +`file` or `source`:: +The path to the script file, or the script itself, inline. + ++ +For example: ++ + +[source] +---- +"file" : "workflow/gettasksview.js" +---- ++ +or ++ + +[source] +---- +"source" : "require('linkedView').fetch(request.resourcePath);" +---- + +-- +You must set authorization appropriately for any custom endpoints that you add, for example, by restricting the appropriate methods to the appropriate roles. For more information, see xref:chap-auth.adoc#openidm-authorization["Authorization"]. + + +[#custom-endpoint-scripts] +==== Writing Custom Endpoint Scripts + +The custom endpoint script files in the `samples/customendpoint/script` directory demonstrate all the HTTP operations that can be called by a script. Each HTTP operation is associated with a `method` - `create`, `read`, `update`, `delete`, `patch`, `action` or `query`. Requests sent to the custom endpoint return a list of the variables available to each method. + +All scripts are invoked with a global `request` variable in their scope. This request structure carries all the information about the request. + +[WARNING] +==== +Read requests on custom endpoints must not modify the state of the resource, either on the client or the server, as this can make them susceptible to CSRF exploits. + +The standard OpenIDM READ endpoints are safe from Cross Site Request Forgery (CSRF) exploits because they are inherently read-only. That is consistent with the __Guidelines for Implementation of REST__, from the US National Security Agency, as "... CSRF protections need only be applied to endpoints that will modify information in some way." +==== +Custom endpoint scripts __must__ return a JSON object. The structure of the return object depends on the `method` in the request. + +The following example shows the `create` method in the `echo.js` file: + +[source, javascript] +---- +if (request.method === "create") { + return { + method: "create", + resourceName: request.resourcePath, + newResourceId: request.newResourceId, + parameters: request.additionalParameters, + content: request.content, + context: context.current +}; +---- +The following example shows the `query` method in the `echo.groovy` file: + +[source, groovy] +---- +else if (request instanceof QueryRequest) { + // query results must be returned as a list of maps + return [ + [ + method: "query", + resourceName: request.resourcePath, + pagedResultsCookie: request.pagedResultsCookie, + pagedResultsOffset: request.pagedResultsOffset, + pageSize: request.pageSize, + queryExpression: request.queryExpression, + queryId: request.queryId, + queryFilter: request.queryFilter.toString(), + parameters: request.additionalParameters, + context: context.toJsonValue().getObject() + ] + ] +} +---- +Depending on the method, the variables available to the script can include the following: +-- + +`resourceName`:: +The name of the resource, without the `endpoint/` prefix, such as `echo`. + +`newResourceId`:: +The identifier of the new object, available as the results of a `create` request. + +`revision`:: +The revision of the object. + +`parameters`:: +Any additional parameters provided in the request. The sample code returns request parameters from an HTTP GET with `?param=x`, as `"parameters":{"param":"x"}`. + +`content`:: +Content based on the latest revision of the object, using `getObject`. + +`context`:: +The context of the request, including headers and security. For more information, see xref:appendix-router.adoc#understanding-request-context["Understanding the Request Context Chain"]. + +Paging parameters:: +The `pagedResultsCookie`, `pagedResultsOffset` and `pageSize` parameters are specific to `query` methods. For more information see xref:chap-data.adoc#paging-query-results["Paging and Counting Query Results"]. + +Query parameters:: +The `queryExpression`, `queryId` and `queryFilter` parameters are specific to `query` methods. For more information see xref:chap-data.adoc#constructing-queries["Constructing Queries"]. + +-- + + +[#custom-script-errors] +==== Setting Up Exceptions in Scripts + +When you create a custom endpoint script, you might need to build exception-handling logic. To return meaningful messages in REST responses and in logs, you must comply with the language-specific method of throwing errors. + +A script written in JavaScript should comply with the following exception format: + +[source, javascript] +---- +throw { + "code": 400, // any valid HTTP error code + "message": "custom error message", + "detail" : { + "var": parameter1, + "complexDetailObject" : [ + "detail1", + "detail2" + ] + } +} +---- +Any exceptions will include the specified HTTP error code, the corresponding HTTP error message, such as `Bad Request`, a custom error message that can help you diagnose the error, and any additional detail that you think might be helpful. + +A script written in Groovy should comply with the following exception format: + +[source, groovy] +---- +import org.forgerock.json.resource.ResourceException +import org.forgerock.json.JsonValue + +throw new ResourceException(404, "Your error message").setDetail(new JsonValue([ + "var": "parameter1", + "complexDetailObject" : [ + "detail1", + "detail2" + ] +])) +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-security.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-security.adoc new file mode 100644 index 000000000..d0ee9a1cd --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-security.adoc @@ -0,0 +1,943 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-security] +== Securing & Hardening OpenIDM + +OpenIDM provides a security management service, that manages keystore and truststore files. The security service is accessible over the REST interface, enabling you to read and import SSL certificates, and to generate certificate signing requests. + +This chapter describes the security management service and its REST interface. + +In addition, the chapter outlines the specific security procedures that you should follow before deploying OpenIDM in a production environment. + +[NOTE] +==== +In a production environment, avoid the use of communication over insecure HTTP, self-signed certificates, and certificates associated with insecure ciphers. +==== + +[#security-management-service] +=== Accessing the Security Management Service + +OpenIDM stores keystore and truststore files in a folder named `/path/to/openidm/security`. These files can be managed by using the `keytool` command, or over the REST interface, at the URL `\https://localhost:8443/openidm/security`. For information about using the `keytool` command, see link:http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html[http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html, window=\_top]. + +The following sections describe how to manage certificates and keys over REST. + +[#display-keystore-over-rest] +==== Displaying the Contents of the Keystore + +OpenIDM generates a symmetric key and a private key the first time the server is started. After startup, display the contents of the keystore over REST, as follows: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/security/keystore" + { + "type" : "JCEKS", + "provider" : { + "Cipher.Blowfish SupportedKeyFormats" : "RAW", + "AlgorithmParameters.DESede" : "com.sun.crypto.provider.DESedeParameters", + "AlgorithmParameters.DES" : "com.sun.crypto.provider.DESParameters", + ... + }, + "aliases" : [ "openidm-sym-default", "openidm-localhost" ] +} +---- +By default, OpenIDM includes the following aliases: + +* `openidm-sym-default` - the default symmetric key that is used, for example, to encrypt the configuration. + +* `openidm-localhost` - the default alias that is used by the Jetty web server to service SSL requests. This alias references a private key and a self-signed certificate. You can use the self-signed certificate for testing purposes. When you deploy OpenIDM in a production environment, you should replace the self-signed certificate with a certificate that has been signed by a certificate authority. + + + +[#import-signed-cert-over-rest] +==== Importing a Signed Certificate into the Keystore + +If you have an existing CA-signed certificate, you can import it into OpenIDM's keystore by running a RESTful `PUT` command on the keystore alias. Include the signed certificate, private key, CA root certificate, and any intermediate certificates in the JSON payload. + +The following command imports a CA-signed certificate, with the alias __example-com__ into the keystore. Replace that alias with the alias of your certificate. + +[source, console] +---- +$ 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 '{ + "alias": "example-com", + "cert": [ + "-----BEGIN CERTIFICATE-----\n +MIIGcDCCBVigAwIBAgIDC23tMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\n +TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\n +YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\n +MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTMwODA3MTMyODAz\n +WhcNMTQwODA4MDY0NTM5WjB2MRkwFwYDVQQNExBwZ3BDaGU4cEJPZnptVE9KMQsw\n +CQYDVQQGEwJHQjEjMCEGA1UEAxMadGVzdC1jb25uZWN0LmZvcmdlcm9jay5jb20x\n +JzAlBgkqhkiG9w0BCQEWGHBvc3RtYXN0ZXJAZm9yZ2Vyb2NrLmNvbTCCASIwDQYJ\n +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJRWGbnMGs+uGKU6ZrlTaaFdPczLqZnv\n +D37T0FOc/X3XXHxSVH94FDk7N4ansP2o6BsDWttIkM2AXkX3efMRaNpgxg7l4+DL\n +opV6H1RkrRba2Lom6Hp2pgkqvOBfd1ZMOmLbjUHt0jhypnIzu7TVwtTH7Ywsrx9F\n +uR9d4veYdW70IeQ64EhUG3RJBGG++AYJZCOjgEfbCwAYe/NoX/YVu+aMreHMR/+0\n +CV0YXKvHZgytcwZIc5WkQYaSWQA9lDWZzt5XjCErCATfiGEQ0k02QgpEfNTXxwQs\n +kfxh//O/qbfOWmloGwVU/2NY+5z3ZW8/eCksmiL1gGAYQAd+9+WI7BsCAwEAAaOC\n +Au4wggLqMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUF\n +BwMBMB0GA1UdDgQWBBR2zHzb71ZOHSwDZk28L9It3PvOtzAfBgNVHSMEGDAWgBTr\n +QjTQmLCrn/Qbawj3zGQu7w4sRTA0BgNVHREELTArghp0ZXN0LWNvbm5lY3QuZm9y\n +Z2Vyb2NrLmNvbYINZm9yZ2Vyb2NrLmNvbTCCAVYGA1UdIASCAU0wggFJMAgGBmeB\n +DAECATCCATsGCysGAQQBgbU3AQIDMIIBKjAuBggrBgEFBQcCARYiaHR0cDovL3d3\n +dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjCB9wYIKwYBBQUHAgIwgeowJxYgU3Rh\n +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwAwIBARqBvlRoaXMgY2VydGlm\n +aWNhdGUgd2FzIGlzc3VlZCBhY2NvcmRpbmcgdG8gdGhlIENsYXNzIDEgVmFsaWRh\n +dGlvbiByZXF1aXJlbWVudHMgb2YgdGhlIFN0YXJ0Q29tIENBIHBvbGljeSwgcmVs\n +aWFuY2Ugb25seSBmb3IgdGhlIGludGVuZGVkIHB1cnBvc2UgaW4gY29tcGxpYW5j\n +ZSBvZiB0aGUgcmVseWluZyBwYXJ0eSBvYmxpZ2F0aW9ucy4wNQYDVR0fBC4wLDAq\n +oCigJoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggr\n +BgEFBQcBAQSBgTB/MDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5zdGFydHNzbC5j\n +b20vc3ViL2NsYXNzMS9zZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly9haWEu\n +c3RhcnRzc2wuY29tL2NlcnRzL3N1Yi5jbGFzczEuc2VydmVyLmNhLmNydDAjBgNV\n +HRIEHDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8wDQYJKoZIhvcNAQEFBQAD\n +ggEBAKVOAHtXTrgISj7XvE4/lLxAfIP56nlhpoLu8CqVlLK6eK4zCQRyTiFYx3xq\n +VQMSNVgQIdimjEsMz8o5/fDrCrozsT6sqxIPFsdgdskPyz9YyC9Y/AVBuECxabQr\n +B//0STicfdPg8PuDYtI64/INA47d/gtb57RaTFYxKs6bU8vtObinDJCwT33x4tvt\n +ob18DwB3/PeTbWyVUIxB0nvfm89dys0SF2alaA/bLuy0B7rdlppd4dOMpmiD0tnI\n +DORtr5HOD1xGiixZWzA1V2pTmF/hJZbhmEgBUSIyPK5Z9pZPephMf+/KrovbQqKr\n +6SEjgs7dGwpo6fA2mfCH5cCrid0=\n +-----END CERTIFICATE-----", + "-----BEGIN CERTIFICATE-----\n +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\n +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\n +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\n +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\n +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\n +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\n +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\n +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\n +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\n +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\n +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\n +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\n +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\n +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\n +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\n +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\n +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\n +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n +-----END CERTIFICATE-----" + ], + "privateKey": "-----BEGIN RSA PRIVATE KEY-----\n +zDot5q3vP9YjCihMZMkSa0zT2Zt+8S+mC0EVuYuTVhVpqrVNtkP1mlt+CYqmDffY\n +sGuD6SMrT6+SeAzX2uYFgY4+s8yaRWBcr0C5Z7yihilM6BK+IJ4is9kaW5VFr1Ph\n +wRKvSeFHBGh2wLNpjVSNPzLMDZBtkVi9Ny/xD5C3M1Gah0PGmnrPGCP8tr1Lshv4\n +PxYJwzHzouTdQDkLYlCjMN++NmIYfx7zrbEYV4VzXMxgNq7d3+d5dlVfE8xpAjSR\n +Lqlamib+doe1oWOQ2WiS6baBAH+Gw5rgqfwhJbCY/UlbCpuJ6kl7TLvTrFp8YpvB\n +Iv1GD0yuwSued3a+AxMFuIzTBYd2rC6rHq+eF4eHd/Q/Sbm9+9VuW/h8dW3LGvbE\n +5SUUhNw6uSkOZmZ0z/+FLbwoLPCASukY9biSd+12KJf4N42WZxID+9mJTp1j/Bv7\n +n29oGfZ3vav8PqG+F987hSyWEIdGTMfIxwaUrdYe1fmbUCxv0suMcYTRbAs9g3cm\n +eCNxbZBYC/fL+Nlj5NjZ+gxA/tEXV7wWynPZW3mZny6fQpDTDMslqsoFZR+rAUzH\n +ViePuLbCdxIC5heUyqvDBbeOzgQWOu6SZjX+mAQpo0DPKt1KDP4DKv9EW92sIwW3\n +AnFg98sje0DZ+zfsnevGioQMJrG0JSnqTYADxHaauu7NWndkfMZisfNIKA0u+ajU\n +AbP8xFXIP5JU8O4tWmlbxAbMOYfrZHabFNZx4DH1OVOJqdJIVx0KER0GSZd50D6W\n +QBzCfEbwMlJ17OB0AgWzNrbaak3MCmW1mh7OecjQwge1ajy7ho+JtQ==\n +-----END RSA PRIVATE KEY-----" + }' \ + "https://localhost:8443/openidm/security/keystore/cert/example-com" + + { + "_id": "example-com", + "alias": "example-com", + "cert": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----", + "privateKey": "-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----" +} +---- +If the import is successful, the command returns the certificate alias that has been added to the keystore, along with the certificates and keys. + +[IMPORTANT] +==== +By default, OpenIDM uses the certificate with the alias `openidm-localhost` to service SSL requests. If you use a different certificate alias, you must change the value of the `openidm.https.keystore.cert.alias` property in your project's `conf/boot/boot.properties` file to match the new alias, so that OpenIDM can use the new signed certificate. This change requires a server restart. +==== + + +[#csr-over-rest] +==== Generating a Certificate Signing Request Over REST + +If you do not have an existing signed certificate, you can generate a certificate signing request (CSR) over REST, as described in this section. The details of the CSR are specified in JSON format, for example: + +[source, javascript] +---- +{ + "CN" : "www.example.com", + "OU" : "HR", + "L" : "Cupertino", + "C" : "US" +} +---- +For information about the complete contents of a CSR, see link:http://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html[http://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html, window=\_top]. + +To generate a CSR over the REST interface, include the private key alias in the URL. The following example uses the alias `example-com`). Set `"returnPrivateKey" : true` to return the private key along with the request. + +[source, console] +---- +$ 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 '{"CN" : "www.example.com", + "OU" : "HR", + "L" : "Cupertino", + "C" : "US", + "returnPrivateKey" : true, + "alias" : "example-com"}' \ + "https://localhost:8443/openidm/security/keystore?_action=generateCSR" +{ + "_id": "example-com", + "csr": "-----BEGIN CERTIFICATE REQUEST-----\n +MIICmzCCAYMCAQAwWDEZMBcGA1UEAwwQd3d3MS5 +leGFtcGxlLmNvbTELMAkGA1UE\nCwwCSFIxDTALBgNVBAoMBE5vbmUxEjAQBgNVBAcMCUN1cGVyd +GlubzELMAkGA1UE\nBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAjCjTt1b +o0WKH\nP/4PR/Td3A1ElTo4/J/7o7eWflOqs8vW5d76SMcJFKOQ6FhoOcOHRNewch+a0DBK\njKF +aRCE1c0PuXiIlrO7wsF4dFTtTZKAhrpFdM+0hU4LeyCDxQQ5UDga3rmyVIvC8\nL1PvW+sZEcZ9r +T67XOV03cwUpjvG4W58FCUKd6UAI0szfIrFdvJp4q4LkkBNkk9J\nUf+MXsSVuHzZrqvqhX900Is +a19mXD6/P9Cql8KmwEzzbglGFf6uYAK33F71Kx409\nTeS85sjmBbyJwUVwhgQ0R35H3HC6jex4P +jx1rSfPmsi61JBx9kyGu6rnSv5FOQGy\nBQpgQFnJAgMBAAEwDQYJKoZIhvcNAQENBQADggEBAKc +yInfo2d7/12jUrOjL4Bqt\nStuQS/HkO2KAsc/zUnlpJyd3RPI7Gs1C6FxIRVCzi4Via5QzE06n2 +F8HHkinqc6m\nBWhIcf5Omk6fSqG0aw7fqn20XWDkRm+I4vtm8P8CuWftUj5qv5kmyUtrcQ3+YPD +O\nL+cK4cfuCkjLQ3h4GIgBJP+gfWX8fTmCHyaHEFjLTMj1hZYEx+3f8awOVFoNmr3/\nB8LIJNH +UiFHO6EED7LDOwa/z32mTRET0nK5DVO60H80JSWxzdWYZQV/IzHzm8ST4\n6j6vuheBZiG5gZR2V +F0x5XoudQrSg7lpVslXBHNeiM85+H08RMQh8Am2bp+Xstw=\n", + -----END CERTIFICATE REQUEST-----\n", + "publicKey": { + "format": "X.509", + "encoded": "-----BEGIN PUBLIC KEY-----\n +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr +ALtYU662bNbQZG7JZ3M\noOUmVP9cPP3+DhQ5H0V0qB+9YjE4XUtuwUGqaUmuT+mrXHwGpLAqvUm +NsVyXJj9s\nJhX6PCyXzO3RdKBVC8pphMfKXodjBC57ef0OkWjO5ZRAqCRwS3BXkoCfu6/ZXRpk\ +ncc/A1RmLZdPmcuKmN5vQl4E3Z6F4YyG7M0g7TE54dhqPvGNS9cO4r0Vom9373MDh\n+8QSfmLCC +94Ro+VUAF9Q6nk2j0PgTi+QZ0i93jbKAWWX57w6S5i7CpEptKyeP9iG\ncFnJddSICPHkbQJ73gu +lyZYkbcBblNUxIhODZV5bJ0oxn9qgYvzlxJupldYsYkBo\ncwIDAQAB\n + -----END PUBLIC KEY-----\n", + "algorithm": "RSA" + }, + "privateKey": { + "format": "PKCS#8", + "encoded": "-----BEGIN RSA PRIVATE KEY-----\n +MIIEpAIBAAKCAQEArALtYU662bNbQZG7JZ3MoOU +VP9cPP3+DhQ5H0V0qB+9YjE4\nXUtuwUGqaUmuT+mrXHwGpLAqvUmNsVyXJj9sJhX6PCyXzO3RdK +BVC8pphMfKXodj\nBC57ef0OkWjO5ZRAqCRwS3BXkoCfu6/ZXRpkcc/A1RmLZdPmcuKmN5vQl4E3 +Z0i93jbKAWWX57w6S5i7CpEptKyeP9iGcFnJddSICPHkbQJ73gulyZYkbcBb\nlNUxIhODZV5bJ0 +Z6F4\nYyG7M0g7TE54dhqPvGNS9cO4r0Vom9373MDh+8QSfmLCC94Ro+VUAF9Q6nk2j0Pg\nTi+Q +oxn9qgYvzlxJupldYsYkBocwIDAQABAoIBAGmfpopRIPWbaBb8\nWNIBcuz9qSsaX1ZolP+qNWVZ +bgfq7Y0FMlo/frQXEYBzqSETGJHC6wVn0+bF6scV\nVw86dLtyVWVr8I77HdoitfZ2hZLuZ/rh4d +BohpPi63YoyJs7DPTy4y2/v1aLuwoy\nMiQ0l6c3bm6sr+eIVgMH4A9Xk5/jzAHVTCBrvfTYZnh6 +qD4Qmiuj8pQn79HQV8NK\nLt/5kmV1+uGj78jg7NR06NjNsa4L3mNZSiqsn2haPXZAnBjKfWApxe +GugURgNBCO\ncmYqCDZLvpMy4S/qoRBu+6qdYGprb+tHshBYNywuDkrgszhwgr5yRm8VQ60T9tM/ +\nceKM+TECgYEA2Az2DkpC9TjJHPJG7x4boRRVqV5YRgPf5MrU+7PxDMb+EauXXUXg\nsch9Eeon +30yINqSv6FwATLVlkzQpZLkkJ6GJqAxUmPjRslAuosiSJqKaWamDUDbz\nSu/7iANJWvRGayqZsa +GQqFwM0Xpfp/EiBGe757k0D02u8sAv94A75bsCgYEAy9FQ\nMwDU3CaDzgv0qgR1ojXkSW0dCbv0 +QPEkKZ2Ik7JbXzwVGzfdv2VUVrzRKBGReYzn\nGg/s4HbZkYy4O+SJo44n/5iO2pgKG5MEDFHSpw +X54Rm+qabT2fQ2lFJ/myWKsPgJ\n4gZ9bUvcemCcLLzsiAphueulQp49eOLnkzPlQKkCgYEAy7A0 +jrZuuDjoStUUET5G\neC/urvZWrPPcMx0TfZZhTVWSlWA8HWDS/WnymGA1ZS4HQdU0TxHl6mwerp +C/8ckn\nEAIZAQlW/L2hHcbAoRIN0ET+1kedmJOl/mGQt+O5Vfn1JfYM3s5ezouyPhBsfK43\nDw +Ypvsb6EO+BYDXXQzVvwx8CgYB9o67LcfTFLNzNFCOi9pLJBm2OMbvXt0wPCFch\nbCG34hdfMntU +RvDjvgPqYASSrZm+kvQW5cBAciMWDOe4y91ovAW+En3lFBoO+2Zg\nbcPr/8wUTblxfQxU660Fa4 +GL0u2Wv5/f+94vlLb5nTpIfcFU7wllAXTjBwaf0Uet\nPy1P2QKBgQDPoyJqPi2TdN7ZQYcoXAM4 +Gl5Yv9oO16RC917XH6SLvj0ePmdLgBXo\nrR6aAmOjLzFp9jiytWZqVR9DbAWd2YNpvQav4Gude3 +lteew02UT+GNv/gC71bXCw\ncFTxnmKjP8YYIBBqZXzuk9wEaHN7OdGybUW0dsBCGxTXwDKe8XiA +6w==\n-----END RSA PRIVATE KEY-----\n", + "algorithm": "RSA" +} +---- +This sample request returns the CSR, the private key associated with the request, and the public key. The security management service stores the private key in the repository. + +When the signed certificate is returned by the certificate authority and you import the certificate into the keystore, you do not need to supply the private key. The security management service locates the private key in the repository, adds the certificate chain, and loads it into the keystore. + +If you will be importing the signed certificate into the keystore of an OpenIDM instance that is not connected to the repository in which this private key was stored, you must include the private key when you import the signed certificate. Setting `"returnPrivateKey" : true` in the CSR enables you to maintain a copy of the private key for this purpose. + +Send the output from + +[source, console] +---- +"csr": "-----BEGIN CERTIFICATE REQUEST----- + ... + -----END CERTIFICATE REQUEST----- +---- +to your certificate authority for signature. + +When the signed certificate is returned, import it into the keystore, as described in xref:#import-signed-cert-over-rest["Importing a Signed Certificate into the Keystore"]. + + +[#certificate-over-rest] +==== Generating a Self-Signed Certificate Over REST + +To generate a self-signed X.509 certificate, use the `generateCert` action on the `keystore` endpoint. This action must be performed as an authenticated administrative user. The generated certificate is returned in the response to the request, and stored in the OpenIDM keystore. + +Specify the details of the certificate in the JSON payload. For example: + +[source, console] +---- +$ 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 '{ + "algorithm" : "RSA", + "signatureAlgorithm" : "SHA512WithRSAEncryption", + "keySize" : 2048, + "domainName" : "www.example.com", + "validFrom" : "2015-08-13T07:59:44.497+02:00", + "validTo" : "2016-08-13T07:59:44.497+02:00", + "returnPrivateKey" : true, + "alias" : "new-alias" + }' \ + "https://localhost:8443/openidm/security/keystore?_action=generateCert" +{ + "publicKey": { + "algorithm": "RSA", + "encoded": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB + ... + \n-----END PUBLIC KEY-----\n", + "format": "X.509" + }, + "cert": "-----BEGIN CERTIFICATE-----\nMIIDSDCCAjCgAwIBAgIGAUfOo3GvMA0GCSqGSIb3 + ... + \n-----END CERTIFICATE-----\n", + "type": "X.509", + "_id": "new-alias" +} +---- +The following certificate details can be specified: + +* `"algorithm"` (optional) - the public key algorithm, for example, `RSA`. If no algorithm is specified, a default of `RSA` is used. + +* `"signatureAlgorithm"` (optional) - the signature type, for example, `SHA512WithRSAEncryption`. If no algorithm is specified, a default of `SHA512WithRSAEncryption` is used. + +* `"keySize"` (optional) - the size of the key (in bits) used in the cryptographic algorithm, for example `2048`. If no key size is specified, a default of `2048` is used. + +* `"domainName"` - the fully qualified domain name (FQDN) of your server, for example `www.example.com`. + +* `"validFrom"` and `"validTo"` (optional) - the validity period of the certificate, in UTC time format, for example `2014-08-13T07:59:44.497+02:00`. If no values are specified, the certificate is valid for one year, from the current date. + +* `"returnPrivateKey"` (optional) - set this to `true` to return the private key along with the request. + +* `"alias"` - the keystore alias or string that identifies the certificate, for example `openidm-localhost`. + + + + +[#security-precautions] +=== Security Precautions for a Production Environment + +Out of the box, OpenIDM is set up for ease of development and deployment. When you deploy OpenIDM in production, there are specific precautions you should take to minimize security breaches. After following the guidance in this section, make sure that you test your installation to verify that it behaves as expected before putting it into production. + +[#security-ssl-https] +==== Use SSL and HTTPS + +Disable plain HTTP access, as described in xref:#security-jetty["Secure Jetty"]. + +Use TLS/SSL to access OpenIDM, ideally with mutual authentication so that only trusted systems can invoke each other. TLS/SSL protects data on the network. Mutual authentication with strong certificates, imported into the trust and keystores of each application, provides a level of confidence for trusting application access. + +Augment this protection with message level security where appropriate. + + +[#rest-over-https] +==== Restrict REST Access to the HTTPS Port + +When possible, use a certificate to secure REST access, over HTTPS. For production, that certificate should be signed by a certificate authority. + +OpenIDM generates a self-signed certificate when it first starts up. You can use this certificate to test secure REST access. + +While not recommended for production, you can test secure REST access using the default self-signed certificate. To do so, you can create a self-signed certificate file, `self-signed.crt`, using the following procedure: + +==== + +. Extract the certificate that is generated when OpenIDM starts up. ++ + +[source, console] +---- +$ openssl s_client -showcerts -connect localhost:8443 old-password +New key password for : keystore-pwd +Re-enter new key password for : keystore-pwd +---- +==== + + +[#security-jetty] +==== Secure Jetty + +If you do not want to use regular HTTP on a production OpenIDM system, you need to make two changes. + +First, edit the `openidm/conf/jetty.xml` configuration file. Comment out or delete the `` code block that includes the `openidm.port.http` property. Keep the `` code blocks that contain the `openidm.port.https` and `openidm.port.mutualauth` properties. You can set the value for these properties in the `conf/boot/boot.properties` file. + +Second, edit the `openidm/conf/config.properties` configuration file. Set the `org.osgi.service.http.enabled` property to false, as shown in the following excerpt: + +[source] +---- +# Enable pax web http/https services to enable jetty +org.osgi.service.http.enabled=false +org.osgi.service.http.secure.enabled=true +---- + + +[#security-urls] +==== Protect Sensitive REST Interface URLs + +Anything attached to the router is accessible with the default policy, including the repository. If you do not need such access, deny it in the authorization policy to reduce the attack surface. + +In addition, you can deny direct HTTP access to system objects in production, particularly access to `action`. As a rule of thumb, do not expose anything that is not used in production. + +For an example that shows how to protect sensitive URLs, see xref:chap-auth.adoc#access-js["Understanding the Access Configuration Script (access.js)"]. + +OpenIDM supports native query expressions on the repository, and you can enable these over HTTP. For example, the following query returns all managed users in an OrientDB repository: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + "https://localhost:8443/openidm/managed/user?_queryExpression=select+*+from+managed_user" +---- +By default, direct HTTP access to native queries is disallowed, and should remain so in production systems. + +For testing or development purposes, it can be helpful to enable native queries on the repository over HTTP. To do so, edit the access control configuration file (`access.js`). In that file, remove any instances of `"disallowQueryExpression()"` such as the following: + +[source, javascript] +---- +// openidm-admin can request nearly anything (except query expressions on repo endpoints) +{ + "pattern" : "*", + "roles" : "openidm-admin", + "methods" : "*", // default to all methods allowed + "actions" : "*", // default to all actions allowed + // "customAuthz" : "disallowQueryExpression()", + "excludePatterns": "repo,repo/*" +}, +// additional rules for openidm-admin that selectively enable certain parts of system/ +{ + "pattern" : "system/*", + "roles" : "openidm-admin", + "methods" : "create,read,update,delete,patch,query", // restrictions on 'action' + "actions" : "" + // "customAuthz" : "disallowQueryExpression()" +}, +---- + + +[#security-files] +==== Protect Sensitive Files & Directories + +Protect OpenIDM files from access by unauthorized users. + +In particular, prevent other users from reading files in at least the `openidm/conf/boot/` and `openidm/security/` directories. + +The objective is to limit access to the user that is running the service. Depending on the operating system and configuration, that user might be `root`, `Administrator`, `openidm`, or something similar. + +[#security-files-unix] +.Protecting key files in Unix +==== + +. For the target directory, and the files therein, make sure user and group ownership is limited to the user that is running the OpenIDM service. + +. Disable access of any sort for `other` users. One simple command for that purpose, from the `/path/to/openidm` directory, is: ++ + +[source, console] +---- +# chmod -R o-rwx . +---- + +==== + +[#security-files-windows] +.Protecting key files in Windows +==== + +. The OpenIDM process in Windows is normally run by the `Administrator` user. + +. If you are concerned about the security of the administrative account, you can `Deny` permissions on the noted directories to existing users, or alternatively the `Users` group. + +==== + + +[#security-remove-dev-tools] +==== Remove or Protect Development & Debug Tools + +Before you deploy OpenIDM in production, remove or protect development and debug tools, including the OSGi console that is exposed under `/system/console`. Authentication for this console is not integrated with authentication for OpenIDM. + +To remove the OSGi console, remove the web console bundle, and all of the plugin bundles related to the web console, as follows: + +[source, console] +---- +$ cd /path/to/openidm/bundle +$ rm org.apache.felix.webconsole*.jar +---- +If you cannot remove the OSGi console, protect the console by overriding the default `admin:admin` credentials. Create a file named `openidm/conf/org.apache.felix.webconsole.internal.servlet.OsgiManager.cfg` that contains the user name and password to access the console in Java properties file format, for example: + +[source, ini] +---- +username=user-name +password=password +---- + + +[#security-protect-repo] +==== Protect the OpenIDM Repository + +OpenIDM 4.5 only supports the use of a JDBC repository in production. + +Use a strong password for the JDBC connection and change at least the password of the database user (`openidm` by default). When you change the database username and/or password, you must update the database connection configuration file (`datasource.jdbc-default.json`) for your repository type. + +For example, the following excerpt of a MySQL connection configuration file indicates the required change when the database user password has been changed to `myPassw0rd`. + +[source, javascript] +---- +{ + "driverClass" : "com.mysql.jdbc.Driver", + "jdbcUrl" : "jdbc:mysql://localhost:3306/openidm?allowMultiQueries=true&characterEncoding=utf8", + "databaseName" : "openidm", + "username" : "openidm", + "password" : "myPassw0rd", + "connectionTimeout" : 30000, + "connectionPool" : { + "type" : "bonecp" + } +} +---- +Use a case sensitive database, particularly if you work with systems with different identifiers that match except for case. Otherwise correlation queries or correlation scripts can pick up identifiers that should not be considered the same. + + +[#remove-orientdb] +==== Remove OrientDB Studio + +OpenIDM ships with the OrientDB Studio web application. ForgeRock strongly recommends that you remove the web application before deploying in a production environment. To remove OrientDB studio, delete the following directory: + +[source, console] +---- +/path/to/openidm/db/util/orientdb +---- +Verify that the application has been removed by trying to access `\http://localhost:2480/`. + +Note that an error will be logged on startup when you have removed OrientDB Studio. You can safely ignore this error. + + +[#security-adjust-log-levels] +==== Adjust Log Levels + +Leave log levels at `INFO` in production to ensure that you capture enough information to help diagnose issues. For more information, see xref:chap-logs.adoc#chap-logs["Configuring Server Logs"]. + +At start up and shut down, `INFO` can produce many messages. Yet, during stable operation, `INFO` generally results in log messages only when coarse-grain operations such as scheduled reconciliation start or stop. + + +[#security-run-as-service] +==== Set Up Restart At System Boot + +You can run OpenIDM in the background as a service (daemon), and add startup and shutdown scripts to manage the service at system boot and shutdown. For more information, see xref:chap-services.adoc#chap-services["Starting and Stopping OpenIDM"]. + +See your operating system documentation for details on adding a service such as OpenIDM to be started at boot and shut down at system shutdown. + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-services.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-services.adoc new file mode 100644 index 000000000..1d923abbf --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-services.adoc @@ -0,0 +1,864 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-services] +== Starting and Stopping OpenIDM + +This chapter covers the scripts provided for starting and stopping OpenIDM, and describes how to verify the __health__ of a system, that is, that all requirements are met for a successful system startup. + +[#starting-and-stopping] +=== To Start and Stop OpenIDM + +By default you start and stop OpenIDM in interactive mode. +To start OpenIDM interactively, open a terminal or command window, change to the `openidm` directory, and run the startup script: + +* `startup.sh` (UNIX) + +* `startup.bat` (Windows) + +The startup script starts OpenIDM, and opens an OSGi console with a `->` prompt where you can issue console commands. + +To stop OpenIDM interactively in the OSGi console, run the `shutdown` command: + +[source, console] +---- +-> shutdown +---- +You can also start OpenIDM as a background process on UNIX and Linux. Follow these steps __before starting OpenIDM for the first time__. + +. If you have already started OpenIDM, shut down OpenIDM and remove the Felix cache files under `openidm/felix-cache/`: ++ + +[source, console] +---- +-> shutdown +... +$ rm -rf felix-cache/* +---- + +. Start OpenIDM in the background. The `nohup` survives a logout and the `2>&1&` redirects standard output and standard error to the noted `console.out` file: ++ + +[source, console] +---- +$ nohup ./startup.sh > logs/console.out 2>&1& +[1] 2343 +---- + +To stop OpenIDM running as a background process, use the `shutdown.sh` script: + +[source, console] +---- +$ ./shutdown.sh +./shutdown.sh +Stopping OpenIDM (2343) +---- +Incidentally, the process identifier (PID) shown during startup should match the PID shown during shutdown. + +[NOTE] +==== +Although installations on OS X systems are not supported in production, you might want to run OpenIDM on OS X in a demo or test environment. To run OpenIDM in the background on an OS X system, take the following additional steps: + +* Remove the `org.apache.felix.shell.tui-*.jar` bundle from the `openidm/bundle` directory. + +* Disable `ConsoleHandler` logging, as described in xref:chap-logs.adoc#log-disabling["Disabling Logs"]. + +==== + + +[#startup-configuration] +=== Specifying the OpenIDM Startup Configuration + +By default, OpenIDM starts with the configuration, script, and binary files in the `openidm/conf`, `openidm/script`, and `openidm/bin` directories. You can launch OpenIDM with a different set of configuration, script, and binary files for test purposes, to manage different OpenIDM projects, or to run one of the included samples. +The `startup.sh` script enables you to specify the following elements of a running OpenIDM instance: + +* `--project-location` or `-p` `/path/to/project/directory` ++ +The project location specifies the directory with OpenIDM configuration and script files. ++ +All configuration objects and any artifacts that are not in the bundled defaults (such as custom scripts) __must__ be included in the project location. These objects include all files otherwise included in the `openidm/conf` and `openidm/script` directories. ++ +For example, the following command starts OpenIDM with the configuration of Sample 1, with a project location of `/path/to/openidm/samples/sample1`: ++ + +[source, console] +---- +$ ./startup.sh -p /path/to/openidm/samples/sample1 +---- ++ +If you do not provide an absolute path, the project location path is relative to the system property, `user.dir`. OpenIDM then sets `launcher.project.location` to that relative directory path. Alternatively, if you start OpenIDM without the `-p` option, OpenIDM sets `launcher.project.location` to `/path/to/openidm/conf`. ++ + +[NOTE] +==== +When we refer to "your project" in ForgeRock's OpenIDM documentation, we're referring to the value of `launcher.project.location`. +==== + +* `--working-location` or `-w` `/path/to/working/directory` ++ +The working location specifies the directory to which OpenIDM writes its database cache, audit logs, and felix cache. The working location includes everything that is in the default `db/` and `audit/`, and `felix-cache/` subdirectories. ++ +The following command specifies that OpenIDM writes its database cache and audit data to `/Users/admin/openidm/storage`: ++ + +[source, console] +---- +$ ./startup.sh -w /Users/admin/openidm/storage +---- ++ +If you do not provide an absolute path, the path is relative to the system property, `user.dir`. If you do not specify a working location, OpenIDM writes this data to the `openidm/db`, `openidm/felix-cache` and `openidm/audit` directories. ++ +Note that this property does not affect the location of the OpenIDM system logs. To change the location of the OpenIDM logs, edit the `conf/logging.properties` file. ++ +You can also change the location of the Felix cache, by editing the `conf/config.properties` file, or by starting OpenIDM with the `-s` option, described later in this section. + +* `--config` or `-c` `/path/to/config/file` ++ +A customizable startup configuration file (named `launcher.json`) enables you to specify how the OSGi Framework is started. ++ +Unless you are working with a highly customized deployment, you should not modify the default framework configuration. This option is therefore described in more detail in xref:chap-advanced.adoc#chap-advanced["Advanced Configuration"]. + +* `--storage` or `-s` `/path/to/storage/directory` ++ +Specifies the OSGi storage location of the cached configuration files. ++ +You can use this option to redirect output if you are installing OpenIDM on a read-only filesystem volume. For more information, see xref:../install-guide/appendix-ro-install.adoc#appendix-ro-install["Installing OpenIDM on a Read-Only Volume"] in the __Installation Guide__. This option is also useful when you are testing different configurations. Sometimes when you start OpenIDM with two different sample configurations, one after the other, the cached configurations are merged and cause problems. Specifying a storage location creates a separate `felix-cache` directory in that location, and the cached configuration files remain completely separate. + +By default, properties files are loaded in the following order, and property values are resolved in the reverse order: + +. `system.properties` + +. `config.properties` + +. `boot.properties` + +If both system and boot properties define the same attribute, the property substitution process locates the attribute in `boot.properties` and does not attempt to locate the property in `system.properties`. + +You can use variable substitution in any `.json` configuration file with the install, working and project locations described previously. You can substitute the following properties: +[none] +* `install.location` +* `install.url` +* `working.location` +* `working.url` +* `project.location` +* `project.url` +Property substitution takes the following syntax: + +[source, console] +---- +&{launcher.property} +---- +For example, to specify the location of the OrientDB database, you can set the `dbUrl` property in `repo.orientdb.json` as follows: + +[source, javascript] +---- +"dbUrl" : "local:&{launcher.working.location}/db/openidm", +---- +The database location is then relative to a working location defined in the startup configuration. + +You can find more examples of property substitution in many other files in your project's `conf/` subdirectory. + +Note that property substitution does not work for connector reference properties. So, for example, the following configuration would not be valid: + +[source, javascript] +---- +"connectorRef" : { + "connectorName" : "&{connectorName}", + "bundleName" : "org.forgerock.openicf.connectors.ldap-connector", + "bundleVersion" : "&{LDAP.BundleVersion}" + ... +---- +The `"connectorName"` must be the precise string from the connector configuration. If you need to specify multiple connector version numbers, use a range of versions, for example: + +[source, javascript] +---- +"connectorRef" : { + "connectorName" : "org.identityconnectors.ldap.LdapConnector", + "bundleName" : "org.forgerock.openicf.connectors.ldap-connector", + "bundleVersion" : "[1.4.0.0,2.0.0.0)", + ... +---- + + +[#system-healthcheck] +=== Monitoring the Basic Health of an OpenIDM System + +Due to the highly modular, configurable nature of OpenIDM, it is often difficult to assess whether a system has started up successfully, or whether the system is ready and stable after dynamic configuration changes have been made. + +OpenIDM includes a health check service, with options to monitor the status of internal resources. + +To monitor the status of external resources such as LDAP servers and external databases, use the commands described in xref:chap-resource-conf.adoc#systems-over-rest["Checking the Status of External Systems Over REST"]. + +[#basic-health-check] +==== Basic Health Checks + +The health check service reports on the state of the OpenIDM system and outputs this state to the OSGi console and to the log files. The system can be in one of the following states: + +* `STARTING` - OpenIDM is starting up + +* `ACTIVE_READY` - all of the specified requirements have been met to consider the OpenIDM system ready + +* `ACTIVE_NOT_READY` - one or more of the specified requirements have not been met and the OpenIDM system is not considered ready + +* `STOPPING` - OpenIDM is shutting down + +You can verify the current state of an OpenIDM system with the following REST call: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/info/ping" + +{ + "_id" : "", + "state" : "ACTIVE_READY", + "shortDesc" : "OpenIDM ready" +} +---- +The information is provided by the following script: `openidm/bin/defaults/script/info/ping.js`. + + +[#current-session-info] +==== Getting Current OpenIDM Session Information + +You can get more information about the current OpenIDM session, beyond basic health checks, with the following REST call: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"https://localhost:8443/openidm/info/login" +{ + "_id" : "", + "class" : "org.forgerock.services.context.SecurityContext", + "name" : "security", + "authenticationId" : "openidm-admin", + "authorization" : { + "id" : "openidm-admin", + "component" : "repo/internal/user", + "roles" : [ "openidm-admin", "openidm-authorized" ], + "ipAddress" : "127.0.0.1" + }, + "parent" : { + "class" : "org.forgerock.caf.authentication.framework.MessageContextImpl", + "name" : "jaspi", + "parent" : { + "class" : "org.forgerock.services.context.TransactionIdContext", + "id" : "2b4ab479-3918-4138-b018-1a8fa01bc67c-288", + "name" : "transactionId", + "transactionId" : { + "value" : "2b4ab479-3918-4138-b018-1a8fa01bc67c-288", + "subTransactionIdCounter" : 0 + }, + "parent" : { + "class" : "org.forgerock.services.context.ClientContext", + "name" : "client", + "remoteUser" : null, + "remoteAddress" : "127.0.0.1", + "remoteHost" : "127.0.0.1", + "remotePort" : 56534, + "certificates" : "", +... +---- +The information is provided by the following script: `openidm/bin/defaults/script/info/login.js`. + + +[#detailed-health-check] +==== Monitoring OpenIDM Tuning and Health Parameters + +You can extend OpenIDM monitoring beyond what you can check on the `openidm/info/ping` and `openidm/info/login` endpoints. Specifically, you can get more detailed information about the state of the: + +* `Operating System` on the `openidm/health/os` endpoint + +* `Memory` on the `openidm/health/memory` endpoint + +* `JDBC Pooling`, based on the `openidm/health/jdbc` endpoint + +* `Reconciliation`, on the `openidm/health/recon` endpoint. + +You can regulate access to these endpoints as described in the following section: xref:chap-auth.adoc#access-js["Understanding the Access Configuration Script (access.js)"]. + +[#health-check-os] +===== Operating System Health Check + +With the following REST call, you can get basic information about the host operating system: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/health/os" +{ + "_id" : "", + "_rev" : "", + "availableProcessors" : 1, + "systemLoadAverage" : 0.06, + "operatingSystemArchitecture" : "amd64", + "operatingSystemName" : "Linux", + "operatingSystemVersion" : "2.6.32-504.30.3.el6.x86_64" +} +---- +From the output, you can see that this particular system has one 64-bit CPU, with a load average of 6 percent, on a Linux system with the noted kernel `operatingSystemVersion` number. + + +[#health-check-memory] +===== Memory Health Check + +With the following REST call, you can get basic information about overall JVM memory use: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/health/memory" +{ + "_id" : "", + "_rev" : "", + "objectPendingFinalization" : 0, + "heapMemoryUsage" : { + "init" : 1073741824, + "used" : 88538392, + "committed" : 1037959168, + "max" : 1037959168 + }, + "nonHeapMemoryUsage" : { + "init" : 24313856, + "used" : 69255024, + "committed" : 69664768, + "max" : 224395264 + } +} +---- +The output includes information on JVM Heap and Non-Heap memory, in bytes. Briefly, + +* JVM Heap memory is used to store Java objects. + +* JVM Non-Heap Memory is used by Java to store loaded classes and related meta-data + + + +[#health-check-jdbc] +===== JDBC Health Check + +With the following REST call, you can get basic information about the status of the configured internal JDBC database: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/health/jdbc" +{ + "_id" : "", + "_rev" : "", + "com.jolbox.bonecp:type=BoneCP-547b64b7-6765-4915-937b-e940cf74ed82" : { + "connectionWaitTimeAvg" : 0.010752126251079611, + "statementExecuteTimeAvg" : 0.8933237895474139, + "statementPrepareTimeAvg" : 8.45602988656923, + "totalLeasedConnections" : 0, + "totalFreeConnections" : 7, + "totalCreatedConnections" : 7, + "cacheHits" : 0, + "cacheMiss" : 0, + "statementsCached" : 0, + "statementsPrepared" : 27840, + "connectionsRequested" : 19683, + "cumulativeConnectionWaitTime" : 211, + "cumulativeStatementExecutionTime" : 24870, + "cumulativeStatementPrepareTime" : 3292, + "cacheHitRatio" : 0.0, + "statementsExecuted" : 27840 + }, + "com.jolbox.bonecp:type=BoneCP-856008a7-3553-4756-8ae7-0d3e244708fe" : { + "connectionWaitTimeAvg" : 0.015448195945945946, + "statementExecuteTimeAvg" : 0.6599738874458875, + "statementPrepareTimeAvg" : 1.4170901010615866, + "totalLeasedConnections" : 0, + "totalFreeConnections" : 1, + "totalCreatedConnections" : 1, + "cacheHits" : 0, + "cacheMiss" : 0, + "statementsCached" : 0, + "statementsPrepared" : 153, + "connectionsRequested" : 148, + "cumulativeConnectionWaitTime" : 2, + "cumulativeStatementExecutionTime" : 152, + "cumulativeStatementPrepareTime" : 107, + "cacheHitRatio" : 0.0, + "statementsExecuted" : 231 + } +} +---- +The statistics shown relate to the time and connections related to SQL statements. + +[NOTE] +==== +To check the health of a JDBC repository, you need to make two changes to your configuration: + +* Install a JDBC repository, as described in xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. + +* Open the `boot.properties` file in your `project-dir/conf/boot` directory, and enable the statistics MBean for the BoneCP JDBC connection pool: ++ + +[source, console] +---- +openidm.bonecp.statistics.enabled=true +---- + +==== + + +[#health-check-recon] +===== Reconciliation Health Check + +With the following REST call, you can get basic information about the system demands related to reconciliation: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/health/recon" +{ + "_id" : "", + "_rev" : "", + "activeThreads" : 1, + "corePoolSize" : 10, + "largestPoolSize" : 1, + "maximumPoolSize" : 10, + "currentPoolSize" : 1 +} +---- +From the output, you can review the number of active threads used by the reconciliation, as well as the available thread pool. + + + +[#custom-health-scripts] +==== Customizing Health Check Scripts + +You can extend or override the default information that is provided by creating your own script file and its corresponding configuration file in `openidm/conf/info-name.json`. Custom script files can be located anywhere, although a best practice is to place them in `openidm/script/info`. A sample customized script file for extending the default ping service is provided in `openidm/samples/infoservice/script/info/customping.js`. The corresponding configuration file is provided in `openidm/samples/infoservice/conf/info-customping.json`. + +The configuration file has the following syntax: + +[source] +---- +{ + "infocontext" : "ping", + "type" : "text/javascript", + "file" : "script/info/customping.js" +} +---- +The parameters in the configuration file are as follows: + +* `infocontext` specifies the relative name of the info endpoint under the info context. The information can be accessed over REST at this endpoint, for example, setting `infocontext` to `mycontext/myendpoint` would make the information accessible over REST at `\https://localhost:8443/openidm/info/mycontext/myendpoint`. + +* `type` specifies the type of the information source. JavaScript (`"type" : "text/javascript"`) and Groovy (`"type" : "groovy"`) are supported. + +* `file` specifies the path to the JavaScript or Groovy file, if you do not provide a `"source"` parameter. + +* `source` specifies the actual JavaScript or Groovy script, if you have not provided a `"file"` parameter. + +Additional properties can be passed to the script as depicted in this configuration file (`openidm/samples/infoservice/conf/info-name.json`). + +Script files in `openidm/samples/infoservice/script/info/` have access to the following objects: + +* `request` - the request details, including the method called and any parameters passed. + +* `healthinfo` - the current health status of the system. + +* `openidm` - access to the JSON resource API. + +* Any additional properties that are depicted in the configuration file ( `openidm/samples/infoservice/conf/info-name.json`.) + + + +[#health-check-modules] +==== Verifying the State of Health Check Service Modules + +The configurable OpenIDM health check service can verify the status of required modules and services for an operational system. During system startup, OpenIDM checks that these modules and services are available and reports on whether any requirements for an operational system have not been met. If dynamic configuration changes are made, OpenIDM rechecks that the required modules and services are functioning, to allow ongoing monitoring of system operation. + +[#d0e1319] +.Examples of Required Modules +==== +OpenIDM checks all required modules. Examples of those modules are shown here: + +[source, console] +---- +"org.forgerock.openicf.framework.connector-framework" + "org.forgerock.openicf.framework.connector-framework-internal" + "org.forgerock.openicf.framework.connector-framework-osgi" + "org.forgerock.openidm.audit" + "org.forgerock.openidm.core" + "org.forgerock.openidm.enhanced-config" + "org.forgerock.openidm.external-email" + ... + "org.forgerock.openidm.system" + "org.forgerock.openidm.ui" + "org.forgerock.openidm.util" + "org.forgerock.commons.org.forgerock.json.resource" + "org.forgerock.commons.org.forgerock.json.resource.restlet" + "org.forgerock.commons.org.forgerock.restlet" + "org.forgerock.commons.org.forgerock.util" + "org.forgerock.openidm.security-jetty" + "org.forgerock.openidm.jetty-fragment" + "org.forgerock.openidm.quartz-fragment" + "org.ops4j.pax.web.pax-web-extender-whiteboard" + "org.forgerock.openidm.scheduler" + "org.ops4j.pax.web.pax-web-jetty-bundle" + "org.forgerock.openidm.repo-jdbc" + "org.forgerock.openidm.repo-orientdb" + "org.forgerock.openidm.config" + "org.forgerock.openidm.crypto" +---- +==== + +[#d0e1327] +.Examples of Required Services +==== +OpenIDM checks all required services. Examples of those services are shown here: + +[source, console] +---- +"org.forgerock.openidm.config" + "org.forgerock.openidm.provisioner" + "org.forgerock.openidm.provisioner.openicf.connectorinfoprovider" + "org.forgerock.openidm.external.rest" + "org.forgerock.openidm.audit" + "org.forgerock.openidm.policy" + "org.forgerock.openidm.managed" + "org.forgerock.openidm.script" + "org.forgerock.openidm.crypto" + "org.forgerock.openidm.recon" + "org.forgerock.openidm.info" + "org.forgerock.openidm.router" + "org.forgerock.openidm.scheduler" + "org.forgerock.openidm.scope" + "org.forgerock.openidm.taskscanner" +---- +==== +You can replace the list of required modules and services, or add to it, by adding the following lines to your project's `conf/boot/boot.properties` file. Bundles and services are specified as a list of symbolic names, separated by commas: + +* `openidm.healthservice.reqbundles` - overrides the default required bundles. + +* `openidm.healthservice.reqservices` - overrides the default required services. + +* `openidm.healthservice.additionalreqbundles` - specifies required bundles (in addition to the default list). + +* `openidm.healthservice.additionalreqservices` - specifies required services (in addition to the default list). + +By default, OpenIDM gives the system 15 seconds to start up all the required bundles and services, before the system readiness is assessed. Note that this is not the total start time, but the time required to complete the service startup after the framework has started. You can change this default by setting the value of the `servicestartmax` property (in milliseconds) in your project's `conf/boot/boot.properties` file. This example sets the startup time to five seconds: + +[source, console] +---- +openidm.healthservice.servicestartmax=5000 +---- + + + +[#installed-modules] +=== Displaying Information About Installed Modules + +On a running OpenIDM instance, you can list the installed modules and their states by typing the following command in the OSGi console. (The output will vary by configuration): + +[source, console] +---- +-> scr list + + Id State Name +[ 12] [active ] org.forgerock.openidm.endpoint +[ 13] [active ] org.forgerock.openidm.endpoint +[ 14] [active ] org.forgerock.openidm.endpoint +[ 15] [active ] org.forgerock.openidm.endpoint +[ 16] [active ] org.forgerock.openidm.endpoint + ... +[ 34] [active ] org.forgerock.openidm.taskscanner +[ 20] [active ] org.forgerock.openidm.external.rest +[ 6] [active ] org.forgerock.openidm.router +[ 33] [active ] org.forgerock.openidm.scheduler +[ 19] [unsatisfied ] org.forgerock.openidm.external.email +[ 11] [active ] org.forgerock.openidm.sync +[ 25] [active ] org.forgerock.openidm.policy +[ 8] [active ] org.forgerock.openidm.script +[ 10] [active ] org.forgerock.openidm.recon +[ 4] [active ] org.forgerock.openidm.http.contextregistrator +[ 1] [active ] org.forgerock.openidm.config +[ 18] [active ] org.forgerock.openidm.endpointservice +[ 30] [unsatisfied ] org.forgerock.openidm.servletfilter +[ 24] [active ] org.forgerock.openidm.infoservice +[ 21] [active ] org.forgerock.openidm.authentication +-> +---- +To display additional information about a particular module or service, run the following command, substituting the `Id` of that module from the preceding list: + +[source, console] +---- +-> scr info Id +---- +The following example displays additional information about the router service: + +[source, console] +---- +-> scr info 9 +ID: 9 +Name: org.forgerock.openidm.router +Bundle: org.forgerock.openidm.api-servlet (127) +State: active +Default State: enabled +Activation: immediate +Configuration Policy: optional +Activate Method: activate (declared in the descriptor) +Deactivate Method: deactivate (declared in the descriptor) +Modified Method: - +Services: org.forgerock.json.resource.ConnectionFactory + java.io.Closeable + java.lang.AutoCloseable +Service Type: service +Reference: requestHandler + Satisfied: satisfied + Service Name: org.forgerock.json.resource.RequestHandler + Target Filter: (org.forgerock.openidm.router=*) + Multiple: single + Optional: mandatory + Policy: static +... +Properties: + component.id = 9 + component.name = org.forgerock.openidm.router + felix.fileinstall.filename = file:/path/to/openidm-latest/conf/router.json + jsonconfig = { + "filters" : [ + { + "condition" : { + "type" : "text/javascript", + "source" : "context.caller.external === true || context.current.name === 'selfservice'" + }, + "onRequest" : { + "type" : "text/javascript", + "file" : "router-authz.js" + } + }, + { + "pattern" : "^(managed|system|repo/internal)($|(/.+))", + "onRequest" : { + "type" : "text/javascript", + "source" : "require('policyFilter').runFilter()" + }, + "methods" : [ + "create", + "update" + ] + }, + { + "pattern" : "repo/internal/user.*", + "onRequest" : { + "type" : "text/javascript", + "source" : "request.content.password = require('crypto').hash(request.content.password);" + }, + "methods" : [ + "create", + "update" + ] + } + ] +} + maintenanceFilter.target = (service.pid=org.forgerock.openidm.maintenance) + requestHandler.target = (org.forgerock.openidm.router=*) + service.description = OpenIDM Common REST Servlet Connection Factory + service.pid = org.forgerock.openidm.router + service.vendor = ForgeRock AS. +-> +---- + + +[#starting-in-debug-mode] +=== Starting OpenIDM in Debug Mode + +To debug custom libraries, you can start OpenIDM with the option to use the Java Platform Debugger Architecture (JPDA): + +* Start OpenIDM with the `jpda` option: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh jpda +Executing ./startup.sh... +Using OPENIDM_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m -Denvironment=PROD -Djava.compiler=NONE + -Xnoagent -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n +Using LOGGING_CONFIG: + -Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Listening for transport dt_socket at address: 5005 +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> OpenIDM version "4.5.1-20" (revision: xxxx) +OpenIDM ready +---- ++ +The relevant JPDA options are outlined in the startup script (`startup.sh`). + +* In your IDE, attach a Java debugger to the JVM via socket, on port 5005. + + +[CAUTION] +==== +This interface is internal and subject to change. If you depend on this interface, contact ForgeRock support. +==== + + +[#linux-service] +=== Running OpenIDM As a Service on Linux Systems + +OpenIDM provides a script that generates an initialization script to run OpenIDM as a service on Linux systems. You can start the script as the root user, or configure it to start during the boot process. + +When OpenIDM runs as a service, logs are written to the directory in which OpenIDM was installed. + +==== +To run OpenIDM as a service, take the following steps: + +. If you have not yet installed OpenIDM, follow the procedure described in xref:../install-guide/chap-install.adoc#chap-install["Installing OpenIDM Services"] in the __Installation Guide__. + +. Run the RC script: ++ + +[source, console] +---- +$ cd /path/to/openidm/bin +$ ./create-openidm-rc.sh +---- + +. As a user with administrative privileges, copy the `openidm` script to the `/etc/init.d` directory: ++ + +[source, console] +---- +$ sudo cp openidm /etc/init.d/ +---- + +. If you run Linux with SELinux enabled, change the file context of the newly copied script with the following command: ++ + +[source, console] +---- +$ sudo restorecon /etc/init.d/openidm +---- ++ +You can verify the change to SELinux contexts with the `ls -Z /etc/init.d` command. For consistency, change the user context to match other scripts in the same directory with the `sudo chcon -u system_u /etc/init.d/openidm` command. + +. Run the appropriate commands to add OpenIDM to the list of RC services: ++ + +* On Red Hat-based systems, run the following commands: ++ + +[source, console] +---- +$ sudo chkconfig --add openidm +---- ++ + +[source, console] +---- +$ sudo chkconfig openidm on +---- + +* On Debian/Ubuntu systems, run the following command: ++ + +[source, console] +---- +$ sudo update-rc.d openidm defaults +Adding system startup for /etc/init.d/openidm ... +/etc/rc0.d/K20openidm -> ../init.d/openidm +/etc/rc1.d/K20openidm -> ../init.d/openidm +/etc/rc6.d/K20openidm -> ../init.d/openidm +/etc/rc2.d/S20openidm -> ../init.d/openidm +/etc/rc3.d/S20openidm -> ../init.d/openidm +/etc/rc4.d/S20openidm -> ../init.d/openidm +/etc/rc5.d/S20openidm -> ../init.d/openidm +---- ++ +Note the output, as Debian/Ubuntu adds start and kill scripts to appropriate runlevels. ++ +When you run the command, you may get the following warning message: `update-rc.d: warning: /etc/init.d/openidm missing LSB information`. You can safely ignore that message. + + +. As an administrative user, start the OpenIDM service: ++ + +[source, console] +---- +$ sudo /etc/init.d/openidm start +---- ++ +Alternatively, reboot the system to start the OpenIDM service automatically. + +. (Optional) The following commands stops and restarts the service: ++ + +[source, console] +---- +$ sudo /etc/init.d/openidm stop +---- ++ + +[source, console] +---- +$ sudo /etc/init.d/openidm restart +---- + +==== +If you have set up a deployment of OpenIDM in a custom directory, such as `/path/to/openidm/production`, you can modify the `/etc/init.d/openidm` script. + +Open the `openidm` script in a text editor and navigate to the `START_CMD` line. + +At the end of the command, you should see the following line: + +[source, console] +---- +org.forgerock.commons.launcher.Main -c bin/launcher.json > logs/server.out 2>&1 &" +---- +Include the path to the production directory. In this case, you would add `-p production` as shown: + +[source, console] +---- +org.forgerock.commons.launcher.Main -c bin/launcher.json -p production > logs/server.out 2>&1 & +---- +Save the `openidm` script file in the `/etc/init.d` directory. The `sudo /etc/init.d/openidm start` command should now start OpenIDM with the files in your `production` subdirectory. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-synchronization.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-synchronization.adoc new file mode 100644 index 000000000..a5eec2ec6 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-synchronization.adoc @@ -0,0 +1,2866 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-synchronization] +== Synchronizing Data Between Resources + +One of the core services of OpenIDM is synchronizing identity data between different resources. In this chapter, you will learn about the different types of synchronization, and how to configure OpenIDM's flexible synchronization mechanism. + +[#sync-types] +=== Types of Synchronization + +__Synchronization__ happens either when OpenIDM receives a change directly, or when OpenIDM discovers a change on an external resource. An __external resource__ can be any system that holds identity data, such as Active Directory, OpenDJ, a CSV file, a JDBC database, and others. OpenIDM connects to external resources by using OpenICF connectors. For more information, see xref:chap-resource-conf.adoc#chap-resource-conf["Connecting to External Resources"]. + +For direct changes to managed objects, OpenIDM immediately synchronizes those changes to all mappings configured to use those objects as their source. A direct change can originate not only as a write request through the REST interface, but also as an update resulting from reconciliation with another resource. + +* OpenIDM discovers and synchronizes changes from external resources by using __reconciliation__ and __liveSync__. + +* OpenIDM synchronizes changes made to its internal repository with external resources by using __implicit synchronization__. + +-- + +Reconciliation:: ++ +__Reconciliation__ is the process of ensuring that the objects in two different data stores are synchronized. Traditionally, reconciliation applies mainly to user objects, but OpenIDM can reconcile any objects, such as groups, roles, and devices. + ++ +In any reconciliation operation, there is a __source system__ (the system that contains the changes) and a __target system__ (the system to which the changes will be propagated). The source and target system are defined in a __mapping__. OpenIDM can be either the source or the target in a mapping. You can configure multiple mappings for one OpenIDM instance, depending on the external resources to which OpenIDM connects. + ++ +To perform reconciliation, OpenIDM analyzes both the source system __and__ the target system, to discover the differences that it must reconcile. Reconciliation can therefore be a heavyweight process. When working with large data sets, finding all changes can be more work than processing the changes. + ++ +Reconciliation is, however, thorough. It recognizes system error conditions and catches changes that might be missed by liveSync. Reconciliation therefore serves as the basis for compliance and reporting functionality. + +LiveSync:: ++ +__LiveSync__ captures the changes that occur on a remote system, then pushes those changes to OpenIDM. OpenIDM uses the defined mappings to replay the changes where they are required; either in the OpenIDM repository, or on another remote system, or both. Unlike reconciliation, liveSync uses a polling system, and is intended to react quickly to changes as they happen. + ++ +To perform this polling, liveSync relies on a change detection mechanism on the external resource to determine which objects have changed. The change detection mechanism is specific to the external resource, and can be a time stamp, a sequence number, a change vector, or any other method of recording changes that have occurred on the system. For example, OpenDJ implements a change log that provides OpenIDM with a list of objects that have changed since the last request. Active Directory implements a change sequence number, and certain databases might have a `lastChange` attribute. ++ + +[NOTE] +====== +In the case of OpenDJ, the change log (`cn=changelog`) can be read only by `cn=directory manager` by default. If you are configuring liveSync with OpenDJ, the `principle` that is defined in the LDAP connector configuration must have access to the change log. For information about allowing a regular user to read the change log, see link:../../../opendj/3.5/admin-guide/#read-ecl-as-regular-user[To Allow a User to Read the Change Log, window=\_blank] in the __Administration Guide__ for OpenDJ. +====== + +Implicit synchronization:: ++ +__Implicit synchronization__ automatically pushes changes that are made in the OpenIDM internal repository to external systems. + ++ +Note that implicit synchronization only pushes __changes__ out to the external data sources. To synchronize a complete data set, you must start with a reconciliation operation. + +-- +OpenIDM uses mappings, configured in your project's `conf/sync.json` file, to determine which data to synchronize, and how that data must be synchronized. You can schedule reconciliation operations, and the frequency with which OpenIDM polls for liveSync changes, as described in xref:chap-scheduler-conf.adoc#chap-scheduler-conf["Scheduling Tasks and Events"]. + +OpenIDM logs reconciliation and synchronization operations in the audit logs by default. For information about querying the reconciliation and synchronization logs, see xref:chap-auditing.adoc#querying-audit-over-rest["Querying Audit Logs Over REST"]. + + +[#sync-flexible-data] +=== Defining Your Data Mapping Model + +In general, identity management software implements one of the following data models: + +* A meta-directory data model, where all data are mirrored in a central repository. ++ +The meta-directory model offers fast access at the risk of getting outdated data. + +* A virtual data model, where only a minimum set of attributes are stored centrally, and most are loaded on demand from the external resources in which they are stored. ++ +The virtual model guarantees fresh data, but pays for that guarantee in terms of performance. + +OpenIDM leaves the data model choice up to you. You determine the right trade offs for a particular deployment. OpenIDM does not hard code any particular schema or set of attributes stored in the repository. Instead, you define how external system objects map onto managed objects, and OpenIDM dynamically updates the repository to store the managed object attributes that you configure. + +You can, for example, choose to follow the data model defined in the Simple Cloud Identity Management (link:http://www.simplecloud.info/specs/draft-scim-core-schema-00.html[SCIM, window=\_blank]) specification. The following object represents a SCIM user: + +[source, javascript] +---- +{ + "userName": "james1", + "familyName": "Berg", + "givenName": "James", + "email": [ + "james1@example.com" + ], + "description": "Created by OpenIDM REST.", + "password": "asdfkj23", + "displayName": "James Berg", + "phoneNumber": "12345", + "employeeNumber": "12345", + "userType": "Contractor", + "title": "Vice President", + "active": true +} +---- + +[NOTE] +==== +Avoid using the dash character ( `-` ) in property names, like `last-name`, as dashes in names make JavaScript syntax more complex. If you cannot avoid the dash, then write `source['last-name']` instead of `source.last-name` in your JavaScript. +==== + + +[#basic-sync] +=== Configuring Synchronization Between Two Resources + +This section describes the high-level steps required to set up synchronization between two resources. A basic synchronization configuration involves the following steps: + +. Set up the connector configuration. ++ +Connector configurations are defined in `conf/provisioner-*.json` files. One provisioner file must be defined for each external resource to which you are connecting. + +. Map source objects to target objects. ++ +Mappings are defined in the `conf/sync.json` file. There is only one `sync.json` file per OpenIDM instance, but multiple mappings can be defined in that file. + +. Configure any scripts that are required to check source and target objects, and to manipulate attributes. + +. In addition to these configuration elements, OpenIDM stores a `links` table in its repository. The links table maintains a record of relationships established between source and target objects. + + +[#connector-config-files] +==== Setting Up the Connector Configuration + +Connector configuration files map external resource objects to OpenIDM objects, and are described in detail in xref:chap-resource-conf.adoc#chap-resource-conf["Connecting to External Resources"]. Connector configuration files are stored in the `conf/` directory of your project, and are named `provisioner.resource-name.json`, where __resource-name__ reflects the connector technology and the external resource, for example, `openicf-xml`. + +You can create and modify connector configurations through the Admin UI or directly in the configuration files, as described in the following sections. + +[#connector-config-adminui] +===== Setting up and Modifying Connector Configurations in the Admin UI + +The easiest way to set up and modify connector configurations is to use the Admin UI. + +==== +To add or modify a connector configuration in the Admin UI: + +. Log in to the UI (`\https://localhost:8443/admin`) as an administrative user. The default administrative username and password is `openidm-admin` and `openidm-admin`. + +. Select Configure > Connectors. + +. Click on the connector that you want to modify (if there is an existing connector configuration) or click New Connector to set up a new connector configuration. + +==== + + +[#connector-config-files-cli] +===== Editing Connector Configuration Files + +A number of sample provisioner files are provided in `path/to/openidm/samples/provisioners`. To modify connector configuration files directly, edit one of the sample provisioner files that corresponds to the resource to which you are connecting. + +The following excerpt of an example LDAP connector configuration shows the name for the connector and two attributes of an account object type. In the attribute mapping definitions, the attribute name is mapped from the `nativeName` (the attribute name used on the external resource) to the attribute name that is used in OpenIDM. The `sn` attribute in LDAP is mapped to `lastName` in OpenIDM. The `homePhone` attribute is defined as an array, because it can have multiple values: + +[source, javascript] +---- +{ + "name": "MyLDAP", + "objectTypes": { + "account": { + "lastName": { + "type": "string", + "required": true, + "nativeName": "sn", + "nativeType": "string" + }, + "homePhone": { + "type": "array", + "items": { + "type": "string", + "nativeType": "string" + }, + "nativeName": "homePhone", + "nativeType": "string" + } + } + } +} +---- +For OpenIDM to access external resource objects and attributes, the object and its attributes must match the connector configuration. Note that the connector file only maps external resource objects to OpenIDM objects. To construct attributes and to manipulate their values, you use the synchronization mappings file, described in the following section. + + + +[#synchronization-mappings-file] +==== Mapping Source Objects to Target Objects + +A synchronization mapping specifies a relationship between objects and their attributes in two data stores. A typical attribute mapping, between objects in an external LDAP directory and an internal Managed User data store, is: + +[source, console] +---- +"source": "lastName", +"target": "sn" +---- +In this case, the `lastName` source attribute is mapped to the `sn` (surname) attribute on the target. + +The core configuration for OpenIDM synchronization is defined in your project's synchronization mappings file (`conf/sync.json`). The mappings file contains one or more mappings for every resource that must be synchronized. + +Mappings are always defined from a __source__ resource to a __target__ resource. To configure bidirectional synchronization, you must define two mappings. For example, to configure bidirectional synchronization between an LDAP server and a local repository, you would define the following two mappings: + +* LDAP Server > Local Repository + +* Local Repository > LDAP Server + +With bidirectional synchronization, OpenIDM includes a `links` property that enables you to reuse the links established between objects, for both mappings. For more information, see xref:#reusing-links["Reusing Links Between Mappings"]. + +You can update a mapping while the server is running. To avoid inconsistencies between repositories, do not update a mapping while a reconciliation is in progress __for that mapping__. + +[#mapping-resources-text] +===== Specifying the Resource Mapping + +Objects in external resources are specified in a mapping as `system/name/object-type`, where __name__ is the name used in the connector configuration file, and __object-type__ is the object defined in the connector configuration file list of object types. Objects in OpenIDM's internal repository are specified in the mapping as `managed/object-type`, where __object-type__ is defined in your project's managed objects configuration file (`conf/managed.json`). + +External resources, and OpenIDM managed objects, can be the __source__ or the __target__ in a mapping. By convention, the mapping name is a string of the form `source_target`, as shown in the following example: + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "systemLdapAccounts_managedUser", + "source": "system/ldap/account", + "target": "managed/user", + "properties": [ + { + "source": "lastName", + "target": "sn" + }, + { + "source": "telephoneNumber", + "target": "telephoneNumber" + }, + { + "target": "phoneExtension", + "default": "0047" + }, + { + "source": "email", + "target": "mail", + "comment": "Set mail if non-empty.", + "condition": { + "type": "text/javascript", + "source": "(object.email != null)" + } + }, + { + "source": "", + "target": "displayName", + "transform": { + "type": "text/javascript", + "source": "source.lastName +', ' + source.firstName;" + } + }, + { + "source" : "uid", + "target" : "userName", + "condition" : "/linkQualifier eq \"user\"" + } + }, + ] + } + ] +} +---- +In this example, the __name__ of the source is the external resource (`ldap`), and the target is OpenIDM's user repository, specifically `managed/user`. The `properties` defined in the mapping reflect attribute names that are defined in the OpenIDM configuration. For example, the source attribute `uid` is defined in the `ldap` connector configuration file, rather than on the external resource itself. + +You can also configure synchronization mappings in the Admin UI. To do so, navigate to `\https://localhost:8443/admin`, and click Configure > Mappings. The Admin UI serves as a front end to OpenIDM configuration files, so, the changes you make to mappings in the Admin UI are written to your project's `conf/sync.json` file. + + +[#mapping-creating-attributes] +===== Creating Attributes in a Mapping + +You can use a mapping to __create__ attributes on the target resource. In the preceding example, the mapping creates a `phoneExtension` attribute with a default value of `0047` on the target object. + +In other words, the `default` property specifies a value to assign to the attribute on the target object. Before OpenIDM determines the value of the target attribute, it first evaluates any applicable conditions, followed by any transformation scripts. If the `source` property and the `transform` script yield a null value, it then applies the default value, create and update actions. The default value overrides the target value, if one exists. + +To set up attributes with default values in the Admin UI: + +. Select Configure > Mappings, and click on the Mapping you want to edit. + +. Click on the Target Property that you want to create (`phoneExtension` in the previous example), select the Default Values tab, and enter a default value for that property mapping. + + + +[#mapping-transforming-attributes] +===== Transforming Attributes in a Mapping + +Use a mapping to define attribute transformations during synchronization. In the following sample mapping excerpt, the value of the `displayName` attribute on the target is set using a combination of the `lastName` and `firstName` attribute values from the source: + +[source, javascript] +---- +{ + "source": "", + "target": "displayName", + "transform": { + "type": "text/javascript", + "source": "source.lastName +', ' + source.firstName;" + } +}, +---- +For transformations, the `source` property is optional. However, a source object is only available when you specify the `source` property. Therefore, in order to use `source.lastName` and `source.firstName` to calculate the `displayName`, the example specifies `"source" : ""`. + +If you set `"source" : ""` (not specifying an attribute), the entire object is regarded as the source, and you must include the attribute name in the transformation script. For example, to transform the source username to lower case, your script would be `source.mail.toLowerCase();`. If you do specify a source attribute (for example `"source" : "mail"`), just that attribute is regarded as the source. In this case, the transformation script would be `source.toLowerCase();`. +To set up a transformation script in the Admin UI: + +. Select Configure > Mappings, and select the Mapping. + +. Select the line with the target attribute whose value you want to set. + +. On the Transformation Script tab, select `Javascript` or `Groovy`, and enter the transformation as an `Inline Script` or specify the path to the file containing your transformation script. + + + +[#mapping-conditions] +===== Using Scriptable Conditions in a Mapping + +By default, OpenIDM synchronizes all attributes in a mapping. To facilitate more complex relationships between source and target objects, you can define conditions for which OpenIDM maps certain attributes. OpenIDM supports two types of mapping conditions: + +* __Scriptable conditions__, in which an attribute is mapped only if the defined script evaluates to `true` + +* __Condition filters__, a declarative filter that sets the conditions under which the attribute is mapped. Condition filters can include a __link qualifier__, that identifies the __type__ of relationship between the source object and multiple target objects. For more information, see xref:#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"]. ++ +Examples of condition filters include: ++ + +** `"condition": "/object/country eq 'France'"` - only map the attribute if the object's `country` attribute equals `France`. + +** `"condition": "/object/password pr"` - only map the attribute if the object's `password` attribute is present. + +** `"/linkQualifier eq 'admin'"` - only map the attribute if the link between this source and target object is of type `admin`. + + +To set up mapping conditions in the Admin UI, select Configure > Mappings. Click the mapping for which you want to configure conditions. On the Properties tab, click on the attribute that you want to map, then select the Conditional Updates tab. + +Configure the filtered condition on the `Condition Filter` tab, or a scriptable condition on the `Script` tab. + +Scriptable conditions create mapping logic, based on the result of the condition script. If the script does not return `true`, OpenIDM does not manipulate the target attribute during a synchronization operation. + +In the following excerpt, the value of the target `mail` attribute is set to the value of the source `email` attribute __only if__ the source attribute is not empty: + +[source, javascript] +---- +{ + "target": "mail", + "comment": "Set mail if non-empty.", + "source": "email", + "condition": { + "type": "text/javascript", + "source": "(object.email != null)" + } +... +---- +Only the source object is in the condition script's scope, so the `object.email` in this example refers to the `email` property of the source object. + +[TIP] +==== +You can add comments to JSON files. While this example includes a property named `comment`, you can use any unique property name, as long as it is not used elsewhere in the server. OpenIDM ignores unknown property names in JSON configuration files. +==== + + +[#linking-multiple-targets] +===== Mapping a Single Source Object to Multiple Target Objects + +In certain cases, you might have a single object in a resource that maps to more than one object in another resource. For example, assume that managed user, bjensen, has two distinct accounts in an LDAP directory: an `employee` account (under `uid=bjensen,ou=employees,dc=example,dc=com`) and a `customer` account (under `uid=bjensen,ou=customers,dc=example,dc=com`). You want to map both of these LDAP accounts to the same managed user account. + +OpenIDM uses __link qualifiers__ to manage this one-to-many scenario. To map a single source object to multiple target objects, you indicate how the source object should be linked to the target object by defining link qualifiers. A link qualifier is essentially a label that identifies the __type__ of link (or relationship) between each object. + +In the previous example, you would define two link qualifiers that enable you to link both of bjensen's LDAP accounts to her managed user object, as shown in the following diagram: + +image::images/link-qualifier.png[] +Note from this diagram that the link qualifier is a property of the __link__ between the source and target object, and not a property of the source or target object itself. + +Link qualifiers are defined as part of the mapping (in your project's `conf/sync.json` file). Each link qualifier must be unique within the mapping. If no link qualifier is specified (when only one possible matching target object exists), OpenIDM uses a default link qualifier with the value `default`. + +Link qualifiers can be defined as a static list, or dynamically, using a script. The following excerpt from a sample mapping shows the two static link qualifiers, `employee` and `customer`, described in the previous example: + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "managedUser_systemLdapAccounts", + "source": "managed/user", + "target": "system/MyLDAP/account", + "linkQualifiers" : [ "employee", "customer" ], +... +---- +The list of static link qualifiers is evaluated for __every__ source record. That is, every reconciliation processes all synchronization operations, for each link qualifier, in turn. + +A dynamic link qualifier script returns a list of link qualifiers applicable for each source record. For example, suppose you have two __types__ of managed users - employees and contractors. For employees, a single managed user (source) account can correlate with three different LDAP (target) accounts - employee, customer, and manager. For contractors, a single managed user account can correlate with only two separate LDAP accounts - contractor, and customer. The possible linking situations for this scenario are shown in the following diagram: + +image::images/link-qualifier-script.png[] +In this scenario, you could write a script to generate a dynamic list of link qualifiers, based on the managed user type. For employees, the script would return `[employee, customer, manager]` in its list of possible link qualifiers. For contractors, the script would return `[contractor, customer]` in its list of possible link qualifiers. A reconciliation operation would then only process the list of link qualifiers applicable to each source object. + +If your source resource includes a large number of records, you should use a dynamic link qualifier script instead of a static list of link qualifiers. Generating the list of applicable link qualifiers dynamically avoids unnecessary additional processing for those qualifiers that will never apply to specific source records. Synchronization performance is therefore improved for large source data sets. + +You can include a dynamic link qualifier script inline (using the `source` property), or by referencing a JavaScript or Groovy script file (using the `file` property). The following link qualifier script sets up the dynamic link qualifier lists described in the previous example: + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "managedUser_systemLdapAccounts", + "source": "managed/user", + "target": "system/MyLDAP/account", + "linkQualifiers" : { + "type" : "text/javascript", + "globals" : { }, + "source" : "if(source.type === 'employee'){['employee', 'customer', 'manager']} + else { ['contractor', 'customer'] }" + } +... +---- +To reference an external link qualifier script, provide a link to the file in the `file` property: + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "managedUser_systemLdapAccounts", + "source": "managed/user", + "target": "system/MyLDAP/account", + "linkQualifiers" : { + "type" : "text/javascript", + "file" : "script/linkQualifiers.js" + } +... +---- +Dynamic link qualifier scripts must return all valid link qualifiers when the `returnAll` global variable is true. The `returnAll` variable is used during the target reconciliation phase to check whether there are any target records that are unassigned, for each known link qualifier. For a list of the variables available to a dynamic link qualifier script, see xref:appendix-scripting.adoc#script-triggers-sync_json["Script Triggers Defined in sync.json"]. + +On their own, link qualifiers have no functionality. However, they can be referenced by various aspects of reconciliation to manage the situations where a single source object maps to multiple target objects. The following examples show how link qualifiers can be used in reconciliation operations: + +* Use link qualifiers during object creation, to create multiple target objects per source object. ++ +The following excerpt of a sample mapping defines a transformation script that generates the value of the `dn` attribute on an LDAP system. If the link qualifier is `employee`, the value of the target `dn` is set to `"uid=userName,ou=employees,dc=example,dc=com"`. If the link qualifier is `customer`, the value of the target `dn` is set to `"uid=userName,ou=customers,dc=example,dc=com"`. The reconciliation operation iterates through the link qualifiers for each source record. In this case, two LDAP objects, with different `dn`s would created for each managed user object. ++ + +[source, javascript] +---- +{ + "target" : "dn", + "transform" : { + "type" : "text/javascript", + "globals" : { }, + "source" : "if (linkQualifier === 'employee') + { 'uid=' + source.userName + ',ou=employees,dc=example,dc=com'; } + else + if (linkQualifier === 'customer') + { 'uid=' + source.userName + ',ou=customers,dc=example,dc=com'; }" + }, + "source" : "" + } +---- + +* Use link qualifiers in conjunction with a __correlation query__ that assigns a link qualifier based on the values of an existing target object. ++ +During the source synchronization, OpenIDM queries the target system for every source record __and__ link qualifier, to check if there are any matching target records. If a match is found, the sourceId, targetId, and linkQualifier are all saved as the __link__. ++ +The following excerpt of a sample mapping shows the two link qualifiers described previously (`employee` and `customer`). The correlation query first searches the target system for the `employee` link qualifier. If a target object matches the query, based on the value of its `dn` attribute, OpenIDM creates a link between the source object and that target object and assigns the `employee` link qualifier to that link. This process is repeated for all source records. Then, the correlation query searches the target system for the `customer` link qualifier. If a target object matches that query, OpenIDM creates a link between the source object and that target object and assigns the `customer` link qualifier to that link. ++ + +[source, javascript] +---- +"linkQualifiers" : ["employee", "customer"], + "correlationQuery" : [ +   { + "linkQualifier" : "employee", +      "type" : "text/javascript", +      "source" : "var query = {'_queryFilter': 'dn co \"' + uid=source.userName + 'ou=employees\"'}; query;" +    }, +    { + "linkQualifier" : "customer", +      "type" : "text/javascript", +      "source" : "var query = {'_queryFilter': 'dn co \"' + uid=source.userName + 'ou=customers\"'}; query;" +    } +  ] +... +---- ++ +For more information about correlation queries, see xref:#correlation["Correlating Source Objects With Existing Target Objects"]. + +* Use link qualifiers during policy validation to apply different policies based on the link type. ++ +The following excerpt of a sample `sync.json` file shows two link qualifiers, `user` and `test`. Depending on the link qualifier, different actions are taken when the target record is ABSENT: ++ + +[source, javascript] +---- +{ + "mappings" : [ + { + "name" : "systemLdapAccounts_managedUser", + "source" : "system/ldap/account", + "target" : "managed/user", + "linkQualifiers" : [ + "user", + "test" + ], + "properties" : [ + ... + "policies" : [ + { + "situation" : "CONFIRMED", + "action" : "IGNORE" + }, + { + "situation" : "FOUND", + "action" : "UPDATE + } + { + "condition" : "/linkQualifier eq \"user\"", + "situation" : "ABSENT", + "action" : "CREATE", + "postAction" : { + "type" : "text/javascript", + "source" : "java.lang.System.out.println('Created user: \');" + } + }, + { + "condition" : "/linkQualifier eq \"test\"", + "situation" : "ABSENT", + "action" : "IGNORE", + "postAction" : { + "type" : "text/javascript", + "source" : "java.lang.System.out.println('Ignored user: ');" + } + }, + ... +---- ++ +With this sample mapping, the synchronization operation creates an object in the target system only if the potential match is assigned a `user` link qualifier. If the match is assigned a `test` qualifier, no target object is created. In this way, the process avoids creating duplicate __test-related__ accounts in the target system. + + +[TIP] +==== +To set up link qualifiers in the Admin UI select Configure > Mappings. Select a mapping, and click Properties > Link Qualifiers. +==== +For an example that uses link qualifiers in conjunction with roles, see xref:../samples-guide/chap-multiaccount-sample.adoc#chap-multiaccount-sample["The Multi-Account Linking Sample"] in the __Samples Guide__. + + +[#correlation] +===== Correlating Source Objects With Existing Target Objects + +When OpenIDM creates an object on a target system in a synchronization process, it also creates a __link__ between the source and target object. OpenIDM then uses that link to determine the object's __synchronization situation__ during later synchronization operations. For a list of synchronization situations, see xref:#sync-situations["Synchronization Situations"]. + +With every synchronization operation, OpenIDM can __correlate__ existing source and target objects. Correlation matches source and target objects, based on the results of a query or script, and creates links between matched objects. + +Correlation queries and correlation scripts are defined in your project's mapping (`conf/sync.json`) file. Each query or script is specific to the mapping for which it is configured. You can also configure correlation by using the Admin UI. Select Configure > Mappings, and click on the mapping for which you want to correlate. On the Association tab, expand Association Rules, and select Correlation Queries or Correlation Script from the list. + +The following sections describe how to write correlation queries and scripts. + +[#correlation-queries-configuring] +====== Writing Correlation Queries + +OpenIDM processes a correlation query by constructing a query map. The content of the query is generated dynamically, using values from the source object. For each source object, a new query is sent to the target system, using (possibly transformed) values from the source object for its execution. + +Queries are run against __target resources__, either managed or system objects, depending on the mapping. Correlation queries on system objects access the connector, which executes the query on the external resource. + +Correlation queries can be expressed using a query filter (`_queryFilter`), a predefined query (`_queryId`), or a native query expression (`_queryExpression`). For more information on these query types, see xref:chap-data.adoc#queries["Defining and Calling Queries"]. The synchronization process executes the correlation query to search through the target system for objects that match the current source object. + +The preferred syntax for a correlation query is a filtered query, using the `_queryFilter` keyword. Filtered queries should work in the same way on any backend, whereas other query types are generally specific to the backend. Predefined queries (using `_queryId`) and native queries (using `_queryExpression`) can also be used for correlation queries on managed resources. Note that `system` resources do not support native queries or predefined queries other than `query-all-ids` (which serves no purpose in a correlation query). + +To configure a correlation query, define a script whose source returns a query that uses the `_queryFilter`, `_queryId`, or `_queryExpression` keyword. For example: + +* For a `_queryId`, the value is the named query. Named parameters in the query map are expected by that query. ++ + +[source, javascript] +---- +{'_queryId' : 'for-userName', 'uid' : source.name} +---- + +* For a `_queryFilter`, the value is the abstract filter string: ++ + +[source, javascript] +---- +{ "_queryFilter" : "uid eq \"" + source.userName + "\"" } +---- + +* For a `_queryExpression`, the value is the system-specific query expression, such as raw SQL. ++ + +[source] +---- +{'_queryExpression': 'select * from managed_user where givenName = \"' + source.firstname + '\"' } +---- ++ + +[CAUTION] +==== +Using a query expression in this way is not recommended as it exposes your system to SQL injection exploits. +==== + + +[#correlation-filtered-queries] +======= Using Filtered Queries to Correlate Objects + +For filtered queries, the script that is defined or referenced in the `correlationQuery` property must return an object with the following elements: + +* The element that is being compared on the target object, for example, `uid`. ++ +The element on the target object is not necessarily a single attribute. Your query filter can be simple or complex; valid query filters range from a single operator to an entire boolean expression tree. ++ +If the target object is a system object, this attribute must be referred to by its OpenIDM name rather than its OpenICF `nativeName`. For example, given the following provisioner configuration excerpt, the attribute to use in the correlation query would be `uid` and not `__NAME__`: ++ + +[source, javascript] +---- +"uid" : { + "type" : "string", + "nativeName" : "__NAME__", + "required" : true, + "nativeType" : "string" +} +... +---- + +* The value to search for in the query. ++ +This value is generally based on one or more values from the source object. However, it does not have to match the value of a single source object property. You can define how your script uses the values from the source object to find a matching record in the target system. ++ +You might use a transformation of a source object property, such as `toUpperCase()`. You can concatenate that output with other strings or properties. You can also use this value to call an external REST endpoint, and redirect the response to the final "value" portion of the query. + +The following correlation query matches source and target objects if the value of the `uid` attribute on the target is the same as the `userName` attribute on the source: + +[source, javascript] +---- +"correlationQuery" : { + "type" : "text/javascript", + "source" : "var qry = {'_queryFilter': 'uid eq \"' + source.userName + '\"'}; qry" +}, +---- +The query can return zero or more objects. The situation that OpenIDM assigns to the source object depends on the number of target objects that are returned, and on the presence of any __link qualifiers__ in the query. For information about synchronization situations, see xref:#sync-situations["Synchronization Situations"]. For information about link qualifiers, see xref:#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"]. + + +[#correlation-predefined-queries] +======= Using Predefined Queries to Correlate Objects + +For correlation queries on __managed objects__, you can use a query that has been predefined in the database table configuration file for the repository, either `conf/repo.jdbc.json` or `conf/repo.orientdb.json`. You reference the query ID in your project's `conf/sync.json` file. + +The following example shows a query defined in the OrientDB repository configuration (`conf/repo.orientdb.json`) that can be used as the basis for a correlation query: + +[source, javascript] +---- +"for-userName" : "SELECT * FROM ${unquoted:_resource} WHERE userName = ${uid} + SKIP ${unquoted:_pagedResultsOffset} LIMIT ${unquoted:_pageSize}" +---- +By default, a `${value}` token replacement is assumed to be a quoted string. If the value is not a quoted string, use the `unquoted:` prefix, as shown above. + +You would call this query in the mapping (`sync.json`) file as follows: + +[source, javascript] +---- +{ + "correlationQuery": { + "type": "text/javascript", + "source": + "var qry = {'_queryId' : 'for-userName', 'uid' : source.name}; qry;" + } + } +---- +In this correlation query, the `_queryId` property value (`for-userName`) matches the name of the query specified in `conf/repo.orientdb.json`. The `source.name` value replaces `${uid}` in the query. OpenIDM replaces `${unquoted:_resource}` in the query with the name of the table that holds managed objects. + + +[#correlation-expression-builder] +======= Using the Expression Builder to Create Correlation Queries + +OpenIDM provides a declarative correlation option, the expression builder, that makes it easier to configure correlation queries. + +The easiest way to use the expression builder to create a correlation query is through the Admin UI: + +. Select Configure > Mappings and select the mapping for which you want to configure a correlation query. + +. On the Association tab, expand the Association Rules item and select Correlation Queries. + +. Click Add Correlation query. + +. In the Correlation Query window, select a link qualifier. ++ +If you do not need to correlate multiple potential target objects per source object, select the `default` link qualifier. For more information about linking to multiple target objects, see xref:#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"]. + +. Select Expression Builder, and add or remove the fields whose values in the source and target must match. ++ +The following image shows how you can use the expression builder to build a correlation query for a mapping from `managed/user` to `system/ldap/accounts` objects. The query will create a match between the source (managed) object and the target (LDAP) object if the value of the `givenName` or the `telephoneNumber` of those objects is the same. ++ + +image::images/expression-builder.png[] + +. Click Submit to exit the Correlation Query pop-up then click Save. + +The correlation query created in the previous steps displays as follows in the mapping configuration (`sync.json`): + +[source, javascript] +---- +"correlationQuery" : [ + { + "linkQualifier" : "default", + "expressionTree" : { + "any" : [ + "givenName", + "telephoneNumber" + ] + }, + "mapping" : "managedUser_systemLdapAccounts", + "type" : "text/javascript", + "file" : "ui/correlateTreeToQueryFilter.js" + } +] +---- + + + +[#correlation-scripts] +====== Writing Correlation Scripts + +If you need a more powerful correlation mechanism than a simple query can provide, you can write a correlation script with additional logic. Correlation scripts are generally more complex than correlation queries and impose no restrictions on the methods used to find matching objects. A correlation script must execute a query and return the result of that query. + +The result of a correlation script is a list of maps, each of which contains a candidate `_id` value. If no match is found, the script returns a zero-length list. If exactly one match is found, the script returns a single-element list. If there are multiple ambiguous matches, the script returns a list with multiple elements. There is no assumption that the matching target record or records can be found by a simple query on the target system. All of the work necessary to find matching records is left to the script. + +In general, a correlation query should meet the requirements of most deployments. Correlation scripts can be useful, however, if your query needs extra processing, such as fuzzy-logic matching or out-of-band verification with a third-party service over REST. + +The following example shows a correlation script that uses link qualifiers. The script returns `resultData.result` - a list of maps, each of which has an `_id` entry. These entries will be the values that are used for correlation. + +[#d0e14133] +.Correlation Script Using Link Qualifiers +==== + +[source, javascript] +---- +(function () { + var query, resultData; + switch (linkQualifier) { + case "test": + logger.info("linkQualifier = test"); + query = {'_queryFilter': 'uid eq \"' + source.userName + '-test\"'}; + break; + case "user": + logger.info("linkQualifier = user"); + query = {'_queryFilter': 'uid eq \"' + source.userName + '\"'}; + break; + case "default": + logger.info("linkQualifier = default"); + query = {'_queryFilter': 'uid eq \"' + source.userName + '\"'}; + break; + default: + logger.info("No linkQualifier provided."); + break; + } + var resultData = openidm.query("system/ldap/account", query); + logger.info("found " + resultData.result.length + " results for link qualifier " + linkQualifier) + for (i=0;i Mappings and select the mapping for which you want to configure the correlation script. + +. On the Association tab, expand the Association Rules item and select Correlation Script from the list. ++ + +image::images/admin-ui-corr-script.png[] + +. Select a script type (either JavaScript or Groovy) and either enter the script source in the Inline Script box, or specify the path to a file that contains the script. ++ +To create a correlation script, use the details from the source object to find the matching record in the target system. If you are using link qualifiers to match a single source record to multiple target records, you must also use the value of the `linkQualifier` variable within your correlation script to find the target ID that applies for that qualifier. + +. Click Save to save the script as part of the mapping. + + + + + +[#filtering-source-and-target] +==== Filtering Synchronized Objects + +-- +By default, OpenIDM synchronizes all objects that match those defined in the connector configuration for the resource. Many connectors allow you to limit the scope of objects that the connector accesses. For example, the LDAP connector allows you to specify base DNs and LDAP filters so that you do not need to access every entry in the directory. You can also filter the source or target objects that are included in a synchronization operation. To apply these filters, use the `validSource`, `validTarget`, or `sourceCondition` properties in your mapping: + +`validSource`:: +A script that determines if a source object is valid to be mapped. The script yields a boolean value: `true` indicates that the source object is valid; `false` can be used to defer mapping until some condition is met. In the root scope, the source object is provided in the `"source"` property. If the script is not specified, then all source objects are considered valid: ++ + +[source, javascript] +---- +{ + "validSource": { + "type": "text/javascript", + "source": "source.ldapPassword != null" + } +} +---- + +`validTarget`:: +A script used during the second phase of reconciliation that determines if a target object is valid to be mapped. The script yields a boolean value: `true` indicates that the target object is valid; `false` indicates that the target object should not be included in reconciliation. In the root scope, the source object is provided in the `"target"` property. If the script is not specified, then all target objects are considered valid for mapping: ++ + +[source, javascript] +---- +{ + "validTarget": { + "type": "text/javascript", + "source": "target.employeeType == 'internal'" + } +} +---- + +`sourceCondition`:: +The `sourceCondition` element defines an additional filter that must be met for a source object's inclusion in a mapping. + ++ +This condition works like a `validSource` script. Its value can be either a `queryFilter` string, or a script configuration. `sourceCondition` is used principally to specify that a mapping applies only to a particular role or entitlement. + ++ +The following `sourceCondition` restricts synchronization to those user objects whose account status is `active`: ++ + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "managedUser_systemLdapAccounts", + "source": "managed/user", + "sourceCondition": "/source/accountStatus eq \"active\"", + ... + } + ] +} +---- + +-- +During synchronization, your scripts and filters have access to a `source` object and a `target` object. Examples already shown in this section use `source.attributeName` to retrieve attributes from the source objects. Your scripts can also write to target attributes using `target.attributeName` syntax: + +[source, javascript] +---- +{ + "onUpdate": { + "type": "text/javascript", + "source": "if (source.email != null) {target.mail = source.email;}" + } +} +---- +In addition, the `sourceCondition` filter has the `linkQualifier` variable in its scope. + +For more information about scripting, see xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"]. + + +[#preventing-accidental-deletion] +==== Preventing Accidental Deletion of a Target System + +If a source resource is empty, the default behavior is to exit without failure and to log a warning similar to the following: + +[source, console] +---- +2015-06-05 10:41:18:918 WARN Cannot reconcile from an empty data + source, unless allowEmptySourceSet is true. +---- +The reconciliation summary is also logged in the reconciliation audit log. + +This behavior prevents reconciliation operations from accidentally deleting everything in a target resource. In the event that a source system is unavailable but erroneously reports its status as up, the absence of source objects should not result in objects being removed on the target resource. + +When you __do__ want reconciliations of an empty source resource to proceed, override the default behavior by setting the `"allowEmptySourceSet"` property to `true` in the mapping. For example: + +[source] +---- +{ + "mappings" : [ + { + "name" : "systemXmlfileAccounts_managedUser", + "source" : "system/xmlfile/account", + "allowEmptySourceSet" : true, + ... +---- +When an empty source is reconciled, the target is wiped out. + + + +[#constructing-attributes] +=== Constructing and Manipulating Attributes With Scripts + +OpenIDM provides a number of __script hooks__ to construct and manipulate attributes. These scripts can be triggered during various stages of the synchronization process, and are defined as part of the mapping, in the `sync.json` file. + +The scripts can be triggered when a managed or system object is created (`onCreate`), updated (`onUpdate`), or deleted (`onDelete`). Scripts can also be triggered when a link is created (`onLink`) or removed (`onUnlink`). + +In the default synchronization mapping, changes are __always__ written to target objects, not to source objects. However, you can explicitly include a call to an action that should be taken on the source object within the script. + +[NOTE] +==== +The `onUpdate` script is __always__ called for an UPDATE situation, even if the synchronization process determines that there is no difference between the source and target objects, and that the target object will not be updated. + +If, subsequent to the `onUpdate` script running, the synchronization process determines that the target value to set is the same as its existing value, the change is prevented from synchronizing to the target. +==== +The following sample extract of a `sync.json` file derives a DN for an LDAP entry when the entry is created in the internal repository: + +[source, javascript] +---- +{ + "onCreate": { + "type": "text/javascript", + "source": + "target.dn = 'uid=' + source.uid + ',ou=people,dc=example,dc=com'" + } +} +---- + + +[#advanced-dataflow] +=== Advanced Use of Scripts in Mappings + +xref:#constructing-attributes["Constructing and Manipulating Attributes With Scripts"] shows how to manipulate attributes with scripts when objects are created and updated. You might want to trigger scripts in response to other synchronization actions. For example, you might not want OpenIDM to delete a managed user directly when an external account record is deleted, but instead unlink the objects and deactivate the user in another resource. (Alternatively, you might delete the object in OpenIDM but nevertheless execute a script.) The following example shows a more advanced mapping configuration that exposes the script hooks available during synchronization. + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "systemLdapAccount_managedUser", + "source": "system/ldap/account", + "target": "managed/user", + "validSource": { + "type": "text/javascript", + "file": "script/isValid.js" + }, + "correlationQuery" : { + "type" : "text/javascript", + "source" : "var map = {'_queryFilter': 'uid eq \"' + + source.userName + '\"'}; map;" + }, + "properties": [ + { + "source": "uid", + "transform": { + "type": "text/javascript", + "source": "source.toLowerCase()" + }, + "target": "userName" + }, + { + "source": "", + "transform": { + "type": "text/javascript", + "source": "if (source.myGivenName) + {source.myGivenName;} else {source.givenName;}" + }, + "target": "givenName" + }, + { + "source": "", + "transform": { + "type": "text/javascript", + "source": "if (source.mySn) + {source.mySn;} else {source.sn;}" + }, + "target": "familyName" + }, + { + "source": "cn", + "target": "fullname" + }, + { + "comment": "Multi-valued in LDAP, single-valued in AD. + Retrieve first non-empty value.", + "source": "title", + "transform": { + "type": "text/javascript", + "file": "script/getFirstNonEmpty.js" + }, + "target": "title" + }, + { + "condition": { + "type": "text/javascript", + "source": "var clearObj = openidm.decrypt(object); + ((clearObj.password != null) && + (clearObj.ldapPassword != clearObj.password))" + }, + "transform": { + "type": "text/javascript", + "source": "source.password" + }, + "target": "__PASSWORD__" + } + ], + "onCreate": { + "type": "text/javascript", + "source": "target.ldapPassword = null; + target.adPassword = null; + target.password = null; + target.ldapStatus = 'New Account'" + }, + "onUpdate": { + "type": "text/javascript", + "source": "target.ldapStatus = 'OLD'" + }, + "onUnlink": { + "type": "text/javascript", + "file": "script/triggerAdDisable.js" + }, + "policies": [ + { + "situation": "CONFIRMED", + "action": "UPDATE" + }, + { + "situation": "FOUND", + "action": "UPDATE" + }, + { + "situation": "ABSENT", + "action": "CREATE" + }, + { + "situation": "AMBIGUOUS", + "action": "EXCEPTION" + }, + { + "situation": "MISSING", + "action": "EXCEPTION" + }, + { + "situation": "UNQUALIFIED", + "action": "UNLINK" + }, + { + "situation": "UNASSIGNED", + "action": "EXCEPTION" + } + ] + } + ] +} +---- +-- +The following list shows the properties that you can use as hooks in mapping configurations to call scripts: + +Triggered by Situation:: +onCreate, onUpdate, onDelete, onLink, onUnlink + +Object Filter:: +validSource, validTarget + +Correlating Objects:: +correlationQuery + +Triggered on Reconciliation:: +result + +Scripts Inside Properties:: +condition, transform + +-- +Your scripts can get data from any connected system at any time by using the `openidm.read(id)` function, where `id` is the identifier of the object to read. + +The following example reads a managed user object from the repository: + +[source, javascript] +---- +repoUser = openidm.read("managed/user/ddoe"); +---- +The following example reads an account from an external LDAP resource: + +[source, javascript] +---- +externalAccount = openidm.read("system/ldap/account/uid=ddoe,ou=People,dc=example,dc=com"); +---- +Note that the query targets a DN rather than a UID as it did in the previous example. The attribute that is used for the `_id` is defined in the connector configuration file and, in this example, is set to `"uidAttribute" : "dn"`. Although it is possible to use a DN (or any unique attribute) for the `_id`, as a best practice, you should use an attribute that is both unique and immutable. + + +[#reusing-links] +=== Reusing Links Between Mappings + +When two mappings synchronize the same objects bidirectionally, use the `links` property in one mapping to have OpenIDM use the same internally managed link for both mappings. If you do not specify a `links` property, OpenIDM maintains a separate link for each mapping. + +The following excerpt shows two mappings, one from MyLDAP accounts to managed users, and another from managed users to MyLDAP accounts. In the second mapping, the `link` property tells OpenIDM to reuse the links created in the first mapping, rather than create new links: + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "systemMyLDAPAccounts_managedUser", + "source": "system/MyLDAP/account", + "target": "managed/user" + }, + { + "name": "managedUser_systemMyLDAPAccounts", + "source": "managed/user", + "target": "system/MyLDAP/account", + "links": "systemMyLDAPAccounts_managedUser" + } + ] +} +---- + + +[#recon-over-rest] +=== Managing Reconciliation Over REST + +Reconciliation is the synchronization of objects between two data stores. You can trigger, cancel, and monitor reconciliation operations over REST, using the REST endpoint `\http://localhost:8080/openidm/recon`. You can also perform most of these actions through the Admin UI. + +[#triggering-recons] +==== Triggering a Reconciliation Run + +The following example triggers a reconciliation operation based on the `systemLdapAccounts_managedUser` mapping. The mapping is defined in the file `conf/sync.json`: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser" +---- +By default, a reconciliation run ID is returned immediately when the reconciliation operation is initiated. Clients can make subsequent calls to the reconciliation service, using this reconciliation run ID to query its state and to call operations on it. + +The reconciliation run initiated previously would return something similar to the following: + +[source, console] +---- +{"_id":"9f4260b6-553d-492d-aaa5-ae3c63bd90f0-14","state":"ACTIVE"} +---- +To complete the reconciliation operation before the reconciliation run ID is returned, set the `waitForCompletion` property to `true` when the reconciliation is initiated: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser&waitForCompletion=true" +---- + + +[#recon-details] +==== Obtaining the Details of a Reconciliation Run + +Display the details of a specific reconciliation run over REST by including the reconciliation run ID in the URL. The following call shows the details of the reconciliation run initiated in the previous section: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/recon/0890ad62-4738-4a3f-8b8e-f3c83bbf212e" +{ + "ended": "2014-03-06T07:00:32.094Z", + "_id": "7a07c100-4f11-4d7e-bf8e-fa4594f99d58", + "mapping": "systemLdapAccounts_managedUser", + "state": "SUCCESS", + "stage": "COMPLETED_SUCCESS", + "stageDescription": "reconciliation completed.", + "progress": { + "links": { + "created": 0, + "existing": { + "total": "1", + "processed": 1 + } + }, + "target": { + "created": 0, + "existing": { + "total": "3", + "processed": 3 + } + }, + "source": { + "existing": { + "total": "1", + "processed": 1 + } + } + }, + "situationSummary": { + "UNASSIGNED": 2, + "TARGET_IGNORED": 0, + "SOURCE_IGNORED": 0, + "MISSING": 0, + "FOUND": 0, + "AMBIGUOUS": 0, + "UNQUALIFIED": 0, + "CONFIRMED": 1, + "SOURCE_MISSING": 0, + "ABSENT": 0 + }, + "started": "2014-03-06T07:00:31.907Z" +} +---- + + +[#canceling-recons] +==== Canceling a Reconciliation Run + +Cancel a reconciliation run by sending a REST call with the `cancel` action, specifying the reconciliation run ID. The following call cancels the reconciliation run initiated in the previous section: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon/0890ad62-4738-4a3f-8b8e-f3c83bbf212e?_action=cancel" +---- +The output for a reconciliation cancellation request is similar to the following: + +[source, console] +---- +{ + "status":"SUCCESS", + "action":"cancel", + "_id":"0890ad62-4738-4a3f-8b8e-f3c83bbf212e" +} +---- +If the reconciliation run is waiting for completion before its ID is returned, obtain the reconciliation run ID from the list of active reconciliations, as described in the following section. + + +[#listing-recons] +==== Listing Reconciliation Runs + +Display a list of reconciliation processes that have completed, and those that are in progress, by running a RESTful GET on `"https://localhost:8443/openidm/recon"`. The following example displays all reconciliation runs: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/recon" +---- +The output is similar to the following, with one item for each reconciliation run: + +[source, console] +---- +{ + "reconciliations": [ + { + "ended": "2014-03-06T06:14:11.845Z", + "_id": "4286510e-986a-4521-bfa4-8cd1e039a7f5", + "mapping": "systemLdapAccounts_managedUser", + "state": "SUCCESS", + "stage": "COMPLETED_SUCCESS", + "stageDescription": "reconciliation completed.", + "progress": { + "links": { + "created": 1, + "existing": { + "total": "0", + "processed": 0 + } + }, + "target": { + "created": 1, + "existing": { + "total": "2", + "processed": 2 + } + }, + "source": { + "existing": { + "total": "1", + "processed": 1 + } + } + }, + "situationSummary": { + "UNASSIGNED": 2, + "TARGET_IGNORED": 0, + "SOURCE_IGNORED": 0, + "MISSING": 0, + "FOUND": 0, + "AMBIGUOUS": 0, + "UNQUALIFIED": 0, + "CONFIRMED": 0, + "SOURCE_MISSING": 0, + "ABSENT": 1 + }, + "started": "2014-03-06T06:14:04.722Z" + }, + ] +} +---- +-- +Each reconciliation run has the following properties: + +`_id`:: +The ID of the reconciliation run. + +`mapping`:: +The name of the mapping, defined in the `conf/sync.json` file. + +`state`:: +The high level state of the reconciliation run. Values can be as follows: ++ + +* `ACTIVE` ++ +The reconciliation run is in progress. + +* `CANCELED` ++ +The reconciliation run was successfully canceled. + +* `FAILED` ++ +The reconciliation run was terminated because of failure. + +* `SUCCESS` ++ +The reconciliation run completed successfully. + + +`stage`:: +The current stage of the reconciliation run. Values can be as follows: ++ + +* `ACTIVE_INITIALIZED` ++ +The initial stage, when a reconciliation run is first created. + +* `ACTIVE_QUERY_ENTRIES` ++ +Querying the source, target and possibly link sets to reconcile. + +* `ACTIVE_RECONCILING_SOURCE` ++ +Reconciling the set of IDs retrieved from the mapping source. + +* `ACTIVE_RECONCILING_TARGET` ++ +Reconciling any remaining entries from the set of IDs retrieved from the mapping target, that were not matched or processed during the source phase. + +* `ACTIVE_LINK_CLEANUP` ++ +Checking whether any links are now unused and should be cleaned up. + +* `ACTIVE_PROCESSING_RESULTS` ++ +Post-processing of reconciliation results. + +* `ACTIVE_CANCELING` ++ +Attempting to abort a reconciliation run in progress. + +* `COMPLETED_SUCCESS` ++ +Successfully completed processing the reconciliation run. + +* `COMPLETED_CANCELED` ++ +Completed processing because the reconciliation run was aborted. + +* `COMPLETED_FAILED` ++ +Completed processing because of a failure. + + +`stageDescription`:: +A description of the stages described previously. + +`progress`:: +The progress object has the following structure (annotated here with comments): ++ + +[source, javascript] +---- +"progress":{ + "source":{ // Progress on set of existing entries in the mapping source + "existing":{ + "processed":1001, + "total":"1001" // Total number of entries in source set, if known, "?" otherwise + } + }, + "target":{ // Progress on set of existing entries in the mapping target + "existing":{ + "processed":1001, + "total":"1001" // Total number of entries in target set, if known, "?" otherwise + }, + "created":0 // New entries that were created + }, + "links":{ // Progress on set of existing links between source and target + "existing":{ + "processed":1001, + "total":"1001" // Total number of existing links, if known, "?" otherwise + }, + "created":0 // Denotes new links that were created + } +}, +---- + +-- + + +[#livesync-over-rest] +==== Triggering LiveSync Over REST + +Because you can trigger liveSync operations over REST (or by using the resource API) you can use an external scheduler to trigger liveSync operations, rather than using the OpenIDM scheduling mechanism. +There are two ways to trigger liveSync over REST: + +* Use the `_action=liveSync` parameter directly on the resource. This is the recommended method. The following example calls liveSync on the user accounts in an external LDAP system: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/system/ldap/account?_action=liveSync" +---- + +* Target the `system` endpoint and supply a `source` parameter to identify the object that should be synchronized. This method matches the scheduler configuration and can therefore be used to test schedules before they are implemented. ++ +The following example calls the same liveSync operation as the previous example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/system?_action=liveSync&source=system/ldap/account" +---- + +A successful liveSync operation returns the following response: + +[source, console] +---- +{ + "_rev": "4", + "_id": "SYSTEMLDAPACCOUNT", + "connectorData": { + "nativeType": "integer", + "syncToken": 1 + } +} +---- +Do not run two identical liveSync operations simultaneously. Rather ensure that the first operation has completed before a second similar operation is launched. + +To troubleshoot a liveSync operation that has not succeeded, include an optional parameter (`detailedFailure`) to return additional information. For example: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/system/ldap/account?_action=liveSync&detailedFailure=true" +---- + +[NOTE] +==== +The first time liveSync is called, it does not have a synchronization token in the database to establish which changes have already been processed. The default liveSync behavior is to locate the last existing entry in the change log, and to store that entry in the database as the current starting position from which changes should be applied. This behavior prevents liveSync from processing changes that might already have been processed during an initial data load. Subsequent liveSync operations will pick up and process any new changes. + +Typically, in setting up liveSync on a new system, you would load the data initially (by using reconciliation, for example) and then enable liveSync, starting from that base point. +==== + + + +[#recon-by-query] +=== Restricting Reconciliation By Using Queries + +Every reconciliation operation performs a query on the source and on the target resource, to determine which records should be reconciled. The default source and target queries are `query-all-ids`, which means that all records in both the source and the target are considered candidates for that reconciliation operation. + +You can restrict reconciliation to specific entries by defining explicit source or target queries in the mapping configuration. + +To restrict reconciliation to only those records whose `employeeType` on the source resource is `Permanent`, you might specify a source query as follows: + +[source, javascript] +---- +"mappings" : [ + { + "name" : "managedUser_systemLdapAccounts", + "source" : "managed/user", + "target" : "system/ldap/account", + "sourceQuery" : { + "_queryFilter" : "employeeType eq \"Permanent\"" + }, +... +---- +The format of the query can be any query type that is supported by the resource, and can include additional parameters, if applicable. OpenIDM 4.5 supports the following query types. +For queries on managed objects: + +* `_queryId` for arbitrary predefined, parameterized queries + +* `_queryFilter` for arbitrary filters, in common filter notation + +* `_queryExpression` for client-supplied queries, in native query format + +For queries on system objects: + +* `_queryId=query-all-ids` (the only supported predefined query) + +* `_queryFilter` for arbitrary filters, in common filter notation + +The source and target queries send the query to the resource that is defined for that source or target, by default. You can override the resource the query is to sent by specifying a `resourceName` in the query. For example, to query a specific endpoint instead of the source resource, you might modify the preceding source query as follows: + +[source, javascript] +---- +"mappings" : [ + { + "name" : "managedUser_systemLdapAccounts", + "source" : "managed/user", + "target" : "system/ldap/account", + "sourceQuery" : { + "resourceName" : "endpoint/scriptedQuery" + "_queryFilter" : "employeeType eq \"Permanent\"" + }, +... +---- +To override a source or target query that is defined in the mapping, you can specify the query when you call the reconciliation operation. If you wanted to reconcile all employee entries, and not just the permanent employees, you would run the reconciliation operation as follows: + +[source, console] +---- +$ 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 '{"sourceQuery": {"_queryId" : "query-all-ids"}}' \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=managedUser_systemLdapAccounts" +---- +By default, a reconciliation operation runs both the source and target phase. To avoid queries on the target resource, set `runTargetPhase` to `false` in the mapping configuration (`conf/sync.json` file). To prevent the target resource from being queried during the reconciliation operation configured in the previous example, amend the mapping configuration as follows: + +[source] +---- +{ + "mappings" : [ + { + "name" : "systemLdapAccounts_managedUser", + "source" : "system/ldap/account", + "target" : "managed/user", + "sourceQuery" : { + "_queryFilter" : "employeeType eq \"Permanent\"" + }, + "runTargetPhase" : false, + ... +---- +You can also restrict reconciliation by using queries through the Admin UI. Select Configure > Mappings, select a Mapping > Association > Reconciliation Query Filters. You can then specify desired source and target queries. + + +[#recon-by-id] +=== Restricting Reconciliation to a Specific ID + +You can specify an ID to restrict reconciliation to a specific record in much the same way as you restrict reconciliation by using queries. + +To restrict reconciliation to a specific ID, use the `reconById` action, instead of the `recon` action when you call the reconciliation operation. Specify the ID with the `ids` parameter. Reconciling more than one ID with the `reconById` action is not currently supported. + +The following example is based on the data from Sample 2b, which maps an LDAP server with the OpenIDM repository. The example reconciles only the user `bjensen`, using the `managedUser_systemLdapAccounts` mapping to update the user account in LDAP with the data from the OpenIDM repository. The `_id` for `bjensen` in this example is `b3c2f414-e7b3-46aa-8ce6-f4ab1e89288c`. The example assumes that implicit synchronization has been disabled and that a reconciliation operation is required to copy changes made in the repository to the LDAP system: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=reconById&mapping=managedUser_systemLdapAccounts&ids=b3c2f414-e7b3-46aa-8ce6-f4ab1e89288c" +---- +Reconciliation by ID takes the default reconciliation options that are specified in the mapping so the source and target queries, and source and target phases described in the previous section apply equally to reconciliation by ID. + + +[#livesync-retry-strategy] +=== Configuring the LiveSync Retry Policy + +You can specify the results when a liveSync operation reports a failure. Configure the liveSync retry policy to specify the number of times a failed modification should be reattempted and what should happen if the modification is unsuccessful after the specified number of attempts. If no retry policy is configured, OpenIDM reattempts the change an infinite number of times until the change is successful. This behavior can increase data consistency in the case of transient failures (for example, when the connection to the database is temporarily lost). However, in situations where the cause of the failure is permanent (for example, if the change does not meet certain policy requirements) the change will never succeed, regardless of the number of attempts. In this case, the infinite retry behavior can effectively block subsequent liveSync operations from starting. + +Generally, a scheduled reconciliation operation will eventually force consistency. However, to prevent repeated retries that block liveSync, restrict the number of times OpenIDM reattempts the same modification. You can then specify what OpenIDM does with failed liveSync changes. The failed modification can be stored in a __dead letter queue__, discarded, or reapplied. Alternatively, an administrator can be notified of the failure by email or by some other means. This behavior can be scripted. The default configuration in the samples provided with OpenIDM is to retry a failed modification five times, and then to log and ignore the failure. + +The liveSync retry policy is configured in the connector configuration file (`provisioner.openicf-*.json`). The sample connector configuration files have a retry policy defined as follows: + +[source, javascript] +---- +"syncFailureHandler" : { + "maxRetries" : 5, + "postRetryAction" : "logged-ignore" +}, +---- +The `maxRetries` field specifies the number of attempts that OpenIDM should make to process the failed modification. The value of this property must be a positive integer, or `-1`. A value of zero indicates that failed modifications should not be reattempted. In this case, the post-retry action is executed immediately when a liveSync operation fails. A value of `-1` (or omitting the `maxRetries` property, or the entire `syncFailureHandler` from the configuration) indicates that failed modifications should be retried an infinite number of times. In this case, no post retry action is executed. + +The default retry policy relies on the scheduler, or whatever invokes liveSync. Therefore, if retries are enabled and a liveSync modification fails, OpenIDM will retry the modification the next time that liveSync is invoked. + +The `postRetryAction` field indicates what OpenIDM should do if the maximum number of retries has been reached (or if `maxRetries` has been set to zero). The post-retry action can be one of the following: + +* `logged-ignore` indicates that OpenIDM should ignore the failed modification, and log its occurrence. + +* `dead-letter-queue` indicates that OpenIDM should save the details of the failed modification in a table in the repository (accessible over REST at `repo/synchronisation/deadLetterQueue/provisioner-name`). + +* `script` specifies a custom script that should be executed when the maximum number of retries has been reached. For information about using custom scripts in the configuration, see xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"]. ++ +In addition to the regular objects described in xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"], the following objects are available in the script scope: ++ +-- + +`syncFailure`:: +Provides details about the failed record. The structure of the `syncFailure` object is as follows: ++ + +[source, javascript] +---- +"syncFailure" : + { + "token" : the ID of the token, + "systemIdentifier" : a string identifier that matches the "name" property in + provisioner.openicf.json, + "objectType" : the object type being synced, one of the keys in the + "objectTypes" property in provisioner.openicf.json, + "uid" : the UID of the object (for example uid=joe,ou=People,dc=example,dc=com), + "failedRecord", the record that failed to synchronize + }, +---- ++ +To access these fields, include `syncFailure.fieldname` in your script. + +`failureCause`:: +Provides the exception that caused the original liveSync failure. + +`failureHandlers`:: +OpenIDM currently provides two synchronization failure handlers out of the box: ++ + +** `loggedIgnore` indicates that the failure should be logged, after which no further action should be taken. + +** `deadLetterQueue` indicates that the failed record should be written to a specific table in the repository, where further action can be taken. + ++ +To invoke one of the internal failure handlers from your script, use a call similar to the following (shown here for JavaScript): ++ + +[source, console] +---- +failureHandlers.deadLetterQueue.invoke(syncFailure, failureCause); +---- + +-- ++ +Two sample scripts are provided in `path/to/openidm/samples/syncfailure/script`, one that logs failures, and one that sends them to the dead letter queue in the repository. + +The following sample provisioner configuration file extract shows a liveSync retry policy that specifies a maximum of four retries before the failed modification is sent to the dead letter queue: + +[source, javascript] +---- +... +"connectorName" : "org.identityconnectors.ldap.LdapConnector" + }, + + "syncFailureHandler" : { + "maxRetries" : 4, + "postRetryAction" : dead-letter-queue + }, + "poolConfigOption" : { +... +---- +In the case of a failed modification, a message similar to the following is output to the log file: + +[source, console] +---- +INFO: sync retries = 1/4, retrying +---- +OpenIDM reattempts the modification the specified number of times. If the modification is still unsuccessful, a message similar to the following is logged: + +[source, console] +---- +INFO: sync retries = 4/4, retries exhausted +Jul 19, 2013 11:59:30 AM + org.forgerock.openidm.provisioner.openicf.syncfailure.DeadLetterQueueHandler invoke +INFO: uid=jdoe,ou=people,dc=example,dc=com saved to dead letter queue +---- +The log message indicates the entry for which the modification failed (`uid=jdoe`, in this example). + +You can view the failed modification in the dead letter queue, over the REST interface, as follows: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/repo/synchronisation/deadLetterQueue/ldap?_queryId=query-all-ids" +{ + "query-time-ms": 2, + "result": + [ + { + "_id": "4", + "_rev": "0" + } + ], + "conversion-time-ms": 0 +} +---- +To view the details of a specific failed modification, include its ID in the URL: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/repo/synchronisation/deadLetterQueue/ldap/4" +{ + "objectType": "account", + "systemIdentifier": "ldap", + "failureCause": "org.forgerock.openidm.sync.SynchronizationException: + org.forgerock.openidm.objset.ConflictException: + org.forgerock.openidm.sync.SynchronizationException: + org.forgerock.openidm.script.ScriptException: + ReferenceError: \"bad\" is not defined. + (PropertyMapping/mappings/0/properties/3/condition#1)", + "token": 4, + "failedRecord": "complete record, in xml format" + "uid": "uid=jdoe,ou=people,dc=example,dc=com", + "_rev": "0", + "_id": "4" +} +---- + + +[#disabling-automatic-sync] +=== Disabling Automatic Synchronization Operations + +By default, all mappings are automatically synchronized. A change to a managed object is automatically synchronized to all resources for which the managed object is configured as a source. Similarly, if liveSync is enabled for a system, changes to an object on that system are automatically propagated to the managed object repository. + +To prevent automatic synchronization for a specific mapping, set the `enableSync` property of that mapping to false. In the following example, implicit synchronization is disabled. This means that changes to objects in the internal repository are not automatically propagated to the LDAP directory. To propagate changes to the LDAP directory, reconciliation must be launched manually: + +[source, javascript] +---- +{ + "mappings" : [ + { + "name" : "managedUser_systemLdapAccounts", + "source" : "managed/user", + "target" : "system/ldap/account", + "enableSync" : false, + .... +} +---- +If `enableSync` is set to `false` for a system to managed user mapping (for example `"systemLdapAccounts_managedUser"`), liveSync is disabled for that mapping. + + +[#sync-failure-compensation] +=== Configuring Synchronization Failure Compensation + +When implicit synchronization is used to push a large number of changes from the managed object repository to several external repositories, the process can take some time. Problems such as lost connections might happen, resulting in the changes being only partially synchronized. + +For example, if a Human Resources manager adds a group of new employees in one database, a partial synchronization might mean that some of those employees do not have access to their email or other systems. + +You can configure implicit synchronization to revert a reconciliation operation if it is not completely successful. This is known as __failure compensation__. An example of such a configuration is shown in xref:../samples-guide/chap-ldap-samples.adoc#more-sample-5b["Sample 5b - Failure Compensation With Multiple Resources"] in the __Samples Guide__. That sample demonstrates how OpenIDM compensates when synchronization to an external resource fails. + +Failure compensation works by using the optional `onSync` hook, which can be specified in the `conf/managed.json` file. The `onSync` hook can be used to provide failure compensation as follows: + +[source, javascript] +---- +... +"onDelete" : { + "type" : "text/javascript", + "file" : "ui/onDelete-user-cleanup.js" + }, +"onSync" : { + "type" : "text/javascript", + "file" : "compensate.js" + }, +"properties" : [ + ... +---- +The `onSync` hook references a script (`compensate.js`), that is located in the `/path/to/openidm/bin/defaults/script` directory. + +When a managed object is changed, an implicit synchronization operation attempts to synchronize the change (and any other pending changes) with any external data store(s) for which a mapping is configured. Note that implicit synchronization is enabled by default. To disable implicit synchronization, see xref:#disabling-automatic-sync["Disabling Automatic Synchronization Operations"]. + +The implicit synchronization process proceeds with each mapping, in the order in which the mappings are specified in `sync.json`. + +The `compensate.js` script is designed to avoid partial synchronization. If synchronization is successful for all configured mappings, OpenIDM exits from the script. + +If an implicit synchronization operation fails for a particular resource, the `onSync` hook invokes the `compensate.js` script. This script attempts to revert the original change by performing another update to the managed object. This change, in turn, triggers another implicit synchronization operation to all external resources for which mappings are configured. + +If the synchronization operation fails again, the `compensate.js` script is triggered a second time. This time, however, the script recognizes that the change was originally called as a result of a compensation and aborts. OpenIDM logs warning messages related to the sync action (`notifyCreate, notifyUpdate, notifyDelete`), along with the error that caused the sync failure. + +If failure compensation is not configured, any issues with connections to an external resource can result in out of sync data stores, as discussed in the earlier Human Resources example. + +With the `compensate.js` script, any such errors will result in each data store using the information it had before implicit synchronization started. OpenIDM stores that information, temporarily, in the `oldObject` variable. + +In the previous Human Resources example, managers should see that new employees are not shown in their database. Then, the OpenIDM administrators can check log files for errors, address them, and restart implicit synchronization with a new REST call. + + +[#handling-sync] +=== Synchronization Situations and Actions + +During synchronization, OpenIDM categorizes objects according to their __situation__. Situations are characterized according to the following criteria: + +* Does the object exist on a source or target system? + +* Has OpenIDM registered a link between the source object and the target object? + +* Is the object considered __valid__, as assessed by the `validSource` and `validTarget` scripts? + +OpenIDM then takes a specific action, depending on the situation. + +You can define actions for particular situations in the `policies` section of a synchronization mapping, as shown in the following excerpt from the `sync.json` file of Sample 2b: + +[source, javascript] +---- +{ + "policies": [ + { + "situation": "CONFIRMED", + "action": "UPDATE" + }, + { + "situation": "FOUND", + "action": "LINK" + }, + { + "situation": "ABSENT", + "action": "CREATE" + }, + { + "situation": "AMBIGUOUS", + "action": "IGNORE" + }, + { + "situation": "MISSING", + "action": "IGNORE" + }, + { + "situation": "SOURCE_MISSING", + "action": "DELETE" + { + "situation": "UNQUALIFIED", + "action": "IGNORE" + }, + { + "situation": "UNASSIGNED", + "action": "IGNORE" + } + ] +} +---- +If you do not define a policy for a particular situation, OpenIDM takes the __default action__ for the situation. The default actions for each situation are listed in xref:#sync-situations["Synchronization Situations"]. + +The following sections describe the possible situations and their default corresponding actions. You can also view these situations and actions in the Admin UI by selecting Configure > Mappings. Click on a Mapping, then update the Policies on the Behaviors tab. + +[#sync-situations] +==== Synchronization Situations + +OpenIDM performs reconciliation in two phases: + +. __Source reconciliation__, where OpenIDM accounts for source objects and associated links based on the configured mapping. + +. __Target reconciliation__, where OpenIDM iterates over the target objects that were not processed in the first phase. + +During source reconciliation, OpenIDM builds three lists, assigning values to the objects to reconcile: + +. All valid objects from the source. ++ +OpenIDM assigns valid source objects `qualifies=1`. Invalid objects, including those that were not found in the source system and those that were filtered out by the script specified in the `validSource` property, are assigned `qualifies=0`. + +. All records from the appropriate links table. ++ +Objects that have a corresponding link in the links table of the repository are assigned `link=1`. Objects that do not have a corresponding link are assigned `link=0`. + +. All valid objects on the target system. ++ +Objects that are found in the target system are assigned `target=1`. Objects that are not found in the target system are assigned `target=0`. + +Based on the values assigned to objects during source reconciliation, OpenIDM assigns situations, listed here with default and appropriate alternative actions: +-- + +Situations detected during reconciliation and change events::: +[open] +==== + +`CONFIRMED` (qualifies=1, link=1, target=1):: +The source object qualifies for a target object, and is linked to an existing target object. + ++ +Default action: `UPDATE` the target object. + ++ +Other valid actions: `IGNORE, REPORT, NOREPORT, ASYNC` + +`FOUND` (qualifies=1, link=0, target=1):: +The source object qualifies for a target object and is not linked to an existing target object. There is a single target object that correlates with this source object, according to the logic in the correlation. + ++ +Default action: `UPDATE` the target object. + ++ +Other valid actions: `EXCEPTION, IGNORE, REPORT, NOREPORT, ASYNC` + +`FOUND_ALREADY_LINKED` (qualifies=1, link=1, target=1):: +The source object qualifies for a target object and is not linked to an existing target object. There is a single target object that correlates with this source object, according to the logic in the correlation, but that target object is already linked to a different source object. + ++ +Default action: throw an `EXCEPTION`. + ++ +Other valid actions: `IGNORE, REPORT, NOREPORT, ASYNC` + +`ABSENT` (qualifies=1, link=0, target=0):: +The source object qualifies for a target object, is not linked to an existing target object, and no correlated target object is found. + ++ +Default action: `CREATE` a target object. + ++ +Other valid actions: `EXCEPTION, IGNORE, REPORT, NOREPORT, ASYNC` + +`UNQUALIFIED` (qualifies=0, link=0 or 1, target=1 or >1):: +The source object is unqualified (by the "validSource" script). One or more target objects are found through the correlation logic. + ++ +Default action: `DELETE` the target object or objects. + ++ +Other valid actions: `EXCEPTION, IGNORE, REPORT, NOREPORT, ASYNC` + +==== + +Situations detected during reconciliation and source object changes::: +[open] +==== + +`AMBIGUOUS` (qualifies=1, link=0, target>1):: +The source object qualifies for a target object, is not linked to an existing target object, but there is more than one correlated target object (that is, more than one possible match on the target system). + ++ +Default action: throw an `EXCEPTION`. + ++ +Other valid actions: `IGNORE, REPORT, NOREPORT, ASYNC` + +`MISSING` (qualifies=1, link=1, target=0):: +The source object qualifies for a target object, and is linked to a target object, but the target object is missing. + ++ +Default action: throw an `EXCEPTION`. + ++ +Other valid actions: `CREATE, UNLINK, IGNORE, REPORT, NOREPORT, ASYNC` ++ + +[NOTE] +======== +When a target object is deleted, the link from the target to the corresponding source object is not deleted automatically. This lets OpenIDM detect and report items that might have been removed without permission or might need review. If you need to remove the corresponding link when a target object is deleted, define a back-mapping so that OpenIDM can identify the deleted object as a source object, and remove the link. +======== + +`SOURCE_IGNORED` (qualifies=0, link=0, target=0):: +The source object is unqualified (by the `validSource` script), no link is found, and no correlated target exists. + ++ +Default action: `IGNORE` the source object. + ++ +Other valid actions: `EXCEPTION, REPORT, NOREPORT, ASYNC` + +==== + +Situations detected only during source object changes::: +[open] +==== + +`TARGET_IGNORED` (qualifies=0, link=0 or 1, target=1):: +The source object is unqualified (by the `validSource` script). One or more target objects are found through the correlation logic. + ++ +This situation differs from the `UNQUALIFIED` situation, based on the status of the link and the target. If there is a link, the target is not valid. If there is no link and exactly one target, that target is not valid. + ++ +Default action: `IGNORE` the target object until the next full reconciliation operation. + ++ +Other valid actions: `DELETE, UNLINK, EXCEPTION, REPORT, NOREPORT, ASYNC` + +`LINK_ONLY` (qualifies=n/a, link=1, target=0):: +The source may or may not be qualified. A link is found, but no target object is found. + ++ +Default action: throw an `EXCEPTION`. + ++ +Other valid actions: `UNLINK, IGNORE, REPORT, NOREPORT, ASYNC` + +`ALL_GONE` (qualifies=n/a, link=0, cannot-correlate):: +The source object has been removed. No link is found. Correlation is not possible, for one of the following reasons: ++ + +* No previous source value can be found. + +* There is no correlation logic used. + +* A previous value was found, and correlation logic exists, but no corresponding target was found. + ++ +Default action: `IGNORE` the source object. + ++ +Other valid actions: `EXCEPTION, REPORT, NOREPORT, ASYNC` + +==== + +-- +During target reconciliation, OpenIDM assigns the following values as it iterates through the target objects that were not accounted for during the source reconciliation: + +. Valid objects from the target. ++ +OpenIDM assigns valid target objects `qualifies=1`. Invalid objects, including those that are filtered out by the script specified in the `validTarget` property, are assigned `qualifies=0`. + +. All records from the appropriate links table. ++ +Objects that have a corresponding link in the links table of the repository are assigned `link=1`. Objects that do not have a corresponding link are assigned `link=0`. + +. All valid objects on the source system. ++ +Objects that are found in the source system are assigned `source=1`. Objects that are not found in the source system are assigned `source=0`. + +Based on the values that are assigned to objects during the target reconciliation phase, OpenIDM assigns situations, listed here with their default actions: +-- + +Situations detected only during reconciliation::: +[open] +==== + +`TARGET_IGNORED` (qualifies=0):: +During target reconciliation, the target becomes unqualified by the `validTarget` script. + ++ +Default action: `IGNORE` the target object. + ++ +Other valid actions: `DELETE, UNLINK, REPORT, NOREPORT, ASYNC` + +`UNASSIGNED` (qualifies=1, link=0):: +A valid target object exists but does not have a link. + ++ +Default action: throw an `EXCEPTION`. + ++ +Other valid actions: `IGNORE, REPORT, NOREPORT, ASYNC` + +`CONFIRMED` (qualifies=1, link=1, source=1):: +The target object qualifies, and a link to a source object exists. + ++ +Default action: `UPDATE` the target object. + ++ +Other valid actions: `IGNORE, REPORT, NOREPORT` + +==== + +Situations detected during reconciliation and change events::: +[open] +==== + +`UNQUALIFIED` (qualifies=0, link=1, source=1, but source does not qualify):: +The target object is unqualified (by the `validTarget` script). There is a link to an existing source object, which is also unqualified. + ++ +Default action: `DELETE` the target object. + ++ +Other valid actions: `UNLINK, EXCEPTION, IGNORE, REPORT, NOREPORT, ASYNC` + +`SOURCE_MISSING` (qualifies=1, link=1, source=0):: +The target object qualifies and a link is found, but the source object is missing. + ++ +Default action: throw an `EXCEPTION`. + ++ +Other valid actions: `DELETE, UNLINK, IGNORE, REPORT, NOREPORT, ASYNC` + +==== + +-- +The following sections walk you through how OpenIDM assigns situations during source and target reconciliation. + + +[#source-reconciliation] +==== Source Reconciliation + +OpenIDM starts reconciliation and liveSync by reading a list of objects from the resource. For reconciliation, the list includes all objects that are available through the connector. For liveSync, the list contains only changed objects. OpenIDM can filter objects from the list by using the script specified in the `validSource` property, or the query specified in the `sourceCondition` property. + +OpenIDM then iterates the list, checking each entry against the `validSource` and `sourceCondition` filters, and classifying objects according to their situations as described in xref:#sync-situations["Synchronization Situations"]. OpenIDM uses the list of links for the current mapping to classify objects. Finally, OpenIDM executes the action that is configured for each situation. + +The following table shows how OpenIDM assigns the appropriate situation during source reconciliation, depending on whether a valid source exists (Source Qualifies), whether a link exists in the repository (Link Exists), and the number of target objects found, based either on links or on the results of the correlation. + +[#d0e15755] +.Resolving Source Reconciliation Situations +[cols="9%,9%,9%,9%,9%,9%,9%,37%"] +|=== +2+|Source Qualifies? 2+|Link Exists? 3+|Target Objects Found .2+|Situation +|Yes +|No +|Yes +|No +|0 +|1 +|> 1 + +a| +a|X +a| +a|X +a| +a|X +a| +a|SOURCE_MISSING + +a| +a|X +a| +a|X +a| +a| +a|X +a|UNQUALIFIED + +a| +a|X +a|X +a| +a|X +a| +a| +a|UNQUALIFIED + +a| +a|X +a|X +a| +a| +a|X +a| +a|TARGET_IGNORED + +a| +a|X +a|X +a| +a| +a| +a|X +a|UNQUALIFIED + +a|X +a| +a| +a|X +a|X +a| +a| +a|ABSENT + +a|X +a| +a| +a|X +a| +a|X +a| +a|FOUND + +a|X +a| +a| +a|X +a| +a|X +a| +a|FOUND_ALREADY_LINKED + +a|X +a| +a| +a|X +a| +a| +a|X +a|AMBIGUOUS + +a|X +a| +a|X +a| +a|X +a| +a| +a|MISSING + +a|X +a| +a|X +a| +a| +a|X +a| +a|CONFIRMED +|=== + + +[#target-reconciliation] +==== Target Reconciliation + +During source reconciliation, OpenIDM cannot detect situations where no source object exists, such as the `UNASSIGNED` situation. When no source object exists, OpenIDM detects the situation during the second reconciliation phase, target reconciliation. During target reconciliation, OpenIDM iterates all target objects that do not have a representation on the source, checking each object against the `validTarget` filter, determining the appropriate situation and executing the action configured for the situation. + +The following table shows how OpenIDM assigns the appropriate situation during target reconciliation, depending on whether a valid target exists (Target Qualifies), whether a link with an appropriate type exists in the repository (Link Exists), whether a source object exists (Source Exists), and whether the source object qualifies (Source Qualifies). Not all situations assigned during source reconciliation are assigned during target reconciliation. + +[#d0e16002] +.Resolving Target Reconciliation Situations +[cols="8%,8%,9%,8%,8%,9%,8%,8%,34%"] +|=== +2+|Target Qualifies? 2+|Link Exists? 2+|Source Exists? 2+|Source Qualifies? .2+|Situation +|Yes +|No +|Yes +|No +|Yes +|No +|Yes +|No + +a| +a|X +a| +a| +a| +a| +a| +a| +a|TARGET_IGNORED + +a|X +a| +a| +a|X +a| +a|X +a| +a| +a|UNASSIGNED + +a|X +a| +a|X +a| +a|X +a| +a|X +a| +a|CONFIRMED + +a|X +a| +a|X +a| +a|X +a| +a| +a|X +a|UNQUALIFIED + +a|X +a| +a|X +a| +a| +a|X +a| +a| +a|SOURCE_MISSING +|=== + + +[#autosync-and-livesync] +==== Situations Specific to Implicit Synchronization and LiveSync + +Certain situations occur only during implicit synchronization (when OpenIDM pushes changes made in the repository out to external systems) and liveSync (when OpenIDM polls external system change logs for changes and updates the repository). + +The following table shows the situations that pertain only to implicit sync and liveSync, when records are __deleted__ from the source or target resource. + +[#d0e16152] +.Resolving Implicit Sync and LiveSync Delete Situations +[cols="9%,9%,9%,9%,9%,9%,9%,37%"] +|=== +2+|Source Qualifies? 2+|Link Exists? 3+|Target Objects Found .2+|Situation +|Yes +|No +|Yes +|No +|0 +|1 +|> 1 + +a|N/A +a|N/A +a|X +a| +a|X +a| +a| +a|LINK_ONLY + +a|N/A +a|N/A +a| +a|X +a|X +a| +a| +a|ALL_GONE + +a|X +a| +a| +a|X +a| +a| +a|X +a|AMBIGUOUS + +a| +a|X +a| +a|X +a| +a| +a|X +a|UNQUALIFIED +|=== + + +[#sync-actions] +==== Synchronization Actions + +-- +When a situation has been assigned to an object, OpenIDM takes the actions configured in the mapping. If no action is configured, OpenIDM takes the default action for the situation. OpenIDM supports the following actions: + +`CREATE`:: +Create and link a target object. + +`UPDATE`:: +Link and update a target object. + +`DELETE`:: +Delete and unlink the target object. + +`LINK`:: +Link the correlated target object. + +`UNLINK`:: +Unlink the linked target object. + +`EXCEPTION`:: +Flag the link situation as an exception. + ++ +Do not use this action for liveSync mappings. + +`IGNORE`:: +Do not change the link or target object state. + +`REPORT`:: +Do not perform any action but report what would happen if the default action were performed. + +`NOREPORT`:: +Do not perform any action or generate any report. + +`ASYNC`:: +An asynchronous process has been started so do not perform any action or generate any report. + +-- + + +[#script-actions] +==== Launching a Script As an Action + +In addition to the static synchronization actions described in the previous section, you can provide a script that is run in specific synchronization situations. The script can be either JavaScript or Groovy, and can be provided inline (with the `"source"` property), or referenced from a file, (with the `"file"` property). + +The following excerpt of a sample `sync.json` file specifies that an inline script should be invoked when a synchronization operation assesses an entry as `ABSENT` in the target system. The script checks whether the `employeeType` property of the corresponding source entry is `contractor`. If so, the entry is ignored. Otherwise, the entry is created on the target system: + +[source, javascript] +---- +{ + "situation" : "ABSENT", + "action" : { + "type" : "text/javascript", + "globals" : { }, + "source" : "if (source.employeeType === "contractor") {action='IGNORE'} + else {action='CREATE'};action;" + }, +} +---- +The variables available to a script that is called as an action are `source`, `target`, `linkQualifier`, and `recon` (where `recon.actionParam` contains information about the current reconciliation operation). For more information about the variables available to scripts, see xref:appendix-scripting.adoc#script-variables["Variables Available to Scripts"]. + +The result obtained from evaluating this script must be a string whose value is one of the synchronization actions listed in xref:#sync-actions["Synchronization Actions"]. This resulting action will be shown in the reconciliation log. + +To launch a script as a synchronization action in the Admin UI: + +. Select Configure > Mappings. + +. Select the mapping that you want to change. + +. On the Behaviors tab, click the pencil icon next to the situation whose action you want to change. + +. On the Perform this Action tab, click Script, then enter the script that corresponds to the action. + + + +[#workflow-actions] +==== Launching a Workflow As an Action + +OpenIDM provides a default script (`triggerWorkflowFromSync.js`) that launches a predefined workflow when a synchronization operation assesses a particular situation. The mechanism for triggering this script is the same as for any other script. The script is provided in the `openidm/bin/defaults/script/workflow` directory. If you customize the script, copy it to the `script` directory of your project to ensure that your customizations are preserved during an upgrade. + +The parameters for the workflow are passed as properties of the `action` parameter. + +The following extract of a sample `sync.json` file specifies that, when a synchronization operation assesses an entry as `ABSENT`, the workflow named `managedUserApproval` is invoked: + +[source, javascript] +---- +{ + "situation" : "ABSENT", + "action" : { + "workflowName" : "managedUserApproval", + "type" : "text/javascript", + "file" : "workflow/triggerWorkflowFromSync.js" + } +} +---- +To launch a workflow as a synchronization action in the Admin UI: + +. Select Configure > Mappings. + +. Select the mapping that you want to change. + +. On the Behaviors tab, click the pencil icon next to the situation whose action you want to change. + +. On the Perform this Action tab, click Workflow, then enter the details of the workflow you want to launch. + + + + +[#asynchronous-reconciliation] +=== Asynchronous Reconciliation + +Reconciliation can work in tandem with workflows to provide additional business logic to the reconciliation process. You can define scripts to determine the action that should be taken for a particular reconciliation situation. A reconciliation process can launch a workflow after it has assessed a situation, and then perform the reconciliation or some other action. + +For example, you might want a reconciliation process to assess new user accounts that need to be created on a target resource. However, new user account creation might require some kind of approval from a manager before the accounts are actually created. The initial reconciliation process can assess the accounts that need to be created, launch a workflow to request management approval for those accounts, and then relaunch the reconciliation process to create the accounts, after the management approval has been received. + +In this scenario, the defined script returns `IGNORE` for new accounts and the reconciliation engine does not continue processing the given object. The script then initiates an asynchronous process which calls back and completes the reconciliation process at a later stage. + +A sample configuration for this scenario is available in `openidm/samples/sample9`, and described in xref:../samples-guide/chap-xml-samples.adoc#more-sample-9["Workflow Sample - Demonstrating Asynchronous Reconciling Using a Workflow"] in the __Samples Guide__. +Configuring asynchronous reconciliation using a workflow involves the following steps: + +. Create the workflow definition file (`.xml or .bar` file) and place it in the `openidm/workflow` directory. For more information about creating workflows, see xref:chap-workflow.adoc#chap-workflow["Integrating Business Processes and Workflows"]. + +. Modify the `conf/sync.json` file for the situation or situations that should call the workflow. Reference the workflow name in the configuration for that situation. ++ +For example, the following `sync.json` extract calls the `managedUserApproval` workflow if the situation is assessed as `ABSENT`: ++ + +[source, javascript] +---- +{ + "situation" : "ABSENT", + "action" : { + "workflowName" : "managedUserApproval", + "type" : "text/javascript", + "file" : "workflow/triggerWorkflowFromSync.js" + } +}, +---- + +. In the sample configuration, the workflow calls a second, explicit reconciliation process as a final step. This reconciliation process is called on the `sync` context path, with the `performAction` action (`openidm.action('sync', 'performAction', params)`). + +You can also use this kind of explicit reconciliation to perform a specific action on a source or target record, regardless of the assessed situation. + +You can call such an operation over the REST interface, specifying the source, and/or target IDs, the mapping, and the action to be taken. The action can be any one of the supported reconciliation actions: `CREATE, UPDATE, DELETE, LINK, UNLINK, EXCEPTION, REPORT, NOREPORT, ASYNC, IGNORE`. + +The following sample command calls the DELETE action on user `bjensen`, whose `_id` in the LDAP directory is `uid=bjensen,ou=People,dc=example,dc=com`. The user is deleted in the target resource, in this case, the OpenIDM repository. + +Note that the `_id` must be URL-encoded in the REST call: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/sync?_action=performAction&sourceId=uid%3Dbjensen%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom&mapping= + systemLdapAccounts_ManagedUser&action=DELETE" +{} +---- +The following example creates a link between a managed object and its corresponding system object. Such a call is useful in the context of manual data association, when correlation logic has linked an incorrect object, or when OpenIDM has been unable to determine the correct target object. + +In this example, there are two separate target accounts (`scarter.user` and `scarter.admin`) that should be mapped to the managed object. This call creates a link to the `user` account and specifies a link qualifier that indicates the type of link that will be created: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/sync?_action=performAction&action=LINK + &sourceId=4b39f74d-92c1-4346-9322-d86cb2d828a8&targetId=scarter.user + &mapping=managedUser_systemXmlfileAccounts&linkQualifier=user" +{} +---- +For more information about linking to multiple accounts, see xref:#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"]. + + +[#case-sensitivity] +=== Configuring Case Sensitivity For Data Stores + +By default, OpenIDM is case-sensitive, which means that case is taken into account when comparing IDs during reconciliation. For data stores that are case-insensitive, such as OpenDJ, IDs and links that are created by reconciliation may be stored with a different case to how they are stored in the OpenIDM repository. This can cause problems during a reconciliation operation, as the links for these IDs might not match. + +For such data stores, you can configure OpenIDM to ignore case during reconciliation operations. With case-sensitivity turned off in OpenIDM, comparisons are done without regard to case. + +To specify case-insensitive data stores, set the `sourceIdsCaseSensitive` or `targetIdsCaseSensitive` property to `false` in the mapping for those links. For example, if the LDAP data store is case-insensitive, set the mapping from the LDAP store to the managed user repository as follows: + +[source, javascript] +---- +"mappings" : [ +{ +"name" : "systemLdapAccounts_managedUser", +"source" : "system/ldap/account", +"sourceIdsCaseSensitive" : false, +"target" : "managed/user", +"properties" : [ +... +---- +If a mapping inherits links by using the `links` property, you do not need to set case-sensitivity, because the mapping uses the setting of the referred links. + +Be aware that, even if you configure OpenIDM to be case-insensitive when comparing links, the OpenICF provisioner is not necessarily case-insensitive when it requests data. For example, if a user entry is stored with the ID `testuser` and you make a request for `\https://localhost:8443/openidm/managed/TESTuser`, most provisioners will filter out the match because of the difference in case, and will indicate that the record is not found. To prevent the provisioner from performing this secondary filtering, set the `enableFilteredResultsHandler` property to `false` in the provisioner configuration. For example: + +[source, console] +---- +"resultsHandlerConfig" : +{ + "enableFilteredResultsHandler":false, +}, +---- + +[CAUTION] +==== +Do not disable the filtered results handler for the CSV file connector. The CSV file connector does not perform filtering so if you disable the filtered results handler for this connector, the full CSV file will be returned for every request. +==== + + +[#reconciliation-optimization] +=== Optimizing Reconciliation Performance + +By default, reconciliation is configured to function optimally, with regard to performance. Some of these optimizations might, however, be unsuitable for your environment. The following sections describe the default optimizations and how they can be configured, as well as additional methods you can use to improve the performance of reconciliation operations. + +[#correlate-target-set] +==== Correlating Empty Target Sets + +To optimize performance, reconciliation does not correlate source objects to target objects if the set of target objects is empty when the correlation is started. This considerably speeds up the process the first time reconciliation is run. You can change this behavior for a specific mapping by adding the `correlateEmptyTargetSet` property to the mapping definition and setting it to `true`. For example: + +[source, javascript] +---- +{ + "mappings": [ + { + "name" : "systemMyLDAPAccounts_managedUser", + "source" : "system/MyLDAP/account", + "target" : "managed/user", + "correlateEmptyTargetSet" : true + }, + ] +} +---- +Be aware that this setting will have a performance impact on the reconciliation process. + + +[#prefetching-links] +==== Prefetching Links + +All links are queried at the start of reconciliation and the results of that query are used. You can disable the link prefetching so that the reconciliation process looks up each link in the database as it processes each source or target object. You can disable the prefetching of links by adding the `prefetchLinks` property to the mapping, and setting it to `false`, for example: + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "systemMyLDAPAccounts_managedUser", + "source": "system/MyLDAP/account", + "target": "managed/user" + "prefetchLinks" : false + } + ] +} +---- +Be aware that this setting will have a performance impact on the reconciliation process. + + +[#parallel-recon-tasks] +==== Parallel Reconciliation Threads + +By default, reconciliation is multithreaded; numerous threads are dedicated to the same reconciliation run. Multithreading generally improves reconciliation performance. The default number of threads for a single reconciliation run is 10 (plus the main reconciliation thread). Under normal circumstances, you should not need to change this number; however the default might not be appropriate in the following situations: + +* The hardware has many cores and supports more concurrent threads. As a rule of thumb for performance tuning, start with setting the thread number to two times the number of cores. + +* The source or target is an external system with high latency or slow response times. Threads may then spend considerable time waiting for a response from the external system. Increasing the available threads enables the system to prepare or continue with additional objects. + +To change the number of threads, set the `taskThreads` property in the `conf/sync.json` file, for example: + +[source, javascript] +---- +"mappings" : [ + { + "name" : "systemXmlfileAccounts_managedUser", + "source" : "system/xmlfile/account", + "target" : "managed/user", + "taskThreads" : 20 + ... + } + ] +} +---- +A zero value runs reconciliation as a serialized process, on the main reconciliation thread. + + +[#recon-query-optimization] +==== Improving Reconciliation Query Performance + +Reconciliation operations are processed in two phases; a __source phase__ and a __target phase__. In most reconciliation configurations, source and target queries make a read call to every record on the source and target systems to determine candidates for reconciliation. On slow source or target systems, these frequent calls can incur a substantial performance cost. + +To improve query performance in these situations, you can preload the entire result set into memory on the source or target system, or on both systems. Subsequent read queries on known IDs are made against the data in memory, rather than the data on the remote system. For this optimization to be effective, the entire result set must fit into the available memory on the system for which it is enabled. + +The optimization works by defining a `sourceQuery` or `targetQuery` in the synchronization mapping that returns not just the ID, but the complete object. + +The following example query loads the full result set into memory during the source phase of the reconciliation. The example uses a common filter expression, called with the `_queryFilter` keyword. The query returns the complete object: + +[source, javascript] +---- +"mappings" : [ + { + "name" : "systemLdapAccounts_managedUser", + "source" : "system/ldap/account", + "target" : "managed/user", + "sourceQuery" : { + "_queryFilter" : "true" + }, + ... +---- +OpenIDM tries to detect what data has been returned. The autodetection mechanism assumes that a result set that includes three or more fields per object (apart from the `_id` and `rev` fields) contains the complete object. + +You can explicitly state whether a query is configured to return complete objects by setting the value of `sourceQueryFullEntry` or `targetQueryFullEntry` in the mapping. The setting of these properties overrides the autodetection mechanism. + +Setting these properties to `false` indicates that the returned object is not the complete object. This might be required if a query returns more than three fields of an object, but not the complete object. Without this setting, the autodetect logic would assume that the complete object was being returned. OpenIDM uses only the IDs from this query result. If the complete object is required, the object is queried on demand. + +Setting these properties to `true` indicates that the complete object is returned. This setting is typically required only for very small objects, for which the number of returned fields does not reach the threshold required for the auto-detection mechanism to assume that it is a full object. In this case, the query result includes all the details required to pre-load the full object. + +The following excerpt indicates that the full objects are returned and that OpenIDM should not autodetect the result set: + +[source, javascript] +---- +"mappings" : [ + { + "name" : "systemLdapAccounts_managedUser", + "source" : "system/ldap/account", + "target" : "managed/user", + "sourceQueryFullEntry" : true, + "sourceQuery" : { + "_queryFilter" : "true" + }, + ... +---- +By default, all the attributes that are defined in the connector configuration file are loaded into memory. If your mapping uses only a small subset of the attributes in the connector configuration file, you can restrict your query to return only those attributes required for synchronization by using the `_fields` parameter with the query filter. + +The following excerpt loads only a subset of attributes into memory, for all users in an LDAP directory. + +[source, javascript] +---- +"mappings" : [ + { + "name" : "systemLdapAccounts_managedUser", + "source" : "system/ldap/account", + "target" : "managed/user", + "sourceQuery" : { + "_queryFilter" : "true", + "_fields" : "cn, sn, dn, uid, employeeType, mail" + }, + ... +---- + + +[#recon-provisioning-optimization] +==== Improving Role-Based Provisioning Performance With an onRecon Script + +OpenIDM provides an `onRecon` script that runs once, at the beginning of each reconciliation. This script can perform any setup or initialization operations that are appropriate for the reconciliation run. + +In addition, OpenIDM provides a `reconContext` that is added to a request's context chain when reconciliation runs. The `reconContext` can store pre-loaded data that can be used by other OpenIDM components (such as the managed object service) to increase performance. + +The default `onRecon` script (`openidm/bin/default/script/roles/onRecon.groovy`) loads the `reconContext` with all the roles and assignments that are required for the current mapping. The `effectiveAssignments` script checks the `reconContext` first. If a `reconContext` is present, the script uses that `reconContext` to populate the array of `effectiveAssignments`. This prevents a read operation to `managed/role` or `managed/assignment` every time reconciliation runs, and greatly improves the overall performance for role-based provisioning. + +You can customize the `onRecon`, `effectiveRoles`, and `effectiveAssignments` scripts to provide additional business logic during reconciliation. If you customize these scripts, copy the default scripts from `openidm/bin/defaults/scripts` into your project's `script` directory, and make the changes there. + + +[#recon-paging] +==== Paging Reconciliation Query Results + +xref:#recon-query-optimization["Improving Reconciliation Query Performance"] describes how to improve reconciliation performance by loading all entries into memory to avoid making individual requests to the external system for every ID. However, this optimization depends on the entire result set fitting into the available memory on the system for which it is enabled. For particularly large data sets (for example, data sets of hundreds of millions of users), having the entire data set in memory might not be feasible. + +To alleviate this constraint, OpenIDM supports reconciliation paging, which breaks down extremely large data sets into chunks. It also lets you specify the number of entries that should be reconciled in each chunk or page. + +Reconciliation paging is disabled by default, and can be enabled per mapping (in the `sync.json` file). To configure reconciliation paging, set the `reconSourceQueryPaging` property to `true` and set the `reconSourceQueryPageSize` in the synchronization mapping, for example: + +[source, javascript] +---- +{ + "mappings" : [ + { + "name" : "systemLdapAccounts_managedUser", + "source" : "system/ldap/account", + "target" : "managed/user", + "reconSourceQueryPaging" : true, + "reconSourceQueryPageSize" : 100, + ... + } +---- +The value of `reconSourceQueryPageSize` must be a positive integer, and specifies the number of entries that will be processed in each page. If reconciliation paging is enabled but no page size is set, a default page size of `1000` is used. + + + +[#scheduling-synchronization] +=== Scheduling Synchronization + +You can schedule synchronization operations, such as liveSync and reconciliation, using `cron`-like syntax. + +This section describes scheduling specifically for reconciliation and liveSync. You can use OpenIDM's scheduler service to schedule any other event by supplying a link to a script file, in which that event is defined. For information about scheduling other events, see xref:chap-scheduler-conf.adoc#chap-scheduler-conf["Scheduling Tasks and Events"]. + +[#configuring-sync-schedule] +==== Configuring Scheduled Synchronization + +Each scheduled reconciliation and liveSync task requires a schedule configuration file in your project's `conf` directory. By convention, schedule configuration files are named `schedule-schedule-name.json`, where __schedule-name__ is a logical name for the scheduled synchronization operation, such as `reconcile_systemXmlAccounts_managedUser`. + +Schedule configuration files have the following format: + +[source, javascript] +---- +{ + "enabled" : true, + "persisted" : false, + "type" : "cron", + "startTime" : "(optional) time", + "endTime" : "(optional) time", + "schedule" : "cron expression", + "misfirePolicy" : "optional, string", + "timeZone" : "(optional) time zone", + "invokeService" : "service identifier", + "invokeContext" : "service specific context info" +} +---- +These properties are specific to the scheduler service, and are explained in xref:chap-scheduler-conf.adoc#chap-scheduler-conf["Scheduling Tasks and Events"]. + +To schedule a reconciliation or liveSync task, set the `invokeService` property to either `sync` (for reconciliation) or `provisioner` for liveSync. + +The value of the `invokeContext` property depends on the type of scheduled event. For reconciliation, the properties are set as follows: + +[source, javascript] +---- +{ + "invokeService": "sync", + "invokeContext": { + "action": "reconcile", + "mapping": "systemLdapAccount_managedUser" + } +} +---- +The `mapping` is either referenced by its name in the `conf/sync.json` file, or defined inline by using the `mapping` property, as shown in the example in xref:#alternative-mapping["Specifying the Mapping as Part of the Schedule"]. + +For liveSync, the properties are set as follows: + +[source, javascript] +---- +{ + "invokeService": "provisioner", + "invokeContext": { + "action": "liveSync", + "source": "system/OpenDJ/__ACCOUNT__" + } +} +---- +The `source` property follows the convention for a pointer to an external resource object and takes the form `system/resource-name/object-type`. + +[IMPORTANT] +==== +When you schedule a reconciliation operation to run at regular intervals, do not set `"concurrentExecution" : true`. This parameter enables multiple scheduled operations to run concurrently. You cannot launch multiple reconciliation operations for a single mapping concurrently. + +Daylight Savings Time (DST) can cause problems for scheduled liveSync operations. For more information, see xref:chap-scheduler-conf.adoc#schedules-dst["Schedules and Daylight Savings Time"]. +==== + + +[#alternative-mapping] +==== Specifying the Mapping as Part of the Schedule + +Mappings for synchronization operations are usually stored in your project's `sync.json` file. You can, however, provide the mapping for scheduled synchronization operation by including it as part of the `invokeContext` of the schedule configuration, as shown in the following example: + +[source, javascript] +---- +{ + "enabled": true, + "type": "cron", + "schedule": "0 08 16 * * ?", + "invokeService": "sync", + "invokeContext": { + "action": "reconcile", + "mapping": { + "name": "CSV_XML", + "source": "system/Ldap/account", + "target": "managed/user", + "properties": [ + { + "source": "firstname", + "target": "firstname" + }, + ... + ], + "policies": [...] + } + } +} +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-troubleshooting.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-troubleshooting.adoc new file mode 100644 index 000000000..5343ae34a --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-troubleshooting.adoc @@ -0,0 +1,173 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-troubleshooting] +== Troubleshooting + +When things are not working check this chapter for tips and answers. + +[#stops-when-backgrounded] +=== OpenIDM Stopped in Background + +When you start OpenIDM in the background without having disabled the text console, the job can stop immediately after startup. + +[source, console] +---- +$ ./startup.sh & +[2] 346 +$ ./startup.sh +Using OPENIDM_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: + -Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> + +[2]+ Stopped ./startup.sh +---- +To resolve this problem, make sure you remove `openidm/bundle/org.apache.felix.shell.tui-1.4.1.jar` before starting OpenIDM, and also remove Felix cache files in `openidm/felix-cache/`. + + +[#sync-service-unsatisfied] +=== The scr list Command Shows Sync Service As Unsatisfied + +You might encounter this message in the logs. + +[source] +---- +WARNING: Loading configuration file /path/to/openidm/conf/sync.json failed +org.forgerock.openidm.config.InvalidException: + Configuration for org.forgerock.openidm.sync could not be parsed and may not + be valid JSON : Unexpected character ('}' (code 125)): expected a value + at [Source: java.io.StringReader@3951f910; line: 24, column: 6] + at org.forgerock.openidm.config.crypto.ConfigCrypto.parse... + at org.forgerock.openidm.config.crypto.ConfigCrypto.encrypt... + at org.forgerock.openidm.config.installer.JSONConfigInstaller.setConfig... +---- +This indicates a syntax error in `openidm/conf/sync.json`. After fixing your configuration, change to the `/path/to/openidm/` directory, and use the `cli.sh validate` command to check that your configuration files are valid. + +[source, console] +---- +$ cd /path/to/openidm ; ./cli.sh validate +Using boot properties at /path/to/openidm/conf/boot/boot.properties +................................................................... +[Validating] Load JSON configuration files from: +[Validating] /path/to/openidm/conf +[Validating] audit.json .................................. SUCCESS +[Validating] authentication.json ......................... SUCCESS +[Validating] managed.json ................................ SUCCESS +[Validating] provisioner.openicf-xml.json ................ SUCCESS +[Validating] repo.orientdb.json .......................... SUCCESS +[Validating] router.json ................................. SUCCESS +[Validating] scheduler-reconcile_systemXmlAccounts_managedUser.json SUCCESS +[Validating] sync.json ................................... SUCCESS +---- + + +[#json-parse-error] +=== JSON Parsing Error + +You might encounter this error message in the logs. + +[source] +---- +"Configuration for org.forgerock.openidm.provisioner.openicf could not be + parsed and may not be valid JSON : Unexpected character ('}' (code 125)): + was expecting double-quote to start field name" +---- +The error message usually indicates the precise point where the JSON file has the syntax problem. The error above was caused by an extra comma in the JSON file, `{"attributeName":{},{},}`. The second comma is redundant. + +The situation usually results in the service that the specific JSON file configures being left in the `unsatisfied` state. + +After fixing your configuration, change to the `/path/to/openidm/` directory, and use the `cli.sh validate` command to check that your configuration files are valid. + + +[#system-not-available] +=== System Not Available + +OpenIDM throws the following error as a result of a reconciliation where the source systems configuration can not be found. + +[source, javascript] +---- +{ + "error": "Conflict", + "description": "Internal Server Error: + org.forgerock.openidm.sync.SynchronizationException: + org.forgerock.openidm.objset.ObjectSetException: + System: system/HR/account is not available.: + org.forgerock.openidm.objset.ObjectSetException: + System: system/HR/account is not available.: + System: system/HR/account is not available." +} +---- +This error occurs when the `"name"` property value in `provisioner.resource.json` is changed from `HR` to something else. + +The same error occurs when a provisioner configuration fails to load due to misconfiguration, or when the path to the data file for a CSV or XML connector is incorrectly set. + + +[#bad-connector-host-reference] +=== Bad Connector Host Reference in Provisioner Configuration + +You might see the following error when a provisioner configuration loads. + +[source] +---- +Wait for meta data for config org.forgerock.openidm.provisioner.openicf-scriptedsql +---- +In this case the configuration fails to load because information is missing. One possible cause is an incorrect value for `connectorHostRef` in the provisioner configuration file. +For local Java connector servers, the following rules apply. + +* If the connector .jar is installed as a bundle under `openidm/bundle`, then the value must be `"connectorHostRef" : "osgi:service/org.forgerock.openicf.framework.api.osgi.ConnectorManager",`. + +* If the connector .jar is installed as a connector under `openidm/connectors`, then the value must be `"connectorHostRef" : "#LOCAL",`. + + + +[#missing-name-attribute] +=== Missing Name Attribute + +In this case, the situation in the audit recon log shows "NULL". + +A missing name attribute error, followed by an `IllegalArgumentException`, points to misconfiguration of the correlation rule, with the correlation query pointing to the external system. Such queries usually reference the "name" field which, if empty, leads to the error below. + +[source] +---- +Jan 20, 2012 1:59:58 PM + org.forgerock.openidm.provisioner.openicf.commons.AttributeInfoHelper build +SEVERE: Failed to build name attribute out of [null] +Jan 20, 2012 1:59:58 PM + org.forgerock.openidm.provisioner.openicf.impl.OpenICFProvisionerService query +SEVERE: Operation [query, system/ad/account] failed with Exception on system + object: java.lang.IllegalArgumentException: Attribute value must be an + instance of String. +Jan 20, 2012 1:59:58 PM org.forgerock.openidm.router.JsonResourceRouterService + handle +WARNING: JSON resource exception +org.forgerock.json.resource.JsonResourceException: IllegalArgumentException + at org.forgerock.openidm.provisioner....OpenICFProvisionerService.query... + at org.forgerock.openidm.provisioner.....OpenICFProvisionerService.handle... + at org.forgerock.openidm.provisioner.impl.SystemObjectSetService.handle... + at org.forgerock.json.resource.JsonResourceRouter.handle... +---- +Check your `correlationQuery`. Another symptom of a broken correlation query is that the audit recon log shows a situation of "NULL", and no onCreate, onUpdate or similar scripts are executed. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-ui.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-ui.adoc new file mode 100644 index 000000000..a93513904 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-ui.adoc @@ -0,0 +1,1367 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-ui] +== OpenIDM Web-Based User Interfaces + +OpenIDM provides a customizable, browser-based user interface. The functionality is subdivided into Administrative and Self-Service User Interfaces. + +If you are administering OpenIDM, navigate to the Administrative User Interface, also known as the Admin UI. If OpenIDM is installed on the local system, you can get to the Admin UI at the following URL: `\https://localhost:8443/admin`. In the Admin UI, you can configure connectors, customize managed objects, set up attribute mappings, manage accounts, and more. + +The Self-Service User Interface, also known as the Self-Service UI, provides role-based access to tasks based on BPMN2 workflows, and allows users to manage certain aspects of their own accounts, including configurable self-service registration. When OpenIDM starts, you can access the Self-Service UI at `\https://localhost:8443/`. + +[WARNING] +==== +The default password for the OpenIDM administrative user, `openidm-admin`, is `openidm-admin`. To protect your deployment in production, change this password. +==== +All users, including `openidm-admin`, can change their password through the Self-Service UI. After you have logged in, click Change Password. + +[#ui-admin] +=== Configuring OpenIDM from the Admin UI + +You can set up a basic configuration for OpenIDM with the Administrative User Interface (Admin UI). + +Through the Admin UI, you can connect to resources, configure attribute mapping and scheduled reconciliation, and set up and manage objects, such as users, groups, and devices. + +You can configure OpenIDM through Quick Start cards, and from the Configure and Manage drop-down menus. Try them out, and see what happens when you select each option. + +In the following sections, you will examine the default Admin UI dashboard, and learn how to set up custom Admin UI dashboards. + +[CAUTION] +==== +If your browser uses an AdBlock extension, it might inadvertently block some OpenIDM UI functionality, particularly if your configuration includes strings such as `ad`. For example, a connection to an Active Directory server might be configured at the endpoint `system/ad`. To avoid problems related to blocked UI functionality, either remove the AdBlock extension, or set up a suitable white list to ensure that none of the targeted endpoints are blocked. +==== + +[#ui-admin-default-dashboard] +==== Default Admin UI Dashboard + +When you log into the Admin UI, the first screen you should see is the "Reconciliation Dashboard". + +[#d0e2407] +image::images/adminui-dashboard.png[] +The Admin UI includes a fixed top menu bar. As you navigate around the Admin UI, you should see the same menu bar throughout. You can click the Dashboards > Reconciliation Dashboard to return to that screen. + +The default dashboard is split into four sections, based on widgets configured for OpenIDM. + +* Quick Start cards support one-click access to common administrative tasks, and are described in detail in the following section. + +* Last Reconciliation includes data from the most recent reconciliation between data stores. After you run a reconciliation, you should see data similar to: ++ + +image::images/reconstats-dashboard.png[] + +* System Health includes data on current CPU and memory usage. + +* Resources include an abbreviated list of configured connectors, mappings, and managed objects. + +The `Quick Start` cards allow quick access to the labeled configuration options, described here: + +* `Add Connector` ++ +Use the Admin UI to connect to external resources. For more information, see xref:chap-resource-conf.adoc#connector-wiz-adminui["Adding New Connectors from the Admin UI"]. + +* `Create Mapping` ++ +Configure synchronization mappings to map objects between resources. For more information, see xref:chap-synchronization.adoc#synchronization-mappings-file["Mapping Source Objects to Target Objects"]. + +* `Manage Role` ++ +Set up managed provisioning or authorization roles. For more information, see xref:chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"]. + +* `Add Device` ++ +Use the Admin UI to set up managed objects, including users, groups, roles, or even Internet of Things (IoT) devices. For more information, see xref:#ui-managing-accounts["Managing Accounts"]. + +* `Set Up Registration` ++ +Configure User Self-Registration. You can set up the OpenIDM Self-Service UI login screen, with a link that allows new users to start a verified account registration process. For more information, see xref:#ui-configuring["Configuring User Self-Service"]. + +* `Set Up Password Reset` ++ +Configure user self-service Password Reset. You can configure OpenIDM to allow users to reset forgotten passwords. For more information, see xref:#ui-configuring["Configuring User Self-Service"]. + +* `Manage User` ++ +Allows management of users in the current internal OpenIDM repository. You may have to run a reconciliation from an external repository first. For more information, see xref:chap-users-groups-roles.adoc#working-with-managed-users["Working with Managed Users"]. + +* `Set Up System` ++ +Configure how OpenIDM works, as it relates to: ++ + +** Authentication, as described in xref:chap-auth.adoc#supported-auth-session-modules["Supported Authentication and Session Modules"]. + +** Audit, as described in xref:chap-auditing.adoc#chap-auditing["Using Audit Logs"]. + +** Self Service UI, as described in xref:#ui-path["Changing the UI Path"]. + +** Email, as described in xref:chap-mail.adoc#chap-mail["Sending Email"]. + +** Updates, as described in xref:../install-guide/chap-update.adoc#chap-update["Updating OpenIDM"] in the __Installation Guide__. + + + + +[#ui-admin-new-dashboard] +==== Creating and Modifying Dashboards + +To create a new dashboard, click Dashboards > New Dashboard. You're prompted for a dashboard name, and whether to set it as the default. You can then add widgets. + +Alternatively, you can start with an existing dashboard. In the upper-right corner of the UI, next to the Add Widgets button, click the vertical ellipsis. In the menu that appears, you can take the following actions on the current dashboard: + +* Rename + +* Duplicate + +* Set as Default + +* Delete + +To add a widget to a dashboard, click Add Widgets and add the widget of your choice in the window that appears. + +To modify the position of a widget in a dashboard, click and drag on the move icon for the widget. You can find that four arrow icon in the upper right corner of the widget window, next to the three dot vertical ellipsis. + +image::images/widget-icons.png[] +If you add a new Quick Start widget, select the vertical ellipsis in the upper right corner of the widget, and click Settings. You can configure an Admin UI sub-widget to embed in the Quick Start widget in the pop-up menu that appears. + +Click Add a Link. You can then enter a name, a __destination URL__, and an icon for the widget. + +If you are linking to a specific page in the OpenIDM Admin UI, the destination URL can be the part of the address after the main page for the Admin UI, such as `\https://localhost:8443/admin` + +For example, if you want to create a quick start link to the Audit configuration tab, at `\https://localhost:8443/admin/#settings/audit/`, you could enter `#settings/audit` in the destination URL text box. + +OpenIDM writes the changes you make to the `ui-dashboard.json` file for your project. + +For example, if you add a Last Reconciliation and Embed Web Page widget to a new dashboard named Test, you'll see the following excerpt in your `ui-dashboard.json` file: + +[source, javascript] +---- +{ + "name" : "Test", + "isDefault" : false, + "widgets" : [ + { + "type" : "frame", + "size" : "large", + "frameUrl" : "http://example.com", + "height" : "100px", + "title" : "Example.com" + }, + { + "type" : "lastRecon", + "size" : "large", + "barchart" : "true" + }, + { + "type" : "quickStart", + "size" : "large", + "cards" : [ + { + "name" : "Audit", + "icon" : "fa-align-justify", + "href" : "#settings/audit" + } + ] + }, + ] +} +---- +For more information on each property, see the following table: + +[#widget-prop] +.Admin UI Widget Properties in ui-dashboard.json +[cols="33%,33%,34%"] +|=== +|Property |Options |Description + +a|`name` +a|User entry +a|Dashboard name + +a|`isDefault` +a|`true` or `false` +a|Default dashboard; can set one default + +a|`widgets` +a|Different options for `type` +a|Code blocks that define a widget + +a|`type` +a|`lifeCycleMemoryHeap`, `lifeCycleMemoryNonHeap`, `systemHealthFull`, `cpuUsage`, `lastRecon`, `resourceList`, `quickStart`, `frame`, `userRelationship` +a|Widget name + +a|`size` +a|`x-small`, `small`, `medium`, or `large` +a|Width of widget, based on a 12-column grid system, where x-small=4, small=6, medium=8, and large=12; for more information, see link:http://getbootstrap.com/css/[Bootstrap CSS, window=\_blank] + +a|`height` +a|Height, in units such as `cm`, `mm`, `px`, and `in` +a|Height; applies only to Embed Web Page widget + +a|`frameUrl` +a|URL +a|Web page to embed; applies only to Embed Web Page widget + +a|`title` +a|User entry +a|Label shown in the UI; applies only to Embed Web Page widget + +a|`barchart` +a|`true` or `false` +a|Reconciliation bar chart; applies only to Last Reconciliation widget +|=== +When complete, you can select the name of the new dashboard under the Dashboards menu. + +You can modify the options for each dashboard and widget. Select the vertical ellipsis in the upper right corner of the object, and make desired choices from the pop-up menu that appears. + + + +[#ui-overview] +=== Working With the Self-Service UI + +For all users, the Self-Service UI includes Dashboard and Profile links in the top menu bar. + +To access the Self-Service UI, start OpenIDM, then navigate to link:https://localhost:8443/[https://localhost:8443/, window=\_top]. If you have not installed a certificate that is trusted by a certificate authority, you are prompted with an Untrusted Connection warning the first time you log in to the UI. + +The Dashboard includes a list tasks assigned to the user who has logged in, tasks assigned to the relevant group, processes available to be invoked, current notifications for that user, along with Quick Start cards for that user's profile and password. + +[#d0e2765] +image::images/self-service-ui.png[] +For examples of these tasks, processes, and notifications, see xref:../samples-guide/chap-workflow-samples.adoc#chap-workflow-samples["Workflow Samples"] in the __Samples Guide__. + + +[#ui-configuring] +=== Configuring User Self-Service + +The following sections describe how you can configure three functions of user self-service: User Registration, Forgotten Username, and Password Reset. + +* User Registration: You can configure limited access that allows a current anonymous user to create their own accounts. To aid in this process, you can configure reCAPTCHA, email validation, and KBA questions. + +* Forgotten Username: You can set up OpenIDM to allow users to recover forgotten usernames via their email addresses or first and last names. OpenIDM can then display that username on the screen, and / or email such information to that user. + +* Password Reset: You can set up OpenIDM to verify user identities via KBA questions. If email configuration is included, OpenIDM would email a link that allows users to reset their passwords. + +If you enable email functionality, the one solution that works for all three self-service functions is to configure an outgoing email service for OpenIDM, as described in xref:chap-mail.adoc#chap-mail["Sending Email"]. + +image::images/ui-email-valid.png[] + +[NOTE] +==== +If you disable email validation only for user registration, you should perform one of the following actions: + +* Disable validation for `mail` in the managed user schema. Click Configure > Managed Objects > User > Schema. Under Schema Properties, click Mail, scroll down to Validation Policies, and set Required to `false`. + +* Configure the User Registration template to support user email entries. To do so, use xref:#ui-selfreg-addentries["Customizing the User Registration Page"], and substitute `mail` for `employeeNum`. + +Without these changes, users who try to register accounts will see a `Forbidden Request Error`. +==== +You can configure user self-service through the UI and through configuration files. + +* In the UI, log into the Admin UI. You can enable these features when you click Configure > User Registration, Configure > Forgotten Username, and Configure > Password Reset. + +* In the command-line interface, copy the following files from `samples/misc` to your working `project-dir/conf` directory: ++ +[none] +* User Registration: `selfservice-registration.json` +* Forgotten username: `selfservice-username.json` +* Password reset: `selfservice-reset.json` ++ +Examine the `ui-configuration.json` file in the same directory. You can activate or deactivate User Registration and Password Reset by changing the value associated with the `selfRegistration` and `passwordReset` properties: ++ + +[source, javascript] +---- +{ + "configuration" : { + "selfRegistration" : true, + "passwordReset" : true, + "forgotUsername" : true, + ... +---- + +For each of these functions, you can configure several options, including: +-- + +reCAPTCHA:: +Google reCAPTCHA helps prevent bots from registering users or resetting passwords on your system. For Google documentation, see link:https://www.google.com/recaptcha[Google reCAPTCHA, window=\_blank]. For directions on how to configure reCAPTCHA for user self-service, see xref:#self-service-recaptcha["Configuring Google reCAPTCHA"]. + +Email Validation / Email Username:: +You can configure the email messages that OpenIDM sends to users, as a way to verify identities for user self-service. For more information, see xref:#self-service-email["Configuring Self-Service Email Messages"]. + ++ +If you configure email validation, you must also configure an outgoing email service in OpenIDM. To do so, click Configure > System Preferences > Email. For more information, read xref:chap-mail.adoc#chap-mail["Sending Email"]. + +User Details:: +You can modify the Identity Email Field associated with user registration; by default, it is set to `mail`. + +User Query:: +When configuring password reset and forgotten username functionality, you can modify the fields that a user is allowed to query. If you do, you may need to modify the HTML templates that appear to users who request such functionality. For more information, see xref:#modifying-user-query-fields["Modifying Valid Query Fields"]. ++ +[open] +==== + +Valid Query Fields:: +Property names that you can use to help users find their usernames or verify their identity, such as `userName`, `mail`, or `givenName`. + +Identity ID Field:: +Property name associated with the User ID, typically `_id`. + +Identity Email Field:: +Property name associated with the user email field, typically something like `mail` or `email`. + +Identity Service URL:: +The path associated with the identity data store, such as `managed/user`. + +==== + +KBA Stage:: +You can modify the list of Knowledge-based Authentication (KBA) questions in the `conf/selfservice.kba.json` file. Users can then select the questions they will use to help them verify their own identities. For directions on how to configure KBA questions, see xref:#self-service-questions["Configuring Self-Service Questions"]. For User Registration, you cannot configure these questions in the Admin UI. + +Password Reset Form:: +You can change the Password Field for the Password Reset feature to specify a relevant password property such as `password`, `pwd`, or `userPassword`. Make sure the property you select matches the canonical form for user passwords. + +Snapshot Token:: +OpenIDM User Self-Service uses JWT tokens, with a default token lifetime of 1800 seconds. + +-- +You can reorder how OpenIDM works with relevant self-service options, specifically reCAPTCHA, KBA stage questions, and email validation. Based on the following screen, users who need to reset their passwords will go through reCAPTCHA, followed by email validation, and then answer any configured KBA questions. + +[#d0e2998] +image::images/password-reset-steps.png[] +To reorder the steps, either "drag and drop" the options in the Admin UI, or change the sequence in the associated configuration file, in the `project-dir/conf` directory. + +OpenIDM generates a token for each process. For example, users who forget their usernames and passwords go through two steps: + +* The user goes through the User Registration process gets a JWT token, and has the token lifetime (default = 1800 seconds) to get to the next step in the process. + +* With username in hand, that user may then start the Password Reset process. That user gets a second JWT token, with the token lifetime configured for that process. + + +[#self-service-common] +==== Common Configuration Details + +This section describes configuration details common to OpenIDM Self-Service features: User Registration, Password Reset, and Forgotten Username. + +[#self-service-email] +===== Configuring Self-Service Email Messages + +When a user requests a new account, a Password Reset, or a reminder of their username, you can configure OpenIDM to send that user an email message, to confirm the request. + +You can configure that email message either through the UI or the associated configuration files, as illustrated in the following excerpt of the `selfservice-registration.json` file: + +[source, javascript] +---- +{ + "stageConfigs" : { + { + "name" : "emailValidation", + "identityEmailField" : "mail", + "emailServiceUrl" : "external/email", + "from" : "admin@example.net", + "subject" : "Register new account", + "mimeType" : "text/html", + "subjectTranslations" : { + "en" : "Register new account", + "fr" : "Créer un nouveau compte" + }, + "messageTranslations" : { + "en" : "

This is your registration email.

Email verification link

", + "fr" : "

Ceci est votre mail d'inscription.

Lien de vérification email

", + "verificationLinkToken" : "%link%", + "verificationLink" : "https://localhost:8443/#register/" + } +... +---- +Note the two languages in the `subjectTranslations` and `messageTranslations` code blocks. You can add translations for languages other than US English `en` and French `fr`. Use the appropriate two-letter code based on ISO 639. End users will see the message in the language configured in their web browsers. + +You can set up similar emails for password reset and forgotten username functionality, in the `selfservice-reset.json` and `selfservice-username.json` files. For templates, see the `/path/to/openidm/samples/misc` directory. + +One difference between User Registration and Password Reset is in the `"verificationLink"`; for Password Reset, the corresponding URL is: + +[source, javascript] +---- +... + "verificationLink" : "https://localhost:8443/#passwordReset/" +... +---- +Substitute the IP address or FQDN where you've deployed OpenIDM for `localhost`. + + +[#self-service-recaptcha] +===== Configuring Google reCAPTCHA + +To use Google reCAPTCHA, you will need a Google account and your domain name (RFC 2606-compliant URLs such as `localhost` and `example.com` are acceptable for test purposes). Google then provides a Site key and a Secret key that you can include in the self-service function configuration. + +For example, you can add the following reCAPTCHA code block (with appropriate keys as defined by Google) into the `selfservice-registration.json`, `selfservice-reset.json` or the `selfservice-username.json` configuration files: + +[source, javascript] +---- +{ + "stageConfigs" : [ + { + "name" : "captcha", + "recaptchaSiteKey" : "< Insert Site Key Here >", + "recaptchaSecretKey" : "< Insert Secret Key Here >", + "recaptchaUri" : "https://www.google.com/recaptcha/api/siteverify" + }, +---- +You may also add the reCAPTCHA keys through the UI. + + +[#self-service-questions] +===== Configuring Self-Service Questions + +OpenIDM uses Knowledge-based Authentication (KBA) to help users prove their identity when they perform the noted functions. In other words, they get a choice of questions configured in the following file: `selfservice.kba.json`. + +The default version of this file is straightforward: + +[source, javascript] +---- +{ + "kbaPropertyName" : "kbaInfo", + "questions" : { + "1" : { + "en" : "What's your favorite color?", + "en_GB" : "What's your favorite colour?", + "fr" : "Quelle est votre couleur préférée?" + }, + "2" : { + "en" : "Who was your first employer?" + } + } +} +---- +You may change or add the questions of your choice, in JSON format. + +At this time, OpenIDM supports editing KBA questions only through the noted configuration file. However, individual users can configure their own questions and answers, during the User Registration process. + +After a regular user logs into the Self-Service UI, that user can modify, add, and delete KBA questions under the Profile tab: + +[#profile-kba-questions] +image::images/profile-kba-questions.png[] + +[NOTE] +==== +The Self-Service KBA modules do not preserve the case of the answers when they hash the value. All answers are first converted to lowercase. If you intend to pre-populate KBA answer strings by using a mapping, or any other means that uses the `openidm.hash` function or the CLI `secureHash` mechanism, you must provide the KBA string in lowercase for the value to be matched correctly. +==== + + +[#self-service-question-number] +===== Setting a Minimum Number of Self-Service Questions + +In addition, you can set a minimum number of questions that users have to define to register for their accounts. To do so, open the associated configuration file, `selfservice-registration.json`, in your `project-dir/conf` directory. Look for the code block that starts with `kbaSecurityAnswerDefinitionStage`: + +[source, javascript] +---- +{ + "name" : "kbaSecurityAnswerDefinitionStage", + "numberOfAnswersUserMustSet" : 1, + "kbaConfig" : null +}, +---- +In a similar fashion, you can set a minimum number of questions that users have to answer before OpenIDM allows them to reset their passwords. The associated configuration file is `selfservice-reset.json`, and the relevant code block is: + +[source, javascript] +---- +{ + "name" : "kbaSecurityAnswerVerificationStage", + "kbaPropertyName" : "kbaInfo", + "identityServiceUrl" : "managed/user", + "numberOfQuestionsUserMustAnswer" : "1", + "kbaConfig" : null +}, +---- + + + +[#ui-self-registration] +==== The End User and Commons User Self-Service + +When all self-service features are enabled, OpenIDM includes three links on the self-service login page: `Reset your password`, `Register`, and `Forgot Username?`. + +When the account registration page is used to create an account, OpenIDM normally creates a managed object in the OpenIDM repository, and applies default policies for managed objects. + + + +[#ui-custom-template] +=== Customizing a UI Template + +You may want to customize information included in the Self-Service UI. + +These procedures do not address actual data store requirements. If you add text boxes in the UI, it is your responsibility to set up associated properties in your repositories. + +To do so, you should copy existing default template files in the `openidm/ui/selfservice/default` subdirectory to associated `extension/` subdirectories. + +To simplify the process, you can copy some or all of the content from the `openidm/ui/selfservice/default/templates` to the `openidm/ui/selfservice/extension/templates` directory. + +You can use a similar process to modify what is shown in the Admin UI. + +[#ui-customizing-selfservice] +==== Customizing User Self-Service Screens + +In the following procedure, you will customize the screen that users see during the User Registration process. You can use a similar process to customize what a user sees during the Password Reset and Forgotten Username processes. + +For user Self-Service features, you can customize options in three files. Navigate to the `extension/templates/user/process` subdirectory, and examine the following files: + +* User Registration: `registration/userDetails-initial.html` + +* Password Reset: `reset/userQuery-initial.html` + +* Forgotten Username: `username/userQuery-initial.html` + +The following procedure demonstrates the process for User Registration. + +[#ui-selfreg-addentries] +.Customizing the User Registration Page +==== + +. When you configure user self service, as described in xref:#ui-configuring["Configuring User Self-Service"], anonymous users who choose to register will see a screen similar to: ++ + +image::images/ui-selfservice-selfreg.png[] + +. The screen you see is from the following file: `userDetails-initial.html`, in the `selfservice/extension/templates/user/process/registration` subdirectory. Open that file in a text editor. + +. Assume that you want new users to enter an employee ID number when they register. ++ +Create a new `form-group` stanza for that number. For this procedure, the stanza appears after the stanza for Last Name (or surname) `sn`: ++ + +[source, html] +---- +
+ + +
+---- + +. Edit the relevant `translation.json` file. As this is the customized file for the Self-Service UI, you will find it in the `selfservice/extension/locales/en` directory that you set up in xref:#ui-customizing["Customizing the UI"]. ++ +You need to find the right place to enter text associated with the `employeeNum` property. Look for the other properties in the `userDetails-initial.html` file. ++ +The following excerpt illustrates the `employeeNum` property as added to the `translation.json` file. ++ + +[source, javascript] +---- +... +"givenName" : "First Name", +"sn" : "Last Name", +"employeeNum" : "Employee ID Number", +... +---- + +. The next time an anonymous user tries to create an account, that user should see a screen similar to: ++ + +image::images/ui-custom-selfreg.png[] + +==== +In the following procedure, you will customize what users can modify when they navigate to their User Profile page: + +[#ui-profile-custtab] +.Adding a Custom Tab to the User Profile Page +==== +If you want to allow users to modify additional data on their profiles, this procedure is for you. + +. Log in to the Self-Service UI. Click the Profile tab. You should see at least the following tabs: `Basic Info` and `Password`. In this procedure, you will add a `Mobile Phone` tab. + +. OpenIDM generates the user profile page from the following file: `UserProfileTemplate.html`. Assuming you set up custom `extension` subdirectories, as described in xref:#ui-custom-template["Customizing a UI Template"], you should find a copy of this file in the following directory: `selfservice/extension/templates/user`. + +. Examine the first few lines of that file. Note how the `tablist` includes the tabs in the Self-Service UI user profile: Basic Info and Password, associated with the `common.user.basicInfo` and `common.user.password` properties. ++ +The following excerpt includes a third tab, with the `mobilePhone` property: ++ + +[source, javascript] +---- +
+ + +... +---- + +. Next, you should provide information for the tab. Based on the comments in the file, and the entries in the `Password` tab, the following code sets up a Mobile Phone number entry: ++ + +[source, html] +---- +
+
+
+
+ +
+ +
+
+
+ +
+
+ ... +---- ++ + +[NOTE] +====== +For illustration, this procedure uses the HTML tags found in the `UserProfileTemplate.html` file. You can use any standard HTML content within `tab-pane` tags, as long as they include a standard `form` tag and standard `input` fields. OpenIDM picks up this information when the tab is saved, and uses it to `PATCH` user content. +====== + +. Review the `managed.json` file. Make sure it is `viewable` and `userEditable` as shown in the following excerpt: ++ + +[source, javascript] +---- +"telephoneNumber" : { + "type" : "string", + "title" : "Mobile Phone", + "viewable" : true, + "userEditable" : true, + "pattern" : "^\\+?([0-9\\- \\(\\)])*$" +}, +---- + +. Open the applicable `translation.json` file. You should find a copy of this file in the following subdirectory: `selfservice/extension/locales/en/`. ++ +Search for the line with `basicInfo`, and add an entry for `mobilePhone`: ++ + +[source, javascript] +---- +"basicInfo": "Basic Info", +"mobilePhone": "Mobile Phone", +---- + +. Review the result. Log in to the Self-Service UI, and click Profile. Note the entry for the Mobile Phone tab. ++ + +image::images/ui-updated-profile.png[] + +==== + + +[#modifying-user-query-fields] +==== Modifying Valid Query Fields + +For Password Reset and Forgotten Username functionality, you may choose to modify Valid Query Fields, such as those described in xref:#ui-configuring["Configuring User Self-Service"]. + +For example, if you click Configure > Password Reset > User Query Form, you can make changes to __Valid Query Fields__. + +image::images/ui-valid-query.png[] +If you add, delete, or modify any Valid Query Fields, you will have to change the corresponding `userQuery-initial.html` file. + +Assuming you set up custom `extension` subdirectories, as described in xref:#ui-custom-template["Customizing a UI Template"], you can find this file in the following directory: `selfservice/extension/templates/user/process`. +If you change any Valid Query Fields, you should make corresponding changes. + +* For Forgotten Username functionality, you would modify the `username/userQuery-initial.html` file. + +* For Password Reset functionality, you would modify the `reset/userQuery-initial.html` file. + +For a model of how you can change the `userQuery-initial.html` file, see xref:#ui-selfreg-addentries["Customizing the User Registration Page"]. + + + +[#ui-managing-accounts] +=== Managing Accounts + +Only administrative users (with the role `openidm-admin`) can add, modify, and delete accounts from the Admin UI. Regular users can modify certain aspects of their own accounts from the Self-Service UI. + +[#ui-account-admin] +==== Account Configuration + +In the Admin UI, you can manage most details associated with an account, as shown in the following screenshot. + +[#d0e3501] +image::images/ui-data-account.png[] +You can configure different functionality for an account under each tab: +-- + +Details:: +The Details tab includes basic identifying data for each user, with two special entries: ++ +[open] +==== + +Status:: +By default, accounts are shown as __active__. To suspend an account, such as for a user who has taken a leave of absence, set that user's status to __inactive__. + +Manager:: +You can assign a manager from the existing list of managed users. + +==== + +Password:: +As an administrator, you can create new passwords for users in the managed user repository. + +Provisioning Roles:: +Used to specify how objects are provisioned to an external system. For more information, see xref:chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"]. + +Authorization Roles:: +Used to specify the authorization rights of a managed user within OpenIDM. For more information, see xref:chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"]. + +Direct Reports:: +Users who are listed as managers of others have entries under the Direct Reports tab, as shown in the following illustration: ++ + +image::images/ui-direct-reports.png[] + +Linked Systems:: +Used to display account information reconciled from external systems. + +-- + + +[#ui-managing-account-procedures] +==== Procedures for Managing Accounts + +With the following procedures, you can add, update, and deactivate accounts for managed objects such as users. + +The managed object does not have to be a user. It can be a role, a group, or even be a physical item such as an IoT device. The basic process for adding, modifying, deactivating, and deleting other objects is the same as it is with accounts. However, the details may vary; for example, many IoT devices do not have telephone numbers. + +[#add-user-account] +.To Add a User Account +==== + +. Log in to the Admin UI at `\https://localhost:8443/admin`. + +. Click Manage > User. + +. Click New User. + +. Complete the fields on the New User page. ++ +Most of these fields are self-explanatory. Be aware that the user interface is subject to policy validation, as described in xref:chap-policies.adoc#chap-policies["Using Policies to Validate Data"]. So, for example, the email address must be a valid email address, and the password must comply with the password validation settings that appear if you enter an invalid password. + +==== +In a similar way, you can create accounts for other managed objects. + +You can review new managed object settings in the `managed.json` file of your `project-dir/conf` directory. + +In the following procedures, you learn how to update, deactivate, and delete user accounts, as well as how to view that account in different user resources. You can follow essentially the same procedures for other managed objects such as IoT devices. + +[#ui-update-account] +.To Update a User Account +==== + +. Log in to the Admin UI at `\https://localhost:8443/admin` as an administrative user. + +. Click Manage > User. + +. Click the Username of the user that you want to update. + +. On the profile page for the user, modify the fields you want to change and click Update. ++ +The user account is updated in the OpenIDM repository. + +==== + +[#delete-user-account] +.To Delete a User Account +==== + +. Log in to the Admin UI at `\https://localhost:8443/admin` as an administrative user. + +. Click Manage > User. + +. Select the checkbox next to the desired Username. + +. Click the Delete Selected button. + +. Click OK to confirm the deletion. ++ +The user is deleted from the internal repository. + +==== + +[#user-linked-view] +.To View an Account in External Resources +==== +The Admin UI displays the details of the account in the OpenIDM repository (managed/user). When a mapping has been configured between the repository and one or more external resources, you can view details of that account in any external system to which it is linked. As this view is read-only, you cannot update a user record in a linked system from within the Self-Service UI. + +By default, __implicit synchronization__ is enabled for mappings __from__ the `managed/user` repository __to__ any external resource. This means that when you update a managed object, any mappings defined in the `sync.json` file that have the managed object as the source are automatically executed to update the target system. You can see these changes in the Linked Systems section of a user's profile. + +To view a user's linked accounts: + +. Log in to the Admin UI at `\https://localhost:8443/admin`. + +. Click Manage User > __Username__ > Linked Systems. + +. The Linked Systems panel indicates the external mapped resource or resources. + +. Select the resource in which you want to view the account, from the Linked Resource list. ++ +The user record in the linked resource is displayed. + +==== + + + +[#ui-account-relationships] +=== Configuring Account Relationships + +This section will help you set up relationships between human users and devices, such as IoT devices. + +You'll set this up with the help of the Admin UI schema editor, which allows you to create and customize managed objects such as `Users` and `Devices` as well as relationships between managed objects. You can also create these options in the `managed.json` file for your project. + +When complete, you will have users who can own multiple unique devices. If you try to assign the same device to more than one owner, OpenIDM will stop you with an error message. + +This section assumes that you have started OpenIDM with xref:../samples-guide/chap-ldap-samples.adoc#more-sample-2b["Sample 2b - LDAP Two Way"] in the __Samples Guide__. +After you have started OpenIDM with "Sample 2b", go through the following procedures, where you will: + +* Set up a managed object named `Device`, with unique serial numbers for each device. You can configure the searchable schema of your choice. See xref:#ui-add-iot-schema["Configuring Schema for a Device"] for details. + +* Set up a relationship from the Device to the User managed object. See xref:#ui-configure-iot-relationship["Configure a Relationship from the Device Managed Object"] for details. + +* Set up a reverse relationship from the User to the Device managed object. See xref:#ui-configure-iot-user["Configure a Relationship From the User Managed Object"] for details. + +* Demonstrate the relationships. Assign users to devices. See what happens when you try to assign a device to more than one user. For details, see xref:#ui-iot-demo["Demonstrating an IoT Relationship"]. + + +[#ui-add-iot-schema] +.Configuring Schema for a Device +==== +This procedure illustrates how you might set up a Device managed object, with schema that configures relationships to users. + +After you configure the schema for the Device managed object, you can collect information such as model, manufacturer, and serial number for each device. In the next procedure, you'll set up an `owner` schema property that includes a relationship to the User managed object. + +. Click Configure > Managed Objects > New Managed Object. Give that object an appropriate IoT name. For this procedure, specify `Device`. You should also select a managed object icon. Click Save. + +. You should now see four tabs: Details, Schema, Scripts, and Properties. Click the Schema tab. ++ + +image::images/ui-initial-mo.png[] + +. The items that you can add to the new managed object depend on the associated properties. ++ +The Schema tab includes the `Readable Title` of the device; in this case, set it to `Device`. + +. You can add schema properties as needed in the UI. Click the Property button. Include the properties shown in the illustration: model, serialNumber, manufacturer, description, and category. + +. Initially, the new property is named `Property 1`. As soon as you enter a property name such as `model`, OpenIDM changes that property name accordingly. + +. To support UI-based searches of devices, make sure to set the Searchable option to true for all configured schema properties, unless it includes extensive text, In this case, you should set Searchable to false for the `description` property. ++ +The Searchable option is used in the data grid for the given object. When you click Manage > Device (or another object such as User), OpenIDM displays searchable properties for that object. + +. After you save the properties for the new managed object type, OpenIDM saves those entries in the `managed.json` file in the `project-dir/conf` directory. + +. Now click Manage > Device > New Device. Add a device as shown in the following illustration. ++ + +image::images/ui-mo-wearable.png[] + +. You can continue adding new devices to the managed object, or reconcile that managed object with another data store. The other procedures in this section assume that you have set up the devices as shown in the next illustration. + +. When complete, you can review the list of devices. Based on this procedure, click Manage > Device. ++ + +image::images/ui-mo-iot.png[] + +. Select one of the listed devices. You'll note that the label for the device in the Admin UI matches the name of the first property of the device. ++ + +image::images/ui-oneiot-device.png[] ++ +You can change the order of schema properties for the Device managed object by clicking Configure > Managed Object > Device > Schema, and select the property that you want to move up or down the list. ++ +Alternatively, you can make the same changes to this (or any managed object schema) in the `managed.json` file for your project. + +==== + +[#ui-configure-iot-relationship] +.Configure a Relationship from the Device Managed Object +==== +In this procedure, you will add a property to the schema of the Device managed object. + +. In the Admin UI, click Configure > Managed Objects > Device > Schema. + +. Under the Schema tab, add a new property. For this procedure, we call it __owner__. Unlike other schema properties, set the Searchable property to false. + +. Scroll down to Validation Policies; click the Type box and select Relationship. This opens additional relationship options. + +. Set up a Reverse Property Name of `IoT_Devices`. You'll use that reverse property name in the next xref:#ui-configure-iot-user["Configure a Relationship From the User Managed Object"]. ++ + +image::images/ui-device-relation.png[] ++ +Be sure to set the Reverse Relationship and Validate options to `true`, which ensures that each device is associated with no more than one user. + +. Scroll down and add a Resource Collection. Set up a link to the `managed/user` object, with a label that matches the `User` managed object. + +. Enable queries of the User managed object by setting Query Filter to true. The Query Filter value for this Device object allows you to identify the user who "owns" each device. For more information, see xref:chap-data.adoc#query-filters["Common Filter Expressions"]. ++ + +image::images/ui-device-resource.png[] + +. Set up fields from `managed/user` properties. The properties shown in the illustration are just examples, based on xref:../samples-guide/chap-ldap-samples.adoc#more-sample-2b["Sample 2b - LDAP Two Way"] in the __Samples Guide__. + +. Add one or more Sort Keys from the configured fields. + +. Save your changes. + +==== + +[#ui-configure-iot-user] +.Configure a Relationship From the User Managed Object +==== +In this procedure, you will configure an existing User Managed Object with schema to match what was created in xref:#ui-configure-iot-relationship["Configure a Relationship from the Device Managed Object"]. + +With the settings you create, OpenIDM supports a relationship between a single user and multiple devices. In addition, this procedure prevents multiple users from "owning" any single device. + +. In the Admin UI, click Configure > Managed Objects > User > Schema. + +. Under the Schema tab, add a new property, called IoT_Devices. + +. Make sure the searchable property is set to false, to minimize confusion in the relationship. Otherwise, you'll see every device owned by every user, when you click Manage > User. + +. For validation policies, you'll set up an __array__ with a relationship. Note how the reverse property name matches the property that you configured in xref:#ui-configure-iot-relationship["Configure a Relationship from the Device Managed Object"]. ++ + +image::images/ui-device-array.png[] ++ +Be sure to set the Reverse Relationship and Validate options to `true`, which ensures that no more than one user gets associated with a specific device. + +. Scroll down to Resource Collection, and add references to the `managed/device` resource, as shown in the next illustration. + +. Enter `true` in the Query Filter text box. In this relationship, OpenIDM will read all information from the `managed/device` managed object, with information from the device fields and sort keys that you configured in xref:#ui-configure-iot-relationship["Configure a Relationship from the Device Managed Object"]. ++ + +image::images/ui-device-resource-collection.png[] + +==== + +[#ui-iot-demo] +.Demonstrating an IoT Relationship +==== +This procedure assumes that you have already taken the steps described in the previous procedures in this section, specifically, xref:#ui-add-iot-schema["Configuring Schema for a Device"], xref:#ui-configure-iot-relationship["Configure a Relationship from the Device Managed Object"], and xref:#ui-configure-iot-user["Configure a Relationship From the User Managed Object"]. + +This procedure also assumes that you started OpenIDM with xref:../samples-guide/chap-ldap-samples.adoc#more-sample-2b["Sample 2b - LDAP Two Way"] in the __Samples Guide__, and have reconciled to set up users. + +. From the Admin UI, click Manage > User. Select a user, and in this case, click the IoT Devices tab. See how you can select any of the devices that you may have added in xref:#ui-add-iot-schema["Configuring Schema for a Device"]. ++ + +image::images/ui-user-iot-device.png[] + +. Alternatively, try to assign a device to an owner. To do so, click Manage > Device, and select a device. You'll see either an `Add Owner` or `Update Owner` button, which allows you to assign a device to a specific user. ++ +If you try to assign a device already assigned by a different user, you'll get the following message: `Conflict with Existing Relationship`. + +==== + + +[#ui-managing-workflows] +=== Managing Workflows From the Self-Service UI + +The Self-Service UI is integrated with the embedded Activiti worfklow engine, enabling users to interact with workflows. Available workflows are displayed under the Processes item on the Dashboard. In order for a workflow to be displayed here, the workflow definition file must be present in the `openidm/workflow` directory. + +A sample workflow integration with the Self-Service UI is provided in `openidm/samples/workflow`, and documented in xref:../samples-guide/chap-workflow-samples.adoc#example-provisioning-workflow["Sample Workflow - Provisioning User Accounts"] in the __Samples Guide__. Follow the steps in that sample for an understanding of how the workflow integration works. + +General access to workflow-related endpoints is based on the access rules defined in the `script/access.js` file. The configuration defined in the `conf/process-access.json` file determines who can invoke workflows. By default all users with the role `openidm-authorized` or `openidm-admin` can invoke any available workflow. The default `process-access.json` file is as follows: + +[source, javascript] +---- +{ + "workflowAccess" : [ + { + "propertiesCheck" : { + "property" : "_id", + "matches" : ".*", + "requiresRole" : "openidm-authorized" + } + }, + { + "propertiesCheck" : { + "property" : "_id", + "matches" : ".*", + "requiresRole" : "openidm-admin" + } + } + ] +} +---- +-- + +`"property"`:: +Specifies the property used to identify the process definition. By default, process definitions are identified by their `_id`. + +`"matches"`:: +A regular expression match is performed on the process definitions, according to the specified property. The default (`"matches" : ".*"`) implies that all process definition IDs match. + +`"requiresRole"`:: +Specifies the OpenIDM role that is required for users to have access to the matched process definition IDs. In the default file, users with the role `openidm-authorized` or `openidm-admin` have access. + +-- +To extend the process action definition file, identify the processes to which users should have access, and specify the qualifying user roles. For example, if you want to allow access to users with a role of `ldap`, add the following code block to the `process-access.json` file: + +[source] +---- +{ + "propertiesCheck" : { + "property" : "_id", + "matches" : ".*", + "requiresRole" : "ldap" + } +} +---- + +[#add-role-workflow] +==== Adding Another Role to a Workflow + +Sometimes, you'll want to configure multiple roles with access to the same workflow process. For example, if you want users with a role of doctor and nurse to both have access to certain workflows, you could set up the following code block within the `process-access.json` file: + +[source, javascript] +---- +{ + "propertiesCheck" : { + "property" : "_id", + "matches" : ".*", + "requiresRole" : "doctor" + } +}, +{ + "propertiesCheck" : { + "property" : "_id", + "matches" : ".*", + "requiresRole" : "nurse" + } +} +---- +You could add more `requiresRole` code blocks, such as: + +[source, javascript] +---- +{ + "propertiesCheck" : { + "property" : "_id", + "matches" : ".*", + "requiresRole" : "medic" + } +} +---- + + + +[#ui-customizing] +=== Customizing the UI + +OpenIDM allows you to customize both the Admin and Self-Service UIs. When you install OpenIDM, you can find the default UI configuration files in two directories: + +* Admin UI: `openidm/ui/admin/default` + +* Self-Service UI: `openidm/ui/selfservice/default` + +OpenIDM looks for custom themes and templates in the following directories: + +* Admin UI: `openidm/ui/admin/extension` + +* Self-Service UI: `openidm/ui/selfservice/extension` + +Before starting the customization process, you should create these directories. If you are running UNIX/Linux, the following commands create a copy of the appropriate subdirectories: + +[source, console] +---- +$ cd /path/to/openidm/ui +$ cp -r selfservice/default/. selfservice/extension +$ cp -r admin/default/. admin/extension +---- +OpenIDM also includes templates that may help, in two other directories: + +* Admin UI: `openidm/ui/admin/default/templates` + +* Self-Service UI: `openidm/ui/selfservice/default/templates` + + + +[#ui-theme] +=== Changing the UI Theme + +You can customize the theme of the user interface. OpenIDM uses the link:http://getbootstrap.com[Bootstrap, window=\_blank] framework. You can download and customize the OpenIDM UI with the Bootstrap themes of your choice. OpenIDM is also configured with the link:http://fortawesome.github.io/Font-Awesome/[Font Awesome CSS toolkit, window=\_blank]. + +[NOTE] +==== +If you use link:http://fortawesome.github.io/Font-Awesome/icons/[Brand Icons from the Font Awesome CSS Toolkit, window=\_blank], be aware of the following statement: + +All brand icons are trademarks of their respective owners. The use of these trademarks does not indicate endorsement of the trademark holder by ForgeRock, nor vice versa. +==== + +[#ui-bootstrap] +==== OpenIDM UI Themes and Bootstrap + +You can configure a few features of the OpenIDM UI in the `ui-themeconfig.json` file in your project's `conf/` subdirectory. However, to change most theme-related features of the UI, you must copy target files to the appropriate `extension` subdirectory, and then modify them as discussed in xref:#ui-customizing["Customizing the UI"]. + +The default configuration files for the Admin and Self-Service UIs are identical for theme configuration. + +By default the UI reads the stylesheets and images from the respective `openidm/ui/function/default` directories. Do not modify the files in this directory. Your changes may be overwritten the next time you update or even patch your system. + +To customize your UI, first set up matching subdirectories for your system (`openidm/ui/admin/extension` and `openidm/ui/selfservice/extension`). For example, assume you want to customize colors, logos, and so on. + +You can set up a new theme, primarily through custom Bootstrap CSS files, in appropriate `extension/` subdirectories, such as `openidm/ui/selfservice/extension/libs` and `openidm/ui/selfservice/extension/css`. + +You may also need to update the `"stylesheets"` listing in the `ui-themeconfig.json` file for your project, in the `project-dir/conf` directory. + +[source, javascript] +---- +... +"stylesheets" : ["css/bootstrap-3.3.5-custom.css", "css/structure.css", "css/theme.css"], +... +---- +You can find these `stylesheets` in the `/css` subdirectory. + +* `bootstrap-3.3.5-custom.css`: Includes custom settings that you can get from various Bootstrap configuration sites, such as the Bootstrap link:http://getbootstrap.com/customize/[Customize and Download, window=\_blank] website. ++ +You may find the ForgeRock version of this in the `config.json` file in the `ui/selfservice/default/css/common/structure/` directory. + +* `structure.css`: Supports configuration of structural elements of the UI. + +* `theme.css`: Includes customizable options for UI themes such as colors, buttons, and navigation bars. + +If you want to set up custom versions of these files, copy them to the `extension/css` subdirectories. + + +[#ui-logo] +==== Changing the Default Logo + +For the Self-Service UI, you can find the default logo in the `openidm/ui/selfservice/default/images` directory. To change the default logo, copy desired files to the `openidm/ui/selfservice/extension/images` directory. You should see the changes after refreshing your browser. + +To specify a different file name, or to control the size, and other properties of the image file that is used for the logo, adjust the `logo` property in the UI theme configuration file for your project: `project-dir/conf/ui-themeconfig.json`). + +The following change to the UI theme configuration file points to an image file named `example-logo.png`, in the `openidm/ui/extension/images` directory: + +[source, javascript] +---- +... +"loginLogo" : { + "src" : "images/example-logo.png", + "title" : "Example.com", + "alt" : "Example.com", + "height" : "104px", + "width" : "210px" +}, +... +---- +Refresh your browser window for the new logo to appear. + + +[#ui-locale] +==== Changing the Language of the UI + +Currently, the UI is provided only in US English. You can translate the UI and specify that your own locale is used. The following example shows how to translate the UI into French: + +==== + +. Assuming you set up custom `extension` subdirectories, as described in xref:#ui-customizing["Customizing the UI"], you can copy the default (`en`) locale to a new (`fr`) subdirectory as follows: ++ + +[source, console] +---- +$ cd /path/to/openidm/ui/selfservice/extension/locales +$ cp -R en fr +---- ++ +The new locale (`fr`) now contains the default `translation.json` file: ++ + +[source, console] +---- +$ ls fr/ +translation.json +---- + +. Translate the values of the properties in the `fr/translate.json` file. Do __not__ translate the property names. For example: ++ + +[source, javascript] +---- +... +"UserMessages" : { + "changedPassword" : "Mot de passe a été modifié", + "profileUpdateFailed" : "Problème lors de la mise à jour du profil", + "profileUpdateSuccessful" : "Profil a été mis à jour", + "userNameUpdated" : "Nom d'utilisateur a été modifié", +.... +---- + +. Change the UI configuration to use the new locale by setting the value of the `lang` property in the `project-dir/conf/ui-configuration.json` file, as follows: ++ + +[source, console] +---- +"lang" : "fr", +---- + +. Refresh your browser window, and OpenIDM applies your change. + +==== +You can also change the labels for accounts in the UI. To do so, navigate to the `Schema Properties` for the managed object to be changed. + +To change the labels for user accounts, navigate to the Admin UI. Click Configure > Managed Objects > User, and scroll down to Schema. + +Under Schema Properties, select a property and modify the `Readable Title`. For example, you can modify the `Readable Title` for `userName` to a label in another language, such as `Nom d'utilisateur`. + + +[#ui-project-config] +==== Creating a Project-Specific UI Theme + +You can create specific UI themes for different projects and then point a particular UI instance to use a defined theme on startup. To create a complete custom theme, follow these steps: + +==== + +. Shut down the OpenIDM instance, if it is running. In the OSGi console, type: ++ + +[source, console] +---- +shutdown +-> +---- + +. Copy the entire default Self-Service UI theme to an accessible location. For example: ++ + +[source, console] +---- +$ cd /path/to/openidm/ui/selfservice +$ cp -r default /path/to/openidm/new-project-theme +---- + +. If desired, repeat the process with the Admin UI; just remember to copy files to a different directory: ++ + +[source, console] +---- +$ cd /path/to/openidm/ui/admin +$ cp -r default /path/to/openidm/admin-project-theme +---- + +. In the copied theme, modify the required elements, as described in the previous sections. Note that nothing is copied to the extension folder in this case - changes are made in the copied theme. + +. In the `conf/ui.context-selfservice.json` file, modify the values for `defaultDir` and `extensionDir` to the directory with your `new-project-theme`: ++ + +[source, javascript] +---- +{ + "enabled" : true, + "urlContextRoot" : "/", + "defaultDir" : "&{launcher.install.location}/ui/selfservice/default", + "extensionDir" : "&{launcher.install.location}/ui/selfservice/extension" +} +---- + +. If you want to repeat the process for the Admin UI, make parallel changes to the `project-dir/conf/ui.context-admin.json` file. + +. Restart OpenIDM. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh +---- + +. Relaunch the UI in your browser. The UI is displayed with the new custom theme. + +==== + + + +[#ui-external-password-reset] +=== Using an External System for Password Reset + +By default, the Password Reset mechanism is handled internally, in OpenIDM. You can reroute Password Reset in the event that a user has forgotten their password, by specifying an external URL to which Password Reset requests are sent. Note that this URL applies to the Password Reset link on the login page only, not to the security data change facility that is available after a user has logged in. + +To set an external URL to handle Password Reset, set the `passwordResetLink` parameter in the UI configuration file (`conf/ui-configuration.json`) file. The following example sets the `passwordResetLink` to `\https://accounts.example.com/account/reset-password`: + +[source, console] +---- +passwordResetLink: "https://accounts.example.com/reset-password" +---- +The `passwordResetLink` parameter takes either an empty string as a value (which indicates that no external link is used) or a full URL to the external system that handles Password Reset requests. + +[NOTE] +==== +External Password Reset and security questions for internal Password Reset are mutually exclusive. Therefore, if you set a value for the `passwordResetLink` parameter, users will not be prompted with any security questions, regardless of the setting of the `securityQuestions` parameter. +==== + + +[#ui-external-logout] +=== Providing a Logout URL to External Applications + +By default, a UI session is invalidated when a user clicks on the Log out link. In certain situations your external applications might require a distinct logout URL to which users can be routed, to terminate their UI session. + +The logout URL is `#logout`, appended to the UI URL, for example, `\https://localhost:8443/#logout/`. + +The logout URL effectively performs the same action as clicking on the Log out link of the UI. + + +[#ui-path] +=== Changing the UI Path + +By default, the self service UI is registered at the root context and is accessible at the URL `\https://localhost:8443`. To specify a different URL, edit the `project-dir/conf/ui.context-selfservice.json` file, setting the `urlContextRoot` property to the new URL. + +For example, to change the URL of the self service UI to `\https://localhost:8443/exampleui`, edit the file as follows: + +[source, console] +---- +"urlContextRoot" : "/exampleui", +---- +Alternatively, to change the Self-Service UI URL in the Admin UI, follow these steps: + +==== + +. Log in to the Admin UI. + +. Select Configure > System Preferences, and select the Self-Service UI tab. + +. Specify the new context route in the Relative URL field. + +==== + + +[#ui-disabling] +=== Disabling the UI + +The UI is packaged as a separate bundle that can be disabled in the configuration before server startup. To disable the registration of the UI servlet, edit the `project-dir/conf/ui.context-selfservice.json` file, setting the `enabled` property to false: + +[source, console] +---- +"enabled" : false, +---- + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-users-groups-roles.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-users-groups-roles.adoc new file mode 100644 index 000000000..3b09909ec --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-users-groups-roles.adoc @@ -0,0 +1,1827 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-users-groups-roles] +== Managing Users, Groups, Roles and Relationships + +OpenIDM provides a default schema for typical managed object types, such as users and roles, but does not control the structure of objects that you store in the OpenIDM repository. You can modify or extend the schema for the default object types, and you can set up a new managed object type for any item that can be collected in a data set. For example, with the right schema, you can set up any device associated with the Internet of Things (IoT). + +Managed objects and their properties are defined in your project's `conf/managed.json` file. Note that the schema defined in this file is not a comprehensive list of all the properties that can be stored in the managed object repository. If you use a generic object mapping, you can create a managed object with any arbitrary property, and that property will be stored in the repository. For more information about explicit and generic object mappings, see xref:chap-repo.adoc#explicit-generic-mapping["Using Explicit or Generic Object Mapping With a JDBC Repository"]. + +This chapter describes how to work with the default managed object types and how to create new object types as required by your deployment. For more information about the OpenIDM object model, see xref:appendix-objects.adoc#appendix-objects["Data Models and Objects Reference"]. + +[#creating-modifying-managed-objects] +=== Creating and Modifying Managed Object Types + +If the managed object types provided in the default configuration are not sufficient for your deployment, you can create any number of new managed object types. + +The easiest way to create a new managed object type is to use the Admin UI, as follows: + +. Navigate to the Admin UI URL (`\https://localhost:8443/admin`) then select Configure > Managed Objects > New Managed Object. + +. Enter a name for the new managed object and, optionally, an icon that will be displayed for that object type in the UI. ++ +Click Save. + +. Select the Scripts tab and specify any scripts that should be applied on various events associated with that object type, for example, when an object of that type is created, updated or deleted. + +. Specify the schema for the object type, that is, the properties that make up the object, and any policies or restrictions that must be applied to the property values. + +Click the JSON button on the Schema tab to display the properties in JSON format. You can also create a new managed object type by adding its configuration, in JSON, to your project's `conf/managed.json` file. The following excerpt of the `managed.json` file shows the configuration of a "Phone" object, that was created through the UI. + +[source, javascript] +---- +{ + "name": "Phone", + "schema": { + "$schema": "http://forgerock.org/json-schema#", + "type": "object", + "properties": { + "brand": { + "description": "The supplier of the mobile phone", + "title": "Brand", + "viewable": true, + "searchable": true, + "userEditable": false, + "policies": [], + "returnByDefault": false, + "minLength": "", + "pattern": "", + "isVirtual": false, + "type": "string" + }, + "assetNumber": { + "description": "The asset tag number of the mobile device", + "title": "Asset Number", + "viewable": true, + "searchable": true, + "userEditable": false, + "policies": [], + "returnByDefault": false, + "minLength": "", + "pattern": "", + "isVirtual": false, + "type": "string" + }, + "model": { + "description": "The model number of the mobile device, such as 6 plus, Galaxy S4", + "title": "Model", + "viewable": true, + "searchable": false, + "userEditable": false, + "policies": [], + "returnByDefault": false, + "minLength": "", + "pattern": "", + "isVirtual": false, + "type": "string" + } + }, + "required": [], + "order": [ + "brand", + "assetNumber", + "model" + ] + } +} +---- +You can add any arbitrary properties to the schema of a new managed object type. A property definition typically includes the following fields: + +* `name` - the name of the property + +* `title` - the name of the property, in human-readable language, used to display the property in the UI + +* `description` - a description of the property + +* `viewable` - specifies whether this property is viewable in the object's profile in the UI). Boolean, `true` or `false` (`true` by default). + +* `searchable` - specifies whether this property can be searched in the UI. A searchable property is visible within the Managed Object data grid in the Self-Service UI. Note that for a property to be searchable in the UI, it __must be indexed__ in the repository configuration. For information on indexing properties in a repository, see xref:chap-repo.adoc#explicit-generic-mapping["Using Explicit or Generic Object Mapping With a JDBC Repository"]. ++ +Boolean, `true` or `false` (`false` by default). + +* `userEditable` - specifies whether users can edit the property value in the UI. This property applies in the context of the self-service UI, where users are able to edit certain properties of their own accounts. Boolean, `true` or `false` (`false` by default). + +* `minLength` - the minimum number of characters that the value of this property must have. + +* `pattern` - any specific pattern to which the value of the property must adhere. For example, a property whose value is a date might require a specific date format. + +* `policies` - any policy validation that must be applied to the property. For more information on managed object policies, see xref:chap-policies.adoc#configuring-default-policy["Configuring the Default Policy for Managed Objects"]. + +* `required` - specifies whether the property must be supplied when an object of this type is created. Boolean, `true` or `false`. + +* `type` - the data type for the property value; can be `String`, `Array`, `Boolean`, `Integer`, `Number`, `Object`, or `Resource Collection`. + +* `isVirtual` - specifies whether the property takes a static value, or whether its value is calculated "on the fly" as the result of a script. Boolean, `true` or `false`. + +* `returnByDefault` - for non-core attributes (virtual attributes and relationship fields), specifies whether the property will be returned in the results of a query on an object of this type __if it is not explicitly requested__. Virtual attributes and relationship fields are not returned by default. When configured in an array within a relationship, always set to `false` Boolean, `true` or `false`. + + + +[#working-with-managed-users] +=== Working with Managed Users + +User objects that are stored in OpenIDM's repository are referred to as __managed users__. For a JDBC repository, OpenIDM stores managed users in the `managedobjects` table. A second table, `managedobjectproperties`, serves as the index table. For an OrientDB repository, managed users are stored in the `managed_user` table. + +OpenIDM provides RESTful access to managed users, at the context path `/openidm/managed/user`. For more information, see xref:../install-guide/chap-install.adoc#first-steps-with-rest["Getting Started With the OpenIDM REST Interface"] in the __Installation Guide__. + + +[#working-with-groups] +=== Working With Managed Groups + +OpenIDM provides support for a managed `group` object. For a JDBC repository, OpenIDM stores managed groups with all other managed objects, in the `managedobjects` table, and uses the `managedobjectproperties` for indexing. For an OrientDB repository, managed groups are stored in the `managed_group` table. + +The managed group object is not provided by default. To use managed groups, add an object similar to the following to your `conf/managed.json` file: + +[source] +---- +{ + "name" : "group" +}, +---- +With this addition, OpenIDM provides RESTful access to managed groups, at the context path `/openidm/managed/group`. + +For an example of a deployment that uses managed groups, see xref:../samples-guide/chap-ldap-samples.adoc#more-sample-2d["Sample 2d - Synchronizing LDAP Groups"] in the __Samples Guide__. + + +[#working-with-managed-roles] +=== Working With Managed Roles + +OpenIDM supports two types of roles: + +* __Provisioning roles__ - used to specify how objects are provisioned to an external system. + +* __Authorization roles__ - used to specify the authorization rights of a managed object internally, within OpenIDM. + +Provisioning roles are always created as managed roles, at the context path `openidm/managed/role/role-name`. Provisioning roles are granted to managed users as values of the user's `roles` property. + +Authorization roles can be created either as managed roles (at the context path `openidm/managed/role/role-name`) or as internal roles (at the context path `openidm/repo/internal/role/role-name`). Authorization roles are granted to managed users as values of the user's `authzRoles` property. + +Both provisioning roles and authorization roles use the relationships mechanism to link the role to the managed object to which it applies. For more information about relationships between objects, see xref:#managing-relationships["Managing Relationships Between Objects"]. + +This section describes how to create and use __managed roles__, either managed provisioning roles, or managed authorization roles. For more information about authorization roles, and how OpenIDM controls authorization to its own endpoints, see xref:chap-auth.adoc#openidm-authorization["Authorization"]. + +__Managed roles__ are defined like any other managed object, and are granted to users through the __relationships__ mechanism. + +A managed role can be granted manually, as a static value of the user's `roles` or `authzRoles` attribute, or dynamically, as a result of a condition or script. For example, a user might be granted a role such as `sales-role` dynamically, if that user is in the `sales` organization. + +A managed user's `roles` and `authzRoles` attributes take an array of __references__ as a value, where the references point to the managed roles. For example, if user bjensen has been granted two provisioning roles (`employee` and `supervisor`), the value of bjensen's `roles` attribute would look something like the following: + +[source, javascript] +---- +"roles": [ + { + "_ref": "managed/role/employee", + "_refProperties": { + "_id": "c090818d-57fd-435c-b1b1-bb23f47eaf09", + "_rev": "1" + } + }, + { + "_ref": "managed/role/supervisor", + "_refProperties": { + "_id": "4961912a-e2df-411a-8c0f-8e63b62dbef6", + "_rev": "1" + } + } + ] +---- + +[IMPORTANT] +==== +The `_ref` property points to the ID of the managed role that has been granted to the user. This particular example uses a client-assigned ID that is the same as the role name, to make the example easier to understand. All other examples in this chapter use system-assigned IDs. In production, you should use system-assigned IDs for role objects. +==== +The following sections describe how to create, read, update, and delete managed roles, and how to grant roles to users. For information about how roles are used to provision users to external systems, see xref:#working-with-role-assignments["Working With Role Assignments"]. For a sample that demonstrates the basic CRUD operations on roles, see xref:../samples-guide/chap-roles-sample.adoc#chap-roles-sample["Roles Samples - Demonstrating the OpenIDM Roles Implementation"] in the __Samples Guide__. + +[#create-new-role] +==== Creating a Role + +The easiest way to create a new role is by using the Admin UI. Select Manage > Role and click New Role on the Role List page. Enter a name and description for the new role and click Save. + +Optionally, select Enable Condition to define a query filter that will allow this role to be granted to members dynamically. For more information, see xref:#granting-roles-dynamically["Granting Roles Dynamically"]. + +To create a managed role over REST, send a PUT or POST request to the `/openidm/managed/role` context path. The following example creates a managed role named `employee`: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "name" : "employee", + "description" : "Role granted to workers on the company payroll" + }' \ + "http://localhost:8080/openidm/managed/role?_action=create" +{ + "_id": "cedadaed-5774-4d65-b4a2-41d455ed524a", + "_rev": "1", + "name": "employee", + "description": "Role granted to workers on the company payroll" +} +---- +At this stage, the `employee` role has no corresponding __assignments__. Assignments are what enables the provisioning logic to the external system. Assignments are created and maintained as separate managed objects, and are referred to within role definitions. For more information about assignments, see xref:#working-with-role-assignments["Working With Role Assignments"]. + + +[#list-existing-roles] +==== Listing Existing Roles + +You can display a list of all configured managed roles over REST or by using the Admin UI. + +To list the managed roles in the Admin UI, select Manage > Role. + +To list the managed roles over REST, query the `openidm/managed/role` endpoint. The following example shows the `employee` role that you created in the previous section: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role?_queryFilter=true" +{ + "result": [ + { + "_id": "cedadaed-5774-4d65-b4a2-41d455ed524a", + "_rev": "1", + "name": "employee", + "description": "Role granted to workers on the company payroll" + } + ], +... +} +---- + + +[#granting-role-user] +==== Granting a Role to a User + +Roles are granted to users through the relationship mechanism. Relationships are essentially references from one managed object to another, in this case from a user object to a role object. For more information about relationships, see xref:#managing-relationships["Managing Relationships Between Objects"]. + +Roles can be granted manually or dynamically. +To grant a role manually, you must do one of the following: + +* Update the value of the user's `roles` property (if the role is a provisioning role) or `authzRoles` property (if the role is an authorization role) to reference the role. + +* Update the value of the role's `members` property to reference the user. + +Manual role grants are described further in xref:#granting-roles-manually["Granting Roles Manually"]. + +Dynamic role grants use the result of a condition or script to update a user's list of roles. Dynamic role grants are described in detail in xref:#granting-roles-dynamically["Granting Roles Dynamically"]. + +[#granting-roles-manually] +===== Granting Roles Manually + +To grant a role to a user manually, use the Admin UI or the REST interface as follows: +-- + +Using the Admin UI:: +Use one of the following UI methods to grant a role to a user: + +* Update the user entry: ++ + +. Select Manage > User and click on the user to whom you want to grant the role. + +. Select the Provisioning Roles tab and click Add Provisioning Roles. + +. Select the role from the dropdown list and click Add. + + +* Update the role entry: ++ + +. Select Manage > Role and click on the role that you want to grant. + +. Select the Role Members tab and click Add Role Members. + +. Select the user from the dropdown list and click Add. + + + +Over the REST interface:: +Use one of the following methods to grant a role to a user over REST: + +* Update the user to refer to the role. ++ +The following sample command grants the `employee` role (with ID `cedadaed-5774-4d65-b4a2-41d455ed524a`) to user scarter: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { + "operation": "add", + "field": "/roles/-", + "value": {"_ref" : "managed/role/cedadaed-5774-4d65-b4a2-41d455ed524a"} + } + ]' \ + "http://localhost:8080/openidm/managed/user/scarter" +{ + "_id": "scarter", + "_rev": "2", + "mail": "scarter@example.com", + "givenName": "Steven", + "sn": "Carter", + "description": "Created By XML1", + "userName": "scarter@example.com", + "telephoneNumber": "1234567", + "accountStatus": "active", + "effectiveRoles": [ + { + "_ref": "managed/role/cedadaed-5774-4d65-b4a2-41d455ed524a" + } + ], + "effectiveAssignments": [] +} +---- ++ +Note that scarter's `effectiveRoles` attribute has been updated with a reference to the new role. For more information about effective roles and effective assignments, see xref:#effective-roles-and-assignments["Understanding Effective Roles and Effective Assignments"]. + +* Update the role to refer to the user. ++ +The following sample command makes scarter a member of the `employee` role: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { + "operation": "add", + "field": "/members/-", + "value": {"_ref" : "managed/user/scarter"} + } + ]' \ + "http://localhost:8080/openidm/managed/role/cedadaed-5774-4d65-b4a2-41d455ed524a" +{ + "_id": "cedadaed-5774-4d65-b4a2-41d455ed524a", + "_rev": "2", + "name": "employee", + "description": "Role granted to workers on the company payroll" +} +---- ++ +Note that the `members` attribute of a role is not returned by default in the output. To show all members of a role, you must specifically request the relationship properties (`*_ref`) in your query. The following sample command lists the members of the `employee` role (currently only scarter): ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role/cedadaed-5774-4d65-b4a2-41d455ed524a?_fields=*_ref,name" + { + "_id": "cedadaed-5774-4d65-b4a2-41d455ed524a", + "_rev": "1", + "name": "employee", + "members": [ + { + "_ref": "managed/user/scarter", + "_refProperties": { + "_id": "98d22d75-7090-47f8-9608-01ff92b447a4", + "_rev": "1" + } + } + ], + "authzMembers": [], + "assignments": [] +} +---- + +* You can replace an existing role grant with a new one by using the `replace` operation in your patch request. The following command ++ +The following command replaces scarter's entire `roles` entry (that is, overwrites any existing roles) with a single entry, the reference to the `employee` role (ID `cedadaed-5774-4d65-b4a2-41d455ed524a`): ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { + "operation": "replace", + "field":"/roles", + "value":[ + {"_ref":"managed/role/cedadaed-5774-4d65-b4a2-41d455ed524a"} + ] + } + ]' \ + "http://localhost:8080/openidm/managed/user/scarter" +---- + + +-- + + +[#granting-roles-dynamically] +===== Granting Roles Dynamically + +The previous section showed how to grant roles to a user manually, by listing a reference to the role as a value of the user's `roles` attribute. OpenIDM also supports the following methods of granting a role __dynamically__: + +* Granting a role based on a condition, where that condition is expressed in a query filter in the role definition. If the condition is `true` for a particular member, that member is granted the role. + +* Using a custom script to define a more complex role granting strategy. + + +[#conditional-role-grants] +====== Granting Roles Based on a Condition + +A role that is granted based on a defined condition is called a __conditional role__. To create a conditional role, include a query filter in the role definition. + +To create a conditional role by using the Admin UI, select Condition on the role Details page, then define the query filter that will be used to assess the condition. In the following example, the role `fr-employee` will be granted only to those users who live in France (whose `country` property is set to `FR`): + +[#d0e8129] +image::images/conditional-role.png[] +To create a conditional role over REST, include the query filter as a value of the `condition` property in the role definition. The following command creates a role similar to the one created in the previous screen shot: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "name": "fr-employee", + "description": "Role granted to employees resident in France", + "condition": "/country eq \"FR\"" + }' \ + "http://localhost:8080/openidm/managed/role?_action=create" + { + "_id": "4b0a3e42-e5be-461b-a995-3e66c74551c1", + "_rev": "1", + "name": "fr-employee", + "description": "Role granted to employees resident in France", + "condition": "/country eq \"FR\"" +} +---- +When a conditional role is created or updated, OpenIDM automatically assesses all managed users, and recalculates the value of their `roles` property, if they qualify for that role. When a condition is removed from a role, that is, when the role becomes an unconditional role, all conditional grants removed. So, users who were granted the role based on the condition have that role removed from their `roles` property. + +[CAUTION] +==== +When a conditional role is defined in an existing data set, every user entry (including the mapped entries on remote systems) must be updated with the assignments implied by that conditional role. The time that it takes to create a new conditional role is impacted by the following items: + +* The number of managed users affected by the condition + +* The number of assignments related to the conditional role + +* The average time required to provision updates to all remote systems affected by those assignments + +In a data set with a very large number of users, creating a new conditional role can therefore incur a significant performance cost at the time of creation. Ideally, you should set up your conditional roles at the beginning of your deployment to avoid performance issues later. +==== + + +[#dynamic-role-scripts] +====== Granting Roles By Using Custom Scripts + +The easiest way to grant roles dynamically is to use conditional roles, as described in xref:#conditional-role-grants["Granting Roles Based on a Condition"]. If your deployment requires complex conditional logic that cannot be achieved with a query filter, you can create a custom script to grant the role, as follows: + +==== + +. Create a `roles` directory in your project's `script` directory and copy the default effective roles script to that new directory: ++ + +[source, console] +---- +$ mkdir project-dir/script/roles/ +$ cp /path/to/openidm/bin/defaults/script/roles/effectiveRoles.js \ + project-dir/script/roles/ +---- ++ +The new script will override the default effective roles script. + +. Modify the script to reference additional roles that have not been granted manually, or as the result of a conditional grant. The effective roles script calculates the grants that are in effect when the user is retrieved. ++ +For example, the following addition to the `effectiveRoles.js` script grants the roles `dynamic-role1` and `dynamic-role2` to all active users (managed user objects whose `accountStatus` value is `active`). This example assumes that you have already created the managed roles, `dynamic-role1` (with ID `d2e29d5f-0d74-4d04-bcfe-b1daf508ad7c`) and `dynamic-role2` (with ID `709fed03-897b-4ff0-8a59-6faaa34e3af6`, and their corresponding assignments: ++ + +[source, javascript] +---- +// This is the location to expand to dynamic roles, +// project role script return values can then be added via +// effectiveRoles = effectiveRoles.concat(dynamicRolesArray); + +if (object.accountStatus === 'active') { + effectiveRoles = effectiveRoles.concat([ + {"_ref": "managed/role/d2e29d5f-0d74-4d04-bcfe-b1daf508ad7c"}, + {"_ref": "managed/role/709fed03-897b-4ff0-8a59-6faaa34e3af6"} + ]); +} +---- + +==== + +[NOTE] +==== +For conditional roles, the user's `roles` property is updated if the user meets the condition. For custom scripted roles, the user's `effectiveRoles` property is calculated when the user is retrieved and includes the dynamic roles according to the custom script. +==== +If you make any of the following changes to a scripted role grant, you must perform a manual reconciliation of all affected users before assignment changes will take effect on an external system: + +* If you create a new scripted role grant. + +* If you change the definition of an existing scripted role grant. + +* If you change any of the assignment rules for a role that is granted by a custom script. + + + + + +[#roles-temporal-constraints] +==== Using Temporal Constraints to Restrict Effective Roles + +To restrict the period during which a role is effective, you can set a temporal constraint on the role itself, or on the role grant. A temporal constraint that is set on a role definition applies to all grants of that role. A temporal constraint that is set on a role grant enables you to specify the period that the role is valid __per user__. + +For example, you might want a role definition such as `contractors-2016` to apply to all contract employees __only__ for the year 2016. Or you might want a `contractors` role to apply to an individual user only during the duration of his contract of employment. + +The following sections describe how to set temporal constraints on role definitions, and on individual role grants. + +[#temporal-constraints-role-definition] +===== Adding a Temporal Constraint to a Role Definition + +When you create a role, you can include a temporal constraint in the role definition, which restricts the validity of the entire role, regardless of how that role is granted. Temporal constraints are expressed as a `duration` in ISO 8601 date and time format. For more information on this format, see link:https://en.wikipedia.org/wiki/ISO_8601#Durations[https://en.wikipedia.org/wiki/ISO_8601#Durations, window=\_blank]. + +To restrict the period during which a role is valid by using the Admin UI, select Temporal Constraint on the role Details page, then select the timezone and start and end dates for the required period. + +In the following example, the role `contractor` is effective from January 1st, 2016 to January 1st, 2017: + +[#d0e8300] +image::images/temporal-role.png[] +The following example adds a similar `contractor` role, over the REST interface: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "name" : "contractor", + "description" : "Role granted to contract workers for 2016", + "temporalConstraints" : [ + { + "duration" : "2016-01-01T00:00:00.000Z/2017-01-01T00:00:00.000Z" + } + ] + }' \ + "http://localhost:8080/openidm/managed/role?_action=create" +{ + "_id": "071283a8-0237-40a2-a31e-ceaa4d93c93d", + "_rev": "1", + "name": "contractor", + "description": "Role granted to contract workers for 2016", + "temporalConstraints": [ + { + "duration": "2016-01-01T00:00:00.000Z/2017-01-01T00:00:00.000Z" + } + ] +} +---- +The preceding example specifies the time zone as Coordinated Universal Time (UTC) by appending `Z` to the time. If no time zone information is provided, the time zone is assumed to be local time. To specify a different time zone, include an offset (from UTC) in the format `±hh:mm`. For example, a duration of `2016-01-01T00:00:00.000+04:00/2017-01-01T00:00:00.000+04:00` specifies a time zone that is four hours ahead of UTC. + +When the period defined by the constraint has ended, the role object remains in the repository but the effective roles script will not include the role in the list of effective roles for any user. + +The following example assumes that user scarter has been granted a role `contractor-april`. A temporal constraint has been included in the `contractor-april` definition that specifies that the role should be applicable only during the month of April 2016. At the end of this period, a query on scarter's entry shows that his `roles` property still includes the `contractor-april` role (with ID `3eb67be6-205b-483d-b36d-562b43a04ff8`), but his `effectiveRoles` property does not: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/scarter?_fields=_id,userName,roles,effectiveRoles" +{ + "_id": "scarter", + "_rev": "1", + "userName": "scarter@example.com", + "roles": [ + { + "_ref": "managed/role/3eb67be6-205b-483d-b36d-562b43a04ff8", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "257099f5-56e5-4ce0-8580-f0f4d4b93d93", + "_rev": "1" + } + } + ], + "effectiveRoles": [] +} +---- +In other words, the role is still in place but is no longer effective. + + +[#temporal-constraints-role-grant] +===== Adding a Temporal Constraint to a Role Grant + +To restrict the validity of a role for individual users, you can apply a temporal constraint at the grant level, rather than as part of the role definition. In this case, the temporal constraint is taken into account per user, when the user's effective roles are calculated. Temporal constraints that are defined at the grant level can be different for each user who is a member of that role. + +To restrict the period during which a role grant is valid by using the Admin UI, set a temporal constraint when you add the member to the role. + +For example, to specify that bjensen be added to a Contractor role only for the duration of her employment contract, select Manage > Role, click the Contractor role, and click Add Role Members. On the Add Role Members screen, select bjensen from the list, then enable the Temporal Constraint and specify the start and end date of her contract. + +To apply a temporal constraint to a grant over the REST interface, include the constraint as one of the `_refProperties` of the relationship between the user and the role. The following example assumes a `contractor` role, with ID `9321fd67-30d1-4104-934d-cfd0a22e8182`. The command adds user bjensen as a member of that role, with a temporal constraint that specifies that she be a member of the role only for one year, from January 1st, 2016 to January 1st, 2017: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { + "operation": "add", + "field": "/members/-", + "value": { + "_ref" : "managed/user/bjensen", + "_refProperties": { + "temporalConstraints": [{"duration": "2016-01-01T00:00:00.000Z/2017-01-01T00:00:00.000Z"}] + } + } + } + ]' \ + "http://localhost:8080/openidm/managed/role/9321fd67-30d1-4104-934d-cfd0a22e8182" +{ + "_id": "9321fd67-30d1-4104-934d-cfd0a22e8182", + "_rev": "2", + "name": "contractor", + "description": "Role for contract workers" +} +---- +A query on bjensen's roles property shows that the temporal constraint has been applied to this grant: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/bjensen/roles?_queryFilter=true" +{ + "result": [ + { + "_ref": "managed/role/9321fd67-30d1-4104-934d-cfd0a22e8182", + "_refProperties": { + "temporalConstraints": [ + { + "duration": "2016-01-01T00:00:00.000Z/2017-01-01T00:00:00.000Z" + } + ], + "_id": "84f5342c-cebe-4f0b-96c9-0267bf68a095", + "_rev": "1" + } + } + ], +... +} +---- + + + +[#querying-user-roles] +==== Querying a User's Manual and Conditional Roles + +The easiest way to check what roles have been granted to a user, either manually, or as the result of a condition, is to look at the user's entry in the Admin UI. Select Manage > User, click on the user whose roles you want to see, and select the Provisioning Roles tab. + +To obtain a similar list over the REST interface, you can query the user's `roles` property. The following sample query shows that scarter has been granted two roles - an `employee` role (with ID `6bf4701a-7579-43c4-8bb4-7fd6cac552a1`) and an `fr-employee` role (with ID `00561df0-1e7d-4c8a-9c1e-3b1096116903`). specifies : + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/scarter/roles?_queryFilter=true&_fields=_ref,_refProperties,name" +{ + "result": [ + { + "_ref": "managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "8417106e-c3ef-4f59-a482-4c92dbf00308", + "_rev": "2" + }, + "name": "employee" + }, + { + "_ref": "managed/role/00561df0-1e7d-4c8a-9c1e-3b1096116903", + "_refProperties": { + "_grantType": "conditional", + "_id": "e59ce7c3-46ce-492a-ba01-be27af731435", + "_rev": "1" + }, + "name": "fr-employee" + } + ], + ... +} +---- +Note that the `fr-employee` role has an additional reference property, `_grantType`. This property indicates __how__ the role was granted to the user. If there is no `_grantType`, the role was granted manually. + +Querying a user's roles in this way __does not__ return any roles that would be in effect as a result of a custom script, or of any temporal constraint applied to the role. To return a complete list of __all__ the roles in effect at a specific time, you need to query the user's `effectiveRoles` property, as follows: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/scarter?_fields=effectiveRoles" +---- + + +[#delete-role-user] +==== Deleting a User's Roles + +Roles that have been granted manually can be removed from a user's entry in two ways: + +* Update the value of the user's `roles` property (if the role is a provisioning role) or `authzRoles` property (if the role is an authorization role) to remove the reference to the role. + +* Update the value of the role's `members` property to remove the reference to that user. + +Both of these actions can be achieved by using the Admin UI, or over REST. +-- + +Using the Admin UI:: +Use one of the following methods to remove a user's roles: + +* Select Manage > User and click on the user whose role or roles you want to remove. ++ +Select the Provisioning Roles tab, select the role that you want to remove, and click Remove Selected Provisioning Roles. + +* Select Manage > Role and click on the role whose members you want to remove. ++ +Select the Role Members tab, select the member or members that that you want to remove, and click Remove Selected Role Members. + + +Over the REST interface:: +Use one of the following methods to remove a role grant from a user: + +* Delete the role from the user's `roles` property, including the reference ID (the ID of the relationship between the user and the role) in the delete request: ++ +The following sample command removes the `employee` role (with ID `6bf4701a-7579-43c4-8bb4-7fd6cac552a1`) from user scarter: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/user/scarter/roles/8417106e-c3ef-4f59-a482-4c92dbf00308" +{ + "_ref": "managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "8417106e-c3ef-4f59-a482-4c92dbf00308", + "_rev": "2" + } +} +---- + +* PATCH the user entry to remove the role from the array of roles, specifying the __value__ of the role object in the JSON payload. ++ + +[CAUTION] +====== +When you remove a role in this way, you must include the __entire object__ in the value, as shown in the following example: +====== ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "remove", + "field" : "/roles", + "value" : { + "_ref": "managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "8417106e-c3ef-4f59-a482-4c92dbf00308", + "_rev": "1" + } + } + } + ]' \ + "http://localhost:8080/openidm/managed/user/scarter" +{ + "_id": "scarter", + "_rev": "3", + "mail": "scarter@example.com", + "givenName": "Steven", + "sn": "Carter", + "description": "Created By XML1", + "userName": "scarter@example.com", + "telephoneNumber": "1234567", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +* Delete the user from the role's `members` property, including the reference ID (the ID of the relationship between the user and the role) in the delete request. ++ +The following example first queries the members of the `employee` role, to obtain the ID of the relationship, then removes bjensen's membership from that role: ++ + +[source, console] +---- +$ url \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1/members?_queryFilter=true" +{ + "result": [ + { + "_ref": "managed/user/bjensen", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "3c047f39-a9a3-4030-8d0c-bcd1fadb1d3d", + "_rev": "3" + } + } + ], +... +} +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1/members/3c047f39-a9a3-4030-8d0c-bcd1fadb1d3d" +{ + "_ref": "managed/user/bjensen", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "3c047f39-a9a3-4030-8d0c-bcd1fadb1d3d", + "_rev": "3" + } +} +---- + + +-- + +[NOTE] +==== +Roles that have been granted as the result of a condition can only be removed when the condition is changed or removed, or when the role itself is deleted. +==== + + +[#delete-role-definition] +==== Deleting a Role Definition + +You can delete a managed provisioning or authorization role by using the Admin UI, or over the REST interface. + +To delete a role by using the Admin UI, select Manage > Role, select the role you want to remove, and click Delete. + +To delete a role over the REST interface, simply delete that managed object. The following command deletes the `employee` role created in the previous section: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1" +{ + "_id": "6bf4701a-7579-43c4-8bb4-7fd6cac552a1", + "_rev": "1", + "name": "employee", + "description": "Role granted to workers on the company payroll" +} +---- + +[NOTE] +==== +You cannot delete a role if it is currently granted to one or more users. If you attempt to delete a role that is granted to a user (either over the REST interface, or by using the Admin UI), OpenIDM returns an error. The following command indicates an attempt to remove the `employee` role while it is still granted to user scarter: +==== + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/role/6bf4701a-7579-43c4-8bb4-7fd6cac552a1" +{ + "code":409, + "reason":"Conflict", + "message":"Cannot delete a role that is currently granted" + } +---- + + +[#working-with-role-assignments] +==== Working With Role Assignments + +__Authorization roles__ control access to OpenIDM itself. __Provisioning roles__ define rules for how attribute values are updated on external systems. These rules are configured through __assignments__ that are attached to a provisioning role definition. The purpose of an assignment is to provision an attribute or set of attributes, based on an object's role membership. + +The synchronization mapping configuration between two resources (defined in the `sync.json` file) provides the basic account provisioning logic (how an account is mapped from a source to a target system). Role assignments provide additional provisioning logic that is not covered in the basic mapping configuration. The attributes and values that are updated by using assignments might include group membership, access to specific external resources, and so on. A group of assignments can collectively represent a __role__. + +Assignment objects are created, updated and deleted like any other managed object, and are attached to a role by using the relationships mechanism, in much the same way as a role is granted to a user. Assignment are stored in the repository and are accessible at the context path `/openidm/managed/assignment`. + +This section describes how to manipulate managed assignments over the REST interface, and by using the Admin UI. When you have created an assignment, and attached it to a role definition, all user objects that reference that role definition will, as a result, reference the corresponding assignment in their `effectiveAssignments` attribute. + +[#creating-an-assignment] +===== Creating an Assignment + +The easiest way to create an assignment is by using the Admin UI, as follows: + +. Select Manage > Assignment and click New Assignment on the Assignment List page. + +. Enter a name and description for the new assignment, and select the mapping to which the assignment should apply. The mapping indicates the target resource, that is, the resource on which the attributes specified in the assignment will be adjusted. + +. Click Add Assignment. + +. Select the Attributes tab and select the attribute or attributes whose values will be adjusted by this assignment. ++ + +* If a regular text field appears, specify what the value of the attribute should be, when this assignment is applied. + +* If an Item button appears, you can specify a managed object type, such as an object, relationship, or string. + +* If a Properties button appears, you can specify additional information such as an array of role references, as described in xref:#working-with-managed-roles["Working With Managed Roles"]. + + +. Select the assignment operation from the dropdown list: ++ + +* `Merge With Target` - the attribute value will be added to any existing values for that attribute. This operation merges the existing value of the target object attribute with the value(s) from the assignment. If duplicate values are found (for attributes that take a list as a value), each value is included only once in the resulting target. This assignment operation is used only with complex attribute values like arrays and objects, and does not work with strings or numbers. (Property: `mergeWithTarget`.) + +* `Replace Target` - the attribute value will overwrite any existing values for that attribute. The value from the assignment becomes the authoritative source for the attribute. (Property: `replaceTarget`.) + ++ +Select the unassignment operation from the dropdown list. You can set the unassignment operation to one of the following: ++ + +* `Remove From Target` - the attribute value is removed from the system object when the user is no longer a member of the role, or when the assignment itself is removed from the role definition. (Property: `removeFromTarget`.) + +* `No Operation` - removing the assignment from the user's `effectiveAssignments` has no effect on the current state of the attribute in the system object. (Property: `noOp`.) + + +. Optionally, click the Events tab to specify any scriptable events associated with this assignment. ++ +The assignment and unassignment operations described in the previous step operate at the __attribute level__. That is, you specify what should happen with each attribute affected by the assignment when the assignment is applied to a user, or removed from a user. ++ +The scriptable __On assignment__ and __On unassignment__ events operate at the __assignment level__, rather than the attribute level. You define scripts here to apply additional logic or operations that should be performed when a user (or other object) receives or loses an entire assignment. This logic can be anything that is not restricted to an operation on a single attribute. + +. Click the Roles tab to attach this assignment to an existing role definition. + +To create a new assignment over REST, send a PUT or POST request to the `/openidm/managed/assignment` context path. + +The following example creates a new managed assignment named `employee`. The JSON payload in this example shows the following: + +* The assignment is applied for the mapping `managedUser_systemLdapAccounts`, so attributes will be updated on the external LDAP system specified in this mapping. + +* The name of the attribute on the external system whose value will be set is `employeeType` and its value will be set to `Employee`. + +* When the assignment is applied during a sync operation, the attribute value `Employee` will be added to any existing values for that attribute. When the assignment is removed (if the role is deleted, or if the managed user is no longer a member of that role), the attribute value `Employee` will be removed from the values of that attribute. + + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "name" : "employee", + "description": "Assignment for employees.", + "mapping" : "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + } + ] + }' \ + "http://localhost:8080/openidm/managed/assignment?_action=create" +{ + "_id": "2fb3aa12-109f-431c-bdb7-e42213747700", + "_rev": "1", + "name": "employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ] +} +---- +Note that at this stage, the assignment is not linked to any role, so no user can make use of the assignment. You must add the assignment to a role, as described in the following section. + + +[#adding-assignment-to-role] +===== Adding an Assignment to a Role + +When you have created a managed role, and a managed assignment, you reference the assignment from the role, in much the same way as a user references a role. + +You can update a role definition to include one or more assignments, either by using the Admin UI, or over the REST interface. +-- + +Using the Admin UI:: + +. Select Manage > Role and click on the role to which you want to add an assignment. + +. Select the Managed Assignments tab and click Add Managed Assignments. + +. Select the assignment that you want to add to the role and click Add. + + +Over the REST interface:: +Update the role definition to include a reference to the ID of the assignment in the `assignments` property of the role. The following sample command adds the `employee` assignment (with ID `2fb3aa12-109f-431c-bdb7-e42213747700`) to an existing `employee` role (whose ID is `59a8cc01-bac3-4bae-8012-f639d002ad8c`): ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/assignments/-", + "value" : { "_ref": "managed/assignment/2fb3aa12-109f-431c-bdb7-e42213747700" } + } + ]' \ + "http://localhost:8080/openidm/managed/role/59a8cc01-bac3-4bae-8012-f639d002ad8c" +{ + "_id": "59a8cc01-bac3-4bae-8012-f639d002ad8c", + "_rev": "3", + "name": "employee", + "description": "Role granted to workers on the company payroll" +} +---- ++ +To check that the assignment was added successfully, you can query the `assignments` property of the role: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role/59a8cc01-bac3-4bae-8012-f639d002ad8c/assignments?_queryFilter=true&_fields=_ref,_refProperties,name" + +{ + "result": [ + { + "_ref": "managed/assignment/2fb3aa12-109f-431c-bdb7-e42213747700", + "_refProperties": { + "_id": "686b328a-e2bd-4e48-be25-4a4e12f3b431", + "_rev": "4" + }, + "name": "employee" + } + ], +... +} +---- ++ +Note that the role's `assignments` property now references the assignment that you created in the previous step. + +-- +To remove an assignment from a role definition, remove the reference to the assignment from the role's `assignments` property. + + +[#delete-managed-assignment] +===== Deleting an Assignment + +You can delete an assignment by using the Admin UI, or over the REST interface. + +To delete an assignment by using the Admin UI, select Manage > Assignment, select the assignment you want to remove, and click Delete. + +To delete an assignment over the REST interface, simply delete that object. The following command deletes the `employee` assignment created in the previous section: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/assignment/2fb3aa12-109f-431c-bdb7-e42213747700" + { + "_id": "2fb3aa12-109f-431c-bdb7-e42213747700", + "_rev": "1", + "name": "employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ] +} +---- + +[NOTE] +==== +You __can__ delete an assignment, even if it is referenced by a managed role. When the assignment is removed, any users to whom the corresponding roles were granted will no longer have that assignment in their list of `effectiveAssignments`. For more information about effective roles and effective assignments, see xref:#effective-roles-and-assignments["Understanding Effective Roles and Effective Assignments"]. +==== + + + +[#effective-roles-and-assignments] +==== Understanding Effective Roles and Effective Assignments + +__Effective roles__ and __effective assignments__ are virtual properties of a user object. Their values are calculated __on the fly__ by the `openidm/bin/defaults/script/roles/effectiveRoles.js` and `openidm/bin/defaults/script/roles/effectiveAssignments.js` scripts. These scripts are triggered when a managed user is retrieved. + +The following excerpt of a `managed.json` file shows how these two virtual properties are constructed for each managed user object: + +[source, javascript] +---- +"effectiveRoles" : { + "type" : "array", + "title" : "Effective Roles", + "viewable" : false, + "returnByDefault" : true, + "isVirtual" : true, + "onRetrieve" : { + "type" : "text/javascript", + "source" : "require('roles/effectiveRoles').calculateEffectiveRoles(object, 'roles');" + }, + "items" : { + "type" : "object" + } +}, +"effectiveAssignments" : { + "type" : "array", + "title" : "Effective Assignments", + "viewable" : false, + "returnByDefault" : true, + "isVirtual" : true, + "onRetrieve" : { + "type" : "text/javascript", + "file" : "roles/effectiveAssignments.js", + "effectiveRolesPropName" : "effectiveRoles" + }, + "items" : { + "type" : "object" + } +}, +---- +When a role references an assignment, and a user references the role, that user automatically references the assignment in its list of effective assignments. + +The `effectiveRoles.js` script uses the `roles` attribute of a user entry to calculate the grants (manual or conditional) that are currently in effect at the time of retrieval, based on temporal constraints or other custom scripted logic. + +The `effectiveAssignments.js` script uses the virtual `effectiveRoles` attribute to calculate that user's effective assignments. The synchronization engine reads the calculated value of the `effectiveAssignments` attribute when it processes the user. The target system is updated according to the configured `assignmentOperation` for each assignment. + +Do not change the default `effectiveRoles.js` and `effectiveAssignments.js` scripts. If you need to change the logic that calculates `effectiveRoles` and `effectiveAssignments`, create your own custom script and include a reference to it in your project's `conf/managed.json` file. For more information about using custom scripts, see xref:appendix-scripting.adoc#appendix-scripting["Scripting Reference"]. + +When a user entry is retrieved, OpenIDM calculates the `effectiveRoles` and `effectiveAssignments` for that user based on the current value of the user's `roles` property, and on any roles that might be granted dynamically through a custom script. The previous set of examples showed the creation of a role `employee` that referenced an assignment `employee` and was granted to user bjensen. Querying that user entry would show the following effective roles and effective assignments: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/bjensen?_fields=userName,roles,effectiveRoles,effectiveAssignments" +{ + "_id": "bjensen", + "_rev": "2", + "userName": "bjensen@example.com", + "roles": [ + { + "_ref": "managed/role/59a8cc01-bac3-4bae-8012-f639d002ad8c", + "_refProperties": { + "temporalConstraints": [], + "_grantType": "", + "_id": "881f0b96-06e9-4af4-b86b-aba4ee15e4ef", + "_rev": "2" + } + } + ], + "effectiveRoles": [ + { + "_ref": "managed/role/59a8cc01-bac3-4bae-8012-f639d002ad8c" + } + ], + "effectiveAssignments": [ + { + "name": "employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ], + "_id": "4606245c-9412-4f1f-af0c-2b06852dedb8", + "_rev": "2" + } + ] +} +---- +In this example, synchronizing the managed/user repository with the external LDAP system defined in the mapping should populate user bjensen's `employeeType` attribute in LDAP with the value `employee`. + + +[#managed-role-script-hooks] +==== Managed Role Script Hooks + +Like any other managed object, a role has script hooks that enable you to configure role behavior. The default role definition in `conf/managed.json` includes the following script hooks: + +[source, javascript] +---- +{ + "name" : "role", + "onDelete" : { + "type" : "text/javascript", + "file" : "roles/onDelete-roles.js" + }, + "onSync" : { + "type" : "text/javascript", + "source" : "require('roles/onSync-roles').syncUsersOfRoles(resourceName, oldObject, newObject, ['members']);" + }, + "onCreate" : { + "type" : "text/javascript", + "source" : "require('roles/conditionalRoles').roleCreate(object);" + }, + "onUpdate" : { + "type" : "text/javascript", + "source" : "require('roles/conditionalRoles').roleUpdate(oldObject, object);" + }, + "postCreate" : { + "type" : "text/javascript", + "file" : "roles/postOperation-roles.js" + }, + "postUpdate" : { + "type" : "text/javascript", + "file" : "roles/postOperation-roles.js" + }, + "postDelete" : { + "type" : "text/javascript", + "file" : "roles/postOperation-roles.js" + }, +... +---- +When a role is deleted, the `onDelete` script hook calls the `bin/default/script/roles/onDelete-roles.js` script. + +When a role is synchronized, the `onSync` hook causes a synchronization operation on all managed objects that reference the role. + +When a __conditional role__ is created or updated, the `onCreate` and `onUpdate` script hooks force an update on all managed users affected by the conditional role. + +Directly after a role is created, updated or deleted, the `postCreate`, `postUpdate`, and `postDelete` hooks call the `bin/default/script/roles/postOperation-roles.js` script. Depending on when this script is called, it either creates or removes the scheduled jobs required to manage temporal constraints on roles. + + + +[#managing-relationships] +=== Managing Relationships Between Objects + +OpenIDM enables you to define __relationships__ between two managed objects. Managed roles are implemented using relationship objects, but you can create a variety of relationship objects, as required by your deployment. + +[#defining-a-relationship-type] +==== Defining a Relationship Type + +Relationships are defined in your project's managed object configuration file (`conf/managed.json`). By default, OpenIDM provides a relationship named `manager`, that enables you to configure a management relationship between two managed users. The `manager` relationship is a good example from which to understand how relationships work. + +The default `manager` relationship is configured as follows: + +[source, javascript] +---- +"manager" : { + "type" : "relationship", + "returnByDefault" : false, + "description" : "", + "title" : "Manager", + "viewable" : true, + "searchable" : false, + "properties" : { + "_ref" : { "type" : "string" }, + "_refProperties": { + "type": "object", + "properties": { + "_id": { "type": "string" } + } + } +}, +---- +-- +All relationships have the following configurable properties: + +`type` (string):: +The object type. Must be `relationship` for a relationship object. + +`returnByDefault` (boolean `true, false`):: +Specifies whether the relationship should be returned in the result of a read or search query on the managed object that has the relationship. If included in an array, always set this property to `false`. By default, relationships are not returned, unless explicitly requested. + +`description` (string, optional):: +An optional string that provides additional information about the relationship object. + +`title` (string):: +Used by the UI to refer to the relationship. + +`viewable` (boolean, `true, false`):: +Specifies whether the relationship is visible as a field in the UI. The default value is `true`. + +`searchable` (boolean, `true, false`):: +Specifies whether values of the relationship can be searched, in the UI. For example, if you set this property to `true` for the `manager` relationship, a user will be able to search for managed user entries using the `manager` field as a filter. + +`_ref` (JSON object):: +Specifies how the relationship between two managed objects is referenced. + ++ +In the relationship definition, the value of this property is `{ "type" : "string" }`. In a managed user entry, the value of the `_ref` property is the reference to the other resource. The `_ref` property is described in more detail in xref:#establishing-relationships-between-objects["Establishing a Relationship Between Two Objects"]. + +`_refProperties` (JSON object):: +Specifies any required properties from the relationship that should be included in the managed object. The `_refProperties` field includes a unique ID (`_id`) and the revision (`_rev`) of the object. `_refProperties` can also contain arbitrary fields to support metadata within the relationship. + +-- + + +[#establishing-relationships-between-objects] +==== Establishing a Relationship Between Two Objects + +When you have defined a relationship __type__, (such as the `manager` relationship, described in the previous section), you can reference that relationship from a managed user, using the `_ref` property. + +For example, imagine that you are creating a new user, psmith, and that psmith's manager will be bjensen. You would add psmith's user entry, and __reference__ bjensen's entry with the `_ref` property, as follows: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "If-None-Match: *" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "sn":"Smith", + "userName":"psmith", + "givenName":"Patricia", + "displayName":"Patti Smith", + "description" : "psmith - new user", + "mail" : "psmith@example.com", + "phoneNumber" : "0831245986", + "password" : "Passw0rd", + "manager" : {"_ref" : "managed/user/bjensen"} + }' \ +"http://localhost:8080/openidm/managed/user/psmith" +{ + "_id": "psmith", + "_rev": "1", + "sn": "Smith", + "userName": "psmith", + "givenName": "Patricia", + "displayName": "Patti Smith", + "description": "psmith - new user", + "mail": "psmith@example.com", + "phoneNumber": "0831245986", + "accountStatus": "active", + "effectiveRoles": null, + "effectiveAssignments": [], + "roles": [] +} +---- +Note that the relationship information is not returned by default in the command-line output. + +Any change to a relationship triggers a synchronization operation on any other managed objects that are referenced by the relationship. For example, OpenIDM maintains referential integrity by deleting the relationship reference, if the object referred to by that relationship is deleted. In our example, if bjensen's user entry is deleted, the corresponding reference in psmith's `manager` property is removed. + + +[#relationships-validation] +==== Validating Relationships Between Objects + +Optionally, you can specify that a relationship between two objects must be validated when the relationship is created. For example, you can indicate that a user cannot reference a role, if that role does not exist. + +When you create a new relationship type, validation is disabled by default as it entails a query to the relationship that can be expensive, if it is not required. To configure validation of a referenced relationship, set `"validate": true` in the object configuration (in `managed.json`). The `managed.json` files provided with OpenIDM enable validation for the following relationships: + +* For user objects ‒ roles, managers, and reports + +* For role objects ‒ members and assignments + +* For assignment objects ‒ roles + +The following configuration of the `manager` relationship enables validation, and prevents a user from referencing a manager that has not already been created: + +[source, javascript] +---- +"manager" : { + "type" : "relationship", + ... + "validate" : true, +---- + + +[#reverse-relationships] +==== Working With Bi-Directional Relationships + +In some cases, it is useful to define a relationship between two objects __in both directions__. For example, a relationship between a user and his manager might indicate a __reverse relationship__ between the manager and her direct report. Reverse relationships are particularly useful in querying. For example, you might want to query jdoe's user entry to discover who his manager is, __or__ query bjensen's user entry to discover all the users who report to bjensen. + +A reverse relationship is declared in the managed object configuration (`conf/managed.json`). Consider the following sample excerpt of the default managed object configuration: + +[source, javascript] +---- +"roles" : { + "description" : "", + "title" : "Provisioning Roles", + ... + "type" : "array", + "items" : { + "type" : "relationship", + "validate": false, + "reverseRelationship" : true, + "reversePropertyName" : "members", + ... +---- +The `roles` property is a `relationship`. So, you can __refer__ to a managed user's roles by referencing the role definition. However, the roles property is also a reverse relationship (`"reverseRelationship" : true`) which means that you can list all users that reference that role. In other words, you can list all `members` of the role. The `members` property is therefore the `reversePropertyName`. + + +[#viewing-relationships-over-rest] +==== Viewing Relationships Over REST + +By default, information about relationships is not returned as the result of a GET request on a managed object. You must explicitly include the relationship property in the request, for example: + +[source, console] +---- +$ curl + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/psmith?_fields=manager" +{ + "_id": "psmith", + "_rev": "1", + "manager": { + "_ref": "managed/user/bjensen", + "_refProperties": { + "_id": "e15779ad-be54-4a1c-b643-133dd9bb2e99", + "_rev": "1" + } + } +} +---- +To obtain more information about the referenced object (psmith's manager, in this case), you can include additional fields from the referenced object in the query, using the syntax `object/property` (for a simple string value) or `object/*/property` (for an array of values). + +The following example returns the email address and contact number for psmith's manager: + +[source, console] +---- +$ curl + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/psmith?_fields=manager/mail,manager/phoneNumber" +{ + "_id": "psmith", + "_rev": "1", + "phoneNumber": "1234567", + "manager": { + "_ref": "managed/user/bjensen", + "_refProperties": { + "_id": "e15779ad-be54-4a1c-b643-133dd9bb2e99", + "_rev": "1" + }, + "mail": "bjensen@example.com", + "phoneNumber": "1234567" + } +} +---- +You can query all the relationships associated with a managed object by querying the reference (`*_ref`) property of the object. For example, the following query shows all the objects that are referenced by psmith's entry: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/psmith?_fields=*_ref" +{ + "_id": "psmith", + "_rev": "1", + "roles": [], + "authzRoles": [ + { + "_ref": "repo/internal/role/openidm-authorized", + "_refProperties": { + "_id": "8e7b2c97-dfa8-4eec-a95b-b40b710d443d", + "_rev": "1" + } + } + ], + "manager": { + "_ref": "managed/user/bjensen", + "_refProperties": { + "_id": "3a246327-a972-4576-b6a6-7126df780029", + "_rev": "1" + } + } +} +---- + + +[#viewing-relationships-ui] +==== Viewing Relationships in Graph Form + +OpenIDM provides a relationship graph widget that gives a visual display of the relationships between objects. + +The relationship graph widget is not displayed on any dashboard by default. You can add it as follows: + +. Log into the Admin UI. + +. Select Dashboards, and choose the dashboard to which you want to add the widget. ++ +For more information about managing dashboards in the UI, see xref:chap-ui.adoc#ui-admin-new-dashboard["Creating and Modifying Dashboards"]. + +. Select Add Widgets. In the Add Widgets window, scroll to the Identity Relationships widget, and click Add. + +. Select Close to exit the Add Widgets window. + +. On the dashboard, scroll down to the Identity Relationships widget. Select the vertical ellipses > Settings to configure the widget. + +. Choose the Widget Size, then enter the object for which you want to display relationships such as `user` and the search property for that object, such as `userName`. ++ +If you want to include an additional level of relationships in the graph, select Display sub-relationships. In a traditional organization, this option will display a user's manager, along with all users with that same manager. + +. Click Save. + +When you have configured the Identity Relationships widget, enter the user whose relationships you want to search. + +The following graph shows all of bjensen's relationships. The graph shows bjensen's manager (emacheke) and all other users who are direct reports of emacheke. + +image::images/relationships-graph.png[] +Select or deselect the Data Types on the left of the screen to control how much information is displayed. + +Select and move the graph for a better view. Double-click on any user in the graph to view that user's profile. + + + +[#managed-objects-scripts] +=== Running Scripts on Managed Objects + +OpenIDM provides a number of __hooks__ that enable you to manipulate managed objects using scripts. These scripts can be triggered during various stages of the lifecycle of the managed object, and are defined in the managed objects configuration file (`managed.json`). + +The scripts can be triggered when a managed object is created (onCreate), updated (onUpdate), retrieved (onRetrieve), deleted (onDelete), validated (onValidate), or stored in the repository (onStore). A script can also be triggered when a change to a managed object triggers an implicit synchronization operation (onSync). + +In addition, OpenIDM supports the use of post-action scripts for managed objects, including after the creation of an object is complete (postCreate), after the update of an object is complete (postUpdate), and after the deletion of an object (postDelete). + +The following sample extract of a `managed.json` file runs a script to calculate the effective assignments of a managed object, whenever that object is retrieved from the repository: + +[source, javascript] +---- +"effectiveAssignments" : { + "type" : "array", + "title" : "Effective Assignments", + "viewable" : false, + "returnByDefault" : true, + "isVirtual" : true, + "onRetrieve" : { + "type" : "text/javascript", + "file" : "roles/effectiveAssignments.js", + "effectiveRolesPropName" : "effectiveRoles" + }, + "items" : { + "type" : "object" + } +}, +---- + + +[#encoding-attribute-values] +=== Encoding Attribute Values + +OpenIDM supports two methods of encoding attribute values for managed objects - reversible encryption and the use of salted hashing algorithms. Attribute values that might be encoded include passwords, authentication questions, credit card numbers, and social security numbers. If passwords are already encoded on the external resource, they are generally excluded from the synchronization process. For more information, see xref:chap-passwords.adoc#chap-passwords["Managing Passwords"]. + +You configure attribute value encoding, per schema property, in the managed object configuration (in your project's `conf/managed.json` file). The following sections show how to use reversible encryption and salted hash algorithms to encode attribute values. + +[#encoding-encryption] +==== Encoding Attribute Values With Reversible Encryption + +The following excerpt of a `managed.json` file shows a managed object configuration that encrypts and decrypts the `password` attribute using the default symmetric key: + +[source, javascript] +---- +{ + "objects" : [ + { + "name" : "user", + ... + "schema" : { + ... + "properties" : { + ... + "password" : { + "title" : "Password", + ... + "encryption" : { + "key" : "openidm-sym-default" + }, + "scope" : "private", + ... + } + ] +} +---- + +[TIP] +==== +To configure encryption of properties by using the Admin UI: + +. Select Configure > Managed Objects, and click on the object type whose property values you want to encrypt (for example User). + +. On the Properties tab, select the property whose value should be encrypted and select the Encrypt checkbox. + +==== +For information about encrypting attribute values from the command-line, see xref:chap-cli.adoc#cli-encrypt["Using the encrypt Subcommand"]. + + +[#encoding-salted-hash] +==== Encoding Attribute Values by Using Salted Hash Algorithms + +To encode attribute values with salted hash algorithms, add the `secureHash` property to the attribute definition, and specify the algorithm that should be used to hash the value. OpenIDM supports the following hash algorithms: +[none] +* `MD5` +* `SHA-1` +* `SHA-256` +* `SHA-384` +* `SHA-512` +The following excerpt of a `managed.json` file shows a managed object configuration that hashes the values of the `password` attribute using the `SHA-1` algorithm: + +[source, javascript] +---- +{ + "objects" : [ + { + "name" : "user", + ... + "schema" : { + ... + "properties" : { + ... + "password" : { + "title" : "Password", + ... + "secureHash" : { + "algorithm" : "SHA-1" + }, + "scope" : "private", + ... + } + ] +} +---- + +[TIP] +==== +To configure hashing of properties by using the Admin UI: + +. Select Configure > Managed Objects, and click on the object type whose property values you want to hash (for example User). + +. On the Properties tab, select the property whose value must be hashed and select the Hash checkbox. + +. Select the algorithm that should be used to hash the property value. ++ +OpenIDM supports the following hash algorithms: ++ +[none] +* `MD5` +* `SHA-1` +* `SHA-256` +* `SHA-384` +* `SHA-512` + +==== +For information about hashing attribute values from the command-line, see xref:chap-cli.adoc#cli-secure-hash["Using the secureHash Subcommand"]. + + + +[#restricting-http-access] +=== Restricting HTTP Access to Sensitive Data + +You can protect specific sensitive managed data by marking the corresponding properties as `private`. Private data, whether it is encrypted or not, is not accessible over the REST interface. Properties that are marked as private are removed from an object when that object is retrieved over REST. + +To mark a property as private, set its `scope` to `private` in the `conf/managed.json` file. + +The following extract of the `managed.json` file shows how HTTP access is prevented on the `password` and `securityAnswer` properties: + +[source, javascript] +---- +{ + "objects": [ + { + "name": "user", + "schema": { + "id" : "http://jsonschema.net", + "title" : "User", + ... + "properties": { + ... + { + "name": "securityAnswer", + "encryption": { + "key": "openidm-sym-default" + }, + "scope" : "private" + }, + { + "name": "password", + "encryption": { + "key": "openidm-sym-default" + }' + "scope" : "private" + } + }, + ... + } + ] +} +---- + +[TIP] +==== +To configure private properties by using the Admin UI: + +. Select Configure > Managed Objects, and click on the object type whose property values you want to make private (for example User). + +. On the Properties tab, select the property that must be private and select the Private checkbox. + +==== +A potential caveat with using private properties is that private properties are __removed__ if an object is updated by using an HTTP `PUT` request. A `PUT` request replaces the entire object in the repository. Because properties that are marked as private are ignored in HTTP requests, these properties are effectively removed from the object when the update is done. To work around this limitation, do not use `PUT` requests if you have configured private properties. Instead, use a `PATCH` request to update only those properties that need to be changed. + +For example, to update the `givenName` of user jdoe, you could run the following command: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--request POST \ +--data '[ + { + "operation":"replace", + "field":"/givenName", + "value":"Jon" + } +]' \ +"http://localhost:8080/openidm/managed/user?_action=patch&_queryId=for-userName&uid=jdoe" +---- + +[NOTE] +==== +The filtering of private data applies only to direct HTTP read and query calls on managed objects. No automatic filtering is done for internal callers, and the data that these callers choose to expose. +==== + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-workflow.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-workflow.adoc new file mode 100644 index 000000000..1a6510d93 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-workflow.adoc @@ -0,0 +1,1256 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-workflow] +== Integrating Business Processes and Workflows + +Key to any identity management solution is the ability to provide workflow-driven provisioning activities, whether for self-service actions such as requests for entitlements, roles or resources, running sunrise or sunset processes, handling approvals with escalations, or performing maintenance. + +OpenIDM provides an embedded workflow and business process engine based on Activiti and the Business Process Model and Notation (BPMN) 2.0 standard. + +More information about Activiti and the Activiti project can be found at link:http://www.activiti.org[http://www.activiti.org, window=\_blank]. + +This chapter describes how to configure the Activiti engine, and how to manage workflow tasks and processes over the REST interface. You can also manage workflows in the Admin UI by selecting Manage > Workflow and then selecting Tasks or Processes. + +For a number of samples that demonstrate workflow-driven provisioning, see xref:../samples-guide/chap-workflow-samples.adoc#chap-workflow-samples["Workflow Samples"] in the __Samples Guide__. + +[#about-bmpm-2-activiti] +=== BPMN 2.0 and the Activiti Tools + +Business Process Model and Notation 2.0 is the result of consensus among Business Process Management (BPM) system vendors. The link:http://omg.org/[Object Management Group, window=\_blank] (OMG) has developed and maintained the link:http://www.omg.org/spec/BPMN/[BPMN, window=\_blank] standard since 2004. + +The first version of the BPMN specification focused only on graphical notation, and quickly became popular with the business analyst audience. BPMN 1.x defines how constructs such as human tasks, executable scripts, and automated decisions are visualized in a vendor-neutral, standard way. The second version of BPMN extends that focus to include execution semantics, and a common exchange format. Thus, BPMN 2.0 process definition models can be exchanged not only between different graphical editors, but can also be executed as is on any BPMN 2.0-compliant engine, such as the engine embedded in OpenIDM. + +Using BPMN 2.0, you can add artifacts describing workflow and business process behavior to OpenIDM for provisioning and other purposes. For example, you can craft the actual artifacts defining business processes and workflow in a text editor, or using a special Eclipse plugin. The Eclipse plugin provides visual design capabilities, simplifying packaging and deployment of the artifact to OpenIDM. For instructions on installing Activiti Eclipse BPMN 2.0 Designer, see the corresponding link:http://docs.alfresco.com/4.1/tasks/wf-install-activiti-designer.html[Alfresco documentation, window=\_blank]. + +Also, read the Activiti __User Guide__ section covering link:http://www.activiti.org/userguide/#bpmnConstructs[BPMN 2.0 Constructs, window=\_blank], which describes in detail the graphical notations and XML representations for events, flows, gateways, tasks, and process constructs. + +With the latest version of Activiti, JavaScript tasks can be added to workflow definitions. However, OpenIDM functions cannot be called from a JavaScript task in a workflow. Therefore, you can use JavaScript for non-OpenIDM workflow tasks, but you must use the `activiti:expression` construct to call OpenIDM functions. + + +[#setting-up-activiti] +=== Setting Up Activiti Integration With OpenIDM + +OpenIDM embeds an Activiti Process Engine that is started in the OpenIDM OSGi container. + +After OpenIDM has been installed (as described in xref:../install-guide/chap-install.adoc#chap-install["Installing OpenIDM Services"] in the __Installation Guide__), start OpenIDM, and run the `scr list` command in the OSGi console to check that the workflow bundle is active. + +[source, console] +---- +-> OpenIDM ready +scr list + Id State Name +... +[ 39] [active ] org.forgerock.openidm.workflow +... +---- +OpenIDM reads workflow definitions from the `/path/to/openidm/workflow` directory. To test workflow integration, at least one workflow definition must exist in this directory. + +A sample workflow (`example.bpmn20.xml`) is provided in the `/path/to/openidm/samples/misc` directory. Copy this workflow to the `/path/to/openidm/workflow` directory to test the workflow integration. + +[source, console] +---- +$ cd /path/to/openidm +$ cp samples/misc/example.bpmn20.xml workflow/ +---- +Verify the workflow integration by using the REST API. The following REST call lists the defined workflows: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processdefinition?_queryId=query-all-ids" +---- +The sample workflow definition that you copied in the previous step is named `osgiProcess`. The result of the preceding REST call therefore includes output similar to the following: + +[source, console] +---- +{ +... + "result":[ + { + ... + "key": "osgiProcess", + ... + "name":"Osgi process", + ... + "_id":"osgiProcess:1:3", + ... + } + ] +} +---- +The `osgiProcess` workflow calls OpenIDM, queries the available workflow definitions from Activiti, then prints the list of workflow definitions to the OpenIDM logs. Invoke the `osgiProcess` workflow with the following REST call: + +[source, console] +---- +$ 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 '{"_key":"osgiProcess"}' \ + "https://localhost:8443/openidm/workflow/processinstance?_action=create" +---- +The workflow prints the list of workflow definitions to the OSGi console. With the default sample, you should see something like this: + +[source, console] +---- +script task using resolver: [ + pagedResultsCookie:null, + remainingPagedResults:-1, + result:[ + [ + tenantId:, + candidateStarterGroupIdExpressions:[], + candidateStarterUserIdExpressions:[], + participantProcess:null, + processDiagramResourceName:null, + historyLevel:null, + hasStartFormKey:false, + laneSets:[], + version:1, _id:osgiProcess:1:3, + description:null, + name:Osgi process, + executionListeners:[:], + key:osgiProcess, + resourceName:OSGI-INF/activiti/example.bpmn20.xml, + ioSpecification:null, + taskDefinitions:null, + suspensionState:1, + deploymentId:1, + properties:[:], + startFormHandler:null, + suspended:false, + variables:null, + _rev:1, + revisionNext:2, + category:Examples, + eventSupport:[:], + graphicalNotationDefined:false + ] + ] +] +script task using expression resolver: [ + pagedResultsCookie:null, + remainingPagedResults:-1, + result:[ + [ + tenantId:, + candidateStarterGroupIdExpressions:[], + ... +] +---- + +[#configuring-activiti-engine] +==== Configuring the Activiti Engine + +The OpenIDM Activiti module is configured in a file named `conf/workflow.json`. To disable workflows, do not include this file in your project's `conf/` subdirectory. In the default OpenIDM installation, the `workflow.json` file is shown here: + +[source, javascript] +---- +{ + "useDataSource" : "default", + "workflowDirectory" : "&{launcher.project.location}/workflow" +} +---- +-- + +`useDataSource`:: +The Activiti data source is enabled by default. + +`workflowDirectory`:: +This directory specifies the location in which OpenIDM expects to find workflow processes. By default, OpenIDM looks for workflow process in the `workflow` folder of the current project. + +-- +There are several additional configuration properties for the Activiti module. A sample `workflow.json` file that includes all configurable properties, is provided in `samples/misc`. To configure an Activiti engine beyond the default configuration, edit this sample file and copy it to your project's `conf/` subdirectory. + +The sample `workflow.json` file contains the following configuration: + +[source, javascript] +---- +{ + "location" : "remote", + "engine" : { + "url" : "http://localhost:9090/openidm-workflow-remote-4.5.1-20", + "username" : "youractivitiuser", + "password" : "youractivitipassword" + }, + "mail" : { + "host" : "yourserver.smtp.com", + "port" : 587, + "username" : "yourusername", + "password" : "yourpassword", + "starttls" : true + }, + "history" : "audit" +} +---- + +[WARNING] +==== +Activiti remote integration is not currently supported. +==== +These properties have the following meaning: + +* `location`: Specifies the remote Activiti engine; it's considered "remote" even if located on the same host as OpenIDM. + +* `engine`: Specifies the details of the Activiti engine. ++ + +** `url`: Notes the URL to access the remote Activiti engine. + +** `username`: The user name of the account that connects to the remote Activiti engine. + +** `password`: The password of the account that connects to the remote Activiti engine. + + +* `mail`: Specifies the details of the mail server that Activiti will use to send email notifications. By default, Activiti uses the mail server `localhost:25`. To specify a different mail server, enter the details of the mail server here. ++ + +** `host`: The host of the mail server. + +** `port`: The port number of the mail server. + +** `username`: The user name of the account that connects to the mail server. + +** `password`: The password for the user specified above. + +** `startTLS`: Whether startTLS should be used to secure the connection. + + +* `history`. Determines the history level that should be used for the Activiti engine. For more information, see link:#activiti-history-level[Configuring the Activiti History Level]. + + +[#activiti-history-level] +===== Configuring the Activiti History Level + +The Activiti history level determines how much historical information is retained when workflows are executed. You can configure the history level by setting the `history` property in the `workflow.json` file, for example: + +[source] +---- +"history" : "audit" +---- +The following history levels can be configured: + +* `none`. No history archiving is done. This level results in the best performance for workflow execution, but no historical information is available. + +* `activity`. Archives all process instances and activity instances. No details are archived. + +* `audit`. This is the default level. All process instances, activity instances and submitted form properties are archived so that all user interaction through forms is traceable and can be audited. + +* `full`. This is the highest level of history archiving and has the greatest performance impact. This history level stores all the information that is stored for the `audit` level, as well as any process variable updates. + + + + +[#defining-activiti-workflows] +==== Defining Activiti Workflows + +The following section outlines the process to follow when you create an Activiti workflow for OpenIDM. Before you start creating workflows, you must configure the Activiti engine, as described in link:#configuring-activiti-engine[Configuring the Activiti Engine]. + +==== + +. Define your workflow in a text file, either using an editor, such as Activiti Eclipse BPMN 2.0 Designer, or a simple text editor. + +. Package the workflow definition file as a `.bar` file (Business Archive File). If you are using Eclipse to define the workflow, a `.bar` file is created when you select "Create deployment artifacts". A `.bar` file is essentially the same as a `.zip` file, but with the `.bar` extension. + +. Copy the `.bar` file to the `openidm/workflow` directory. + +. Invoke the workflow using a script (in `openidm/script/`) or directly using the REST interface. For more information, see xref:#invoking-activiti-workflows["Invoking Activiti Workflows"]. ++ +You can also schedule the workflow to be invoked repeatedly, or at a future time. For more information, see xref:chap-scheduler-conf.adoc#chap-scheduler-conf["Scheduling Tasks and Events"]. + +==== + + +[#invoking-activiti-workflows] +==== Invoking Activiti Workflows + +You can invoke workflows and business processes from any trigger point within OpenIDM, including reacting to situations discovered during reconciliation. Workflows can be invoked from script files, using the `openidm.create()` function, or directly from the REST interface. + +The following sample script extract shows how to invoke a workflow from a script file: + +[source, javascript] +---- +/* + * Calling 'myWorkflow' workflow + */ + +var params = { + "_key": "myWorkflow" +}; + +openidm.create('workflow/processinstance', null, params); +---- +The `null` in this example indicates that you do not want to specify an ID as part of the create call. For more information, see xref:appendix-scripting.adoc#function-create["openidm.create(resourceName, newResourceId, content, params, fields)"]. + +You can invoke the same workflow from the REST interface by sending the following REST call to OpenIDM: + +[source, console] +---- +$ 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 '{"_key":"myWorkflow"}' \ + "https://localhost:8443/openidm/workflow/processinstance?_action=create" +---- +There are two ways in which you can specify the workflow definition that is used when a new workflow instance is started. + +* `_key` specifies the `id` attribute of the workflow process definition, for example: ++ + +[source, javascript] +---- + +---- ++ +If there is more than one workflow definition with the same `_key` parameter, the latest deployed version of the workflow definition is invoked. + +* `_processDefinitionId` specifies the ID that is generated by the Activiti Process Engine when a workflow definition is deployed, for example: ++ + +[source, javascript] +---- +"sendNotificationProcess:1:104"; +---- ++ +To obtain the `processDefinitionId`, query the available workflows, for example: ++ + +[source, javascript] +---- +{ + "result": [ + { + "name": "Process Start Auto Generated Task Auto Generated", + "_id": "ProcessSAGTAG:1:728" + }, + { + "name": "Process Start Auto Generated Task Empty", + "_id": "ProcessSAGTE:1:725" + }, + ... +---- ++ +If you specify a `_key` and a `_processDefinitionId`, the `_processDefinitionId` is used because it is more precise. + +Use the optional `_businessKey` parameter to add specific business logic information to the workflow when it is invoked. For example, the following workflow invocation assigns the workflow a business key of `"newOrder"`. This business key can later be used to query "newOrder" processes. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{"_key":"myWorkflow", "_businessKey":"newOrder"}' \ + "https://localhost:8443/openidm/workflow/processinstance?_action=create" +---- +Access to workflows is based on OpenIDM roles, and is configured in your project's `conf/process-access.json` file. For more information, see xref:chap-ui.adoc#ui-managing-workflows["Managing Workflows From the Self-Service UI"]. + + +[#querying-activiti-workflows] +==== Querying Activiti Workflows + +The Activiti implementation supports filtered queries that enable you to query the running process instances and tasks, based on specific query parameters. To perform a filtered query send a GET request to the `workflow/processinstance` context path, including the query in the URL. + +For example, the following query returns all process instances with the business key `"newOrder"`, as invoked in the previous example. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance?_queryId=filtered-query&processInstanceBusinessKey=newOrder" +---- +Any Activiti properties can be queried using the same notation, for example, `processDefinitionId=managedUserApproval:1:6405`. The query syntax applies to all queries with `_queryId=filtered-query`. The following query returns all process instances that were started by the user `openidm-admin`: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance?_queryId=filtered-query&startUserId=openidm-admin" +---- +You can also query process instances based on the value of any process instance variable, by prefixing the variable name with `var-`. For example: + +[source, console] +---- +var-processvariablename=processvariablevalue +---- + + + +[#activiti-custom-templates] +=== Using Custom Templates for Activiti Workflows + +The embedded Activiti engine is integrated with the default user interface. For simple workflows, you can use the standard Activiti form properties, and have the UI render the corresponding generic forms automatically. If you require a more complex form template, (including input validation, rich input field types, complex CSS, and so forth) you must define a custom form template. +There are two ways in which you can define custom form templates for your workflows: + +* Create an HTML template, and refer to that template in the workflow definition. ++ +This is the recommended method of creating custom form templates. To refer to the HTML template in the workflow definition, use the `activiti:formKey` attribute, for example `activiti:formKey="nUCStartForm.xhtml"`. ++ +The HTML file must be deployed as part of the workflow definition. Create a .zip file that contains the HTML template and the workflow definition file. Rename the .zip file with a .bar extension. ++ +For a sample workflow that uses external, referenced form templates, see `samples/usecase/workflow/newUserCreate.bpmn20.xml`. The HTML templates, and the corresponding .bar file are included in that directory. + +* Use an embedded template within the workflow definition. ++ +This method is not ideal, because the HTML code must be escaped, and is difficult to read, edit, or maintain, as a result. Also, sections of HTML code will most likely need to be duplicated if your workflow includes multiple task stages. However, you might want to use this method if your form is small, not too complex and you do not want to bother with creating a separate HTML file and .bar deployment. + + + +[#workflows-REST] +=== Managing Workflows Over the REST Interface + +In addition to the queries described previously, the following examples show the context paths that are exposed for managing workflows over the REST interface. The example output is based on the sample workflow that is provided in `openidm/samples/sample9`. +====openidm/workflow/processdefinition + + +* List the available workflow definitions: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processdefinition?_queryId=query-all-ids" +{ + "result" : [ { + "tenantId" : "", + "candidateStarterGroupIdExpressions" : [ ], + "candidateStarterUserIdExpressions" : [ ], + "participantProcess" : null, + "processDiagramResourceName" : null, + "historyLevel" : null, + "hasStartFormKey" : false, + "laneSets" : [ ], + "version" : 1, + "_id" : "managedUserApproval:1:3", + "description" : null, + "name" : "Managed User Approval Workflow", + "executionListeners" : { }, + "key" : "managedUserApproval", + "resourceName" : "OSGI-INF/activiti/managedUserApproval.bpmn20.xml", + "ioSpecification" : null, + "taskDefinitions" : null, + "suspensionState" : 1, + "deploymentId" : "1", + "properties" : { }, + "startFormHandler" : null, + "suspended" : false, + "variables" : null, + "_rev" : 1, + "revisionNext" : 2, + "category" : "Examples", + "eventSupport" : { }, + "graphicalNotationDefined" : false + } ], + "resultCount" : 1, + "pagedResultsCookie" : null, + "remainingPagedResults" : -1 +} +---- + +* List the workflow definitions, based on certain filter criteria: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processdefinition?_queryId=filtered-query&category=Examples" +{ + "result": [ + { + ... + "name": "Managed User Approval Workflow", + "_id": "managedUserApproval:1:3", + ... + "category" : "Examples", + ... + } + ] +} +---- + +====openidm/workflow/processdefinition/{id} + + +* Obtain detailed information for a process definition, based on the ID. You can determine the ID by querying all the available process definitions, as described in the first example in this section. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processdefinition/managedUserApproval:1:3" +{ + "tenantId" : "", + "candidateStarterGroupIdExpressions" : [ ], + "candidateStarterUserIdExpressions" : [ ], + "participantProcess" : null, + "processDiagramResourceName" : null, + "historyLevel" : null, + "hasStartFormKey" : false, + "laneSets" : [ ], + "version" : 1, + "formProperties" : [ ], + "_id" : "managedUserApproval:1:3", + "description" : null, + "name" : "Managed User Approval Workflow", + "executionListeners" : { + "end" : [ { } ] + }, + "key" : "managedUserApproval", + "resourceName" : "OSGI-INF/activiti/managedUserApproval.bpmn20.xml", + "ioSpecification" : null, + "taskDefinitions" : { + "evaluateRequest" : { + "assigneeExpression" : { + "expressionText" : "openidm-admin" + }, + "candidateGroupIdExpressions" : [ ], + "candidateUserIdExpressions" : [ ], + "categoryExpression" : null, + "descriptionExpression" : null, + "dueDateExpression" : null, + "key" : "evaluateRequest", + "nameExpression" : { + "expressionText" : "Evaluate request" + }, + "ownerExpression" : null, + "priorityExpression" : null, + "taskFormHandler" : { + "deploymentId" : "1", + "formKey" : null, + "formPropertyHandlers" : [ { + "defaultExpression" : null, + "id" : "requesterName", + "name" : "Requester's name", + "readable" : true, + "required" : false, + "type" : null, + "variableExpression" : { + "expressionText" : "${sourceId}" + }, + "variableName" : null, + "writable" : false + }, { + "defaultExpression" : null, + "id" : "requestApproved", + "name" : "Do you approve the request?", + "readable" : true, + "required" : true, + "type" : { + "name" : "enum", + "values" : { + "true" : "Yes", + "false" : "No" + } + }, + "variableExpression" : null, + "variableName" : null, + "writable" : true + } ] + }, + "taskListeners" : { + "assignment" : [ { } ], + "create" : [ { } ] + } + } + }, + "suspensionState" : 1, + "deploymentId" : "1", + "properties" : { + "documentation" : null + }, + "startFormHandler" : { + "deploymentId" : "1", + "formKey" : null, + "formPropertyHandlers" : [ ] + }, + "suspended" : false, + "variables" : { }, + "_rev" : 2, + "revisionNext" : 3, + "category" : "Examples", + "eventSupport" : { }, + "graphicalNotationDefined" : false +} +---- + +* Delete a workflow process definition, based on its ID. Note that you cannot delete a process definition if there are currently running instances of that process definition. ++ +OpenIDM picks up workflow definitions from the files located in the `/path/to/openidm/workflow` directory. If you delete the workflow definition (`.xml` file) from this directory, the OSGI bundle is deleted. However, deleting this file does not remove the workflow definition from the Activiti engine. You must therefore delete the definition over REST, as shown in the following example. ++ +Note that, although there is only one representation of a workflow definition in the file system, there might be several versions of the same definition in Activiti. If you want to delete redundant process definitions, delete the definition over REST, __making sure that you do not delete the latest version__. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "If-Match: *" \ + --request DELETE \ + "https://localhost:8443/openidm/workflow/processdefinition/managedUserApproval:1:3" +---- ++ +The delete request returns the contents of the deleted workflow definition. + +====openidm/workflow/processinstance + + +* Start a workflow process instance. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --data '{"_key":"managedUserApproval"}' \ + --request POST \ + "https://localhost:8443/openidm/workflow/processinstance?_action=create" +{ + "_id" : "4", + "processInstanceId" : "4", + "status" : "suspended", + "businessKey" : null, + "processDefinitionId" : "managedUserApproval:1:3" +} +---- + +* Obtain the list of running workflows (process instances). The query returns a list of IDs. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance?_queryId=query-all-ids" + +{ + "result" : [ { + "tenantId" : "", + "businessKey" : null, + "queryVariables" : null, + "durationInMillis" : null, + "processVariables" : { }, + "endTime" : null, + "superProcessInstanceId" : null, + "startActivityId" : "start", + "startTime" : "2014-04-25T09:54:30.035+02:00", + "startUserId" : "openidm-admin", + "_id" : "4", + "endActivityId" : null, + "processInstanceId" : "4", + "processDefinitionId" : "managedUserApproval:1:3", + "deleteReason" : null + } ], + "resultCount" : 1, + "pagedResultsCookie" : null, + "remainingPagedResults" : -1 +} +---- + +* Obtain the list of running workflows based on specific filter criteria. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance?_queryId=filtered-query&businessKey=myBusinessKey" +---- + +====openidm/workflow/processinstance/{id} + + +* Obtain the details of the specified process instance. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance/4" +{ + "tenantId" : "", + "businessKey" : null, + "queryVariables" : null, + "durationInMillis" : null, + "processVariables" : { }, + "endTime" : null, + "superProcessInstanceId" : null, + "startActivityId" : "start", + "startTime" : "2014-05-12T20:56:25.415+02:00", + "startUserId" : "openidm-admin", + "_id" : "4", + "endActivityId" : null, + "processInstanceId" : "4", + "processDefinitionId" : "managedUserApproval:1:3", + "deleteReason" : null +} +---- + +* Stop the specified process instance. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "https://localhost:8443/openidm/workflow/processinstance/4" +{ + "deleteReason": null, + "processDefinitionId": "managedUserApproval:1:3", + "processInstanceId": "4", + "endActivityId": null, + "_id": "4", + "startUserId": "openidm-admin", + "startTime": "2014-06-18T10:33:40.955+02:00", + "tenantId": "", + "businessKey": null, + "queryVariables": null, + "durationInMillis": null, + "processVariables": {}, + "endTime": null, + "superProcessInstanceId": null, + "startActivityId": "start" +} +---- ++ +The delete request returns the contents of the deleted process instance. + +====openidm/workflow/processinstance/history + + +* List the running and completed workflows (process instances). ++ +The following query returns two process instances - one that has completed (`"endActivityId": "end"`) and one that is still running (`"endActivityId": null`): ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance/history?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "12", + "businessKey": null, + "deleteReason": null, + "durationInMillis": 465287, + "endActivityId": "end", + "endTime": "2015-07-28T14:43:53.374+02:00", + "processDefinitionId": "newUserCreate:1:11", + "processInstanceId": "12", + "processVariables": {}, + "queryVariables": null, + "startActivityId": "start", + "startTime": "2015-07-28T14:36:08.087+02:00", + "startUserId": "user.1", + "superProcessInstanceId": null, + "tenantId": "", + "processDefinitionResourceName": "User onboarding process" + }, + { + "_id": "65", + "businessKey": null, + "deleteReason": null, + "durationInMillis": null, + "endActivityId": null, + "endTime": null, + "processDefinitionId": "newUserCreate:1:11", + "processInstanceId": "65", + "processVariables": {}, + "queryVariables": null, + "startActivityId": "start", + "startTime": "2015-07-28T15:36:20.187+02:00", + "startUserId": "user.0", + "superProcessInstanceId": null, + "tenantId": "", + "processDefinitionResourceName": "User onboarding process" + } + ], + "resultCount": 2, + "pagedResultsCookie": null, + "remainingPagedResults": -1 +} +---- + +* Obtain the list of running and completed workflows, based on specific filter criteria. ++ +The following command returns the running and completed workflows that were launched by `user.0`. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance/history?_queryId=filtered-query&startUserId=user.0" +{ + "result": [ + { + "_id": "65", + "businessKey": null, + "deleteReason": null, + "durationInMillis": null, + "endActivityId": null, + "endTime": null, + "processDefinitionId": "newUserCreate:1:11", + "processInstanceId": "65", + "processVariables": {}, + "queryVariables": null, + "startActivityId": "start", + "startTime": "2015-07-28T15:36:20.187+02:00", + "startUserId": "user.0", + "superProcessInstanceId": null, + "tenantId": "", + "processDefinitionResourceName": "User onboarding process" + } + ], + "resultCount": 1, + "pagedResultsCookie": null, + "remainingPagedResults": -1 +} +---- ++ +For large result sets, you can use the `_sortKeys` parameter with a `filtered-query` to order search results by one or more fields. You can prefix a `-` character to the field name to specify that results should be returned in descending order, rather than ascending order. ++ +The following query orders results according to their `startTime`. The `-` character in this case indicates that results should be sorted in reverse order, that is, with the most recent results returned first. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processinstance/history?_queryId=filtered-query&_sortKeys=-startTime" +{ + "result": [ + { + "_id": "104", + "businessKey": null, + "deleteReason": null, + "durationInMillis": null, + "endActivityId": null, + "endTime": null, + "processDefinitionId": "newUserCreate:1:11", + "processInstanceId": "104", + "processVariables": {}, + "queryVariables": null, + "startActivityId": "start", + "startTime": "2015-07-28T16:33:37.834+02:00", + "startUserId": "user.0", + "superProcessInstanceId": null, + "tenantId": "", + "processDefinitionResourceName": "User onboarding process" + }, + { + "_id": "65", + "businessKey": null, + "deleteReason": null, + "durationInMillis": 3738013, + "endActivityId": "end", + "endTime": "2015-07-28T16:38:38.200+02:00", + "processDefinitionId": "newUserCreate:1:11", + "processInstanceId": "65", + "processVariables": {}, + "queryVariables": null, + "startActivityId": "start", + "startTime": "2015-07-28T15:36:20.187+02:00", + "startUserId": "user.0", + "superProcessInstanceId": null, + "tenantId": "", + "processDefinitionResourceName": "User onboarding process" + }, + { + "_id": "12", + "businessKey": null, + "deleteReason": null, + "durationInMillis": 465287, + "endActivityId": "end", + "endTime": "2015-07-28T14:43:53.374+02:00", + "processDefinitionId": "newUserCreate:1:11", + "processInstanceId": "12", + "processVariables": {}, + "queryVariables": null, + "startActivityId": "start", + "startTime": "2015-07-28T14:36:08.087+02:00", + "startUserId": "user.1", + "superProcessInstanceId": null, + "tenantId": "", + "processDefinitionResourceName": "User onboarding process" + } + ], + "resultCount": 3, + "pagedResultsCookie": null, + "remainingPagedResults": -1 +} +---- ++ + +[CAUTION] +==== +The Activiti engine treats certain property values as __strings__, regardless of their actual data type. This might result in results being returned in an order that is different to what you might expect. For example, if you wanted to sort the following results by their `_id` field, `"88", "45", "101"`, you would expect them to be returned in the order `"45", "88", "101"`. Because Activiti treats IDs as strings, rather than numbers, they would be returned in the order `"101", "45", "88"`. +==== + +====openidm/workflow/processdefinition/{id}/taskdefinition + + +* Query the list of tasks defined for a specific process definition. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processdefinition/managedUserApproval:1:3/taskdefinition?_queryId=query-all-ids" +{ + "result" : [ { + "taskCandidateGroup" : [ ], + "ownerExpression" : null, + "assignee" : { + "expressionText" : "openidm-admin" + }, + "categoryExpression" : null, + "taskListeners" : { + "assignment" : [ { } ], + "create" : [ { } ] + }, + "formProperties" : { + "deploymentId" : "1", + "formKey" : null, + "formPropertyHandlers" : [ { + "_id" : "requesterName", + "defaultExpression" : null, + "name" : "Requester's name", + "readable" : true, + "required" : false, + "type" : null, + "variableExpression" : { + "expressionText" : "${sourceId}" + }, + "variableName" : null, + "writable" : false + }, { + "_id" : "requestApproved", + "defaultExpression" : null, + "name" : "Do you approve the request?", + "readable" : true, + "required" : true, + "type" : { + "name" : "enum", + "values" : { + "true" : "Yes", + "false" : "No" + } + }, + "variableExpression" : null, + "variableName" : null, + "writable" : true + } ] + }, + "taskCandidateUser" : [ ], + "formResourceKey" : null, + "_id" : "evaluateRequest", + "priority" : null, + "descriptionExpression" : null, + "name" : { + "expressionText" : "Evaluate request" + }, + "dueDate" : null + } ], + "resultCount" : 1, + "pagedResultsCookie" : null, + "remainingPagedResults" : -1 +} +---- + +* Query a task definition based on the process definition ID and the task name (`taskDefinitionKey`). For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/workflow/processdefinition/managedUserApproval:1:3/taskdefinition/evaluateRequest" +{ + "taskCandidateGroup" : [ ], + "ownerExpression" : null, + "formProperties" : { + "deploymentId" : "1", + "formKey" : null, + "formPropertyHandlers" : [ { + "_id" : "requesterName", + "defaultExpression" : null, + "name" : "Requester's name", + "readable" : true, + "required" : false, + "type" : null, + "variableExpression" : { + "expressionText" : "${sourceId}" + }, + "variableName" : null, + "writable" : false + }, { + "_id" : "requestApproved", + "defaultExpression" : null, + "name" : "Do you approve the request?", + "readable" : true, + "required" : true, + "type" : { + "name" : "enum", + "values" : { + "true" : "Yes", + "false" : "No" + } + }, + "variableExpression" : null, + "variableName" : null, + "writable" : true + } ] + }, + "taskCandidateUser" : [ ], + "_id" : "evaluateRequest", + "priority" : null, + "name" : { + "expressionText" : "Evaluate request" + }, + "descriptionExpression" : null, + "categoryExpression" : null, + "assignee" : { + "expressionText" : "openidm-admin" + }, + "taskListeners" : { + "assignment" : [ { } ], + "create" : [ { } ] + }, + "dueDate" : null +} +---- + +====openidm/workflow/taskinstance + + +* Query all running task instances. For example: ++ + +[source, console] +---- +$ 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" +{ + "result" : [ { + "tenantId" : "", + "createTime" : "2014-05-12T21:17:10.054+02:00", + "executionId" : "10", + "delegationStateString" : null, + "processVariables" : { }, + "_id" : "15", + "processInstanceId" : "10", + "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" : 1, + "pagedResultsCookie" : null, + "remainingPagedResults" : -1 +} +---- + +* Query task instances based on candidate users or candidate groups. For example: ++ + +[source, console] +---- +$ 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=filtered-query&taskCandidateUser=manager1" +---- ++ +or ++ + +[source, console] +---- +$ 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=filtered-query&taskCandidateGroup=management" +---- ++ +Note that you can include both users and groups in the same query. + +====openidm/workflow/taskinstance/{id} + + +* Obtain detailed information for a running task, based on the task ID. For example: ++ + +[source, console] +---- +$ 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/15" +{ + "dueDate": null, + "processDefinitionId": "managedUserApproval:1:3", + "owner": null, + "taskDefinitionKey": "evaluateRequest", + "name": "Evaluate request", +... +---- + +* Update task-related data stored in the Activiti workflow engine. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "If-Match : *" \ + --request PUT \ + --data '{"description":"Evaluate the new managed user request"}' \ + "https://localhost:8443/openidm/workflow/taskinstance/15" +---- + +* Complete the specified task. The variables required by the task are provided in the request body. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{"requestApproved":"true"}' \ + "https://localhost:8443/openidm/workflow/taskinstance/15?_action=complete" +---- + +* Claim the specified task. A user who claims a task has that task inserted into his list of pending tasks. The ID of the user who claims the task is provided in the request body. For example: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{"userId":"manager1"}' \ + "https://localhost:8443/openidm/workflow/taskinstance/15?_action=claim" +---- + + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/index.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/index.adoc new file mode 100644 index 000000000..279e22a80 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/index.adoc @@ -0,0 +1,69 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + += Integrator's Guide +:doctype: book +:toc: +:authors: Anders Askåsen, Paul Bryan, Mark Craig, Andi Egloff, Laszlo Hordos, Matthias Tristl, Lana Frost, Mike Jang, Daly Chikhaoui +:copyright: Copyright 2011-2017 ForgeRock AS. +:copyright: Portions Copyright 2024 3A Systems LLC. + +:imagesdir: ../ +:figure-caption!: +:example-caption!: +:table-caption!: +[abstract] +Guide to configuring and integrating OpenIDM into identity management solutions. OpenIDM identity management software offers flexible, open source services for automating management of the identity life cycle. + +include::./preface.adoc[] +include::./chap-overview.adoc[] +include::./chap-services.adoc[] +include::./chap-cli.adoc[] +include::./chap-ui.adoc[] +include::./chap-repo.adoc[] +include::./chap-configuration.adoc[] +include::./chap-data.adoc[] +include::./chap-users-groups-roles.adoc[] +include::./chap-policies.adoc[] +include::./chap-logs.adoc[] +include::./chap-resource-conf.adoc[] +include::./chap-synchronization.adoc[] +include::./chap-scripting.adoc[] +include::./chap-scheduler-conf.adoc[] +include::./chap-passwords.adoc[] +include::./chap-auth.adoc[] +include::./chap-security.adoc[] +include::./chap-workflow.adoc[] +include::./chap-auditing.adoc[] +include::./chap-cluster.adoc[] +include::./chap-mail.adoc[] +include::./chap-external-rest.adoc[] +include::./chap-best-practices.adoc[] +include::./chap-troubleshooting.adoc[] +include::./chap-advanced.adoc[] +include::./appendix-file-layout.adoc[] +include::./appendix-ports-used.adoc[] +include::./appendix-objects.adoc[] +include::./appendix-synchronization.adoc[] +include::./appendix-rest.adoc[] +include::./appendix-scripting.adoc[] +include::./appendix-router.adoc[] +include::./appendix-jetty.adoc[] +include::./appendix-auth-modules.adoc[] +include::./appendix-audit.adoc[] +include::./appendix-interface-stability.adoc[] +include::./openidm-glossary.adoc[] diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/openidm-glossary.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/openidm-glossary.adoc new file mode 100644 index 000000000..36d1e00ec --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/openidm-glossary.adoc @@ -0,0 +1,80 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[glossary] +[#openidm-glossary] +== OpenIDM Glossary + + +correlation query:: +A correlation query specifies an expression that matches existing entries in a source repository to one or more entries on a target repository. While a correlation query may be built with a script, it is __not__ a correlation script. + ++ +As noted in xref:../integrators-guide/chap-synchronization.adoc#correlation["Correlating Source Objects With Existing Target Objects"], you can set up a query definition, such as`_queryId`,`_queryFilter`, or`_queryExpression`, possibly with the help of a`linkQualifier`. + +correlation script:: +A correlation script matches existing entries in a source repository, and returns the IDs of one or more matching entries on a target repository. While it skips the intermediate step associated with a`correlation query`, a correlation script can be relatively complex, based on the operations of the script. + +entitlement:: +An entitlement is a collection of attributes that can be added to a user entry via roles. As such, it is a specialized type of `assignment`. A user or device with an entitlement gets access rights to specified resources. An entitlement is a property of a managed object. + +JSON:: +JavaScript Object Notation, a lightweight data interchange format based on a subset of JavaScript syntax. For more information, see the link:http://www.json.org[JSON, window=\_blank] site. + +JWT:: +JSON Web Token. As noted in the link:http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html[JSON Web Token draft IETF Memo, window=\_blank], "JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties." For OpenIDM, the JWT is associated with the `JWT_SESSION` authentication module. + +managed object:: +An object that represents the identity-related data managed by OpenIDM. Managed objects are configurable, JSON-based data structures that OpenIDM stores in its pluggable repository. The default configuration of a managed object is that of a user, but you can define any kind of managed object, for example, groups or roles. + +mapping:: +A policy that is defined between a source object and a target object during reconciliation or synchronization. A mapping can also define a trigger for validation, customization, filtering, and transformation of source and target objects. + +OSGi:: +A module system and service platform for the Java programming language that implements a complete and dynamic component model. For a good introduction, see the link:https://www.osgi.org//developer/benefits-of-using-osgi[OSGi, window=\_blank] site. While OpenIDM services are designed to run in any OSGi container, currently only link:http://felix.apache.org/[Apache Felix, window=\_blank] is supported. + +reconciliation:: +During reconciliation, comparisons are made between managed objects and objects on source or target systems. Reconciliation can result in one or more specified actions, including, but not limited to, synchronization. + +resource:: +An external system, database, directory server, or other source of identity data to be managed and audited by the identity management system. + +[#gloss-rest] +REST:: +Representational State Transfer. A software architecture style for exposing resources, using the technologies and protocols of the World Wide Web. REST describes how distributed data objects, or resources, can be defined and addressed. + +role:: +OpenIDM includes two different types of provisioning roles and authorization roles. For more information, see xref:../integrators-guide/chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"]. + +source object:: +In the context of reconciliation, a source object is a data object on the source system, that OpenIDM scans before attempting to find a corresponding object on the target system. Depending on the defined mapping, OpenIDM then adjusts the object on the target system (target object). + +synchronization:: +The synchronization process creates, updates, or deletes objects on a target system, based on the defined mappings from the source system. Synchronization can be scheduled or on demand. + +system object:: +A pluggable representation of an object on an external system. For example, a user entry that is stored in an external LDAP directory is represented as a system object in OpenIDM for the period during which OpenIDM requires access to that entry.System objects follow the same RESTful resource-based design principles as managed objects. + +target object:: +In the context of reconciliation, a target object is a data object on the target system, that OpenIDM scans after locating its corresponding object on the source system. Depending on the defined mapping, OpenIDM then adjusts the target object to match the corresponding source object. + + diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/preface.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/preface.adoc new file mode 100644 index 000000000..c68865fad --- /dev/null +++ b/openidm-doc/src/main/asciidoc/integrators-guide/preface.adoc @@ -0,0 +1,43 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[preface] +[#preface] +== Preface + +In this guide you will learn how to integrate OpenIDM as part of a complete identity management solution. + +[#d0e226] +=== Who Should Use This Guide + +This guide is written for systems integrators building identity management solutions based on OpenIDM services. This guide describes OpenIDM, and shows you how to set up OpenIDM as part of your identity management solution. + +You do not need to be an OpenIDM wizard to learn something from this guide, though a background in identity management and building identity management solutions can help. + + +include::../partials/sec-formatting-conventions.adoc[] + +include::../partials/sec-accessing-doc-online.adoc[] + +include::../partials/sec-joining-the-community.adoc[] + +include::../partials/sec-support-contact.adoc[] diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-audit-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-audit-sample.adoc new file mode 100644 index 000000000..d85df61e9 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-audit-sample.adoc @@ -0,0 +1,352 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-audit-sample] +== Audit Samples + +This chapter demonstrates two ways to configure OpenIDM to save audit data. In xref:#basic-audit-sample["Directing Audit Information To a MySQL Database"], you'll see how to direct information for OpenIDM audit events to a MySQL database server. In xref:#jms-audit-sample["Show Audit Events Published on a JMS Topic"], you'll see how to configure an audit event handler for the Java Message Service (JMS). + +[#basic-audit-sample] +=== Directing Audit Information To a MySQL Database + +This sample demonstrates how you can use the audit features provided with OpenIDM and directs audit information to a MySQL database. The sample is closely related to xref:chap-xml-samples.adoc#more-sample-1["First OpenIDM Sample - Reconciling an XML File Resource"] but includes a ScriptedSQL implementation of the Groovy Connector Toolkit to connect to the MySQL database. + +To use any of the audit features that are demonstrated in this sample, copy information from files in the `samples/audit-sample` directory. This can help you to set up auditing for any other sample. + +[#audit-config-files] +==== Audit Sample Configuration Files + +Review the configuration files used in this sample. They can help you understand the functionality of the data sets being audited. +The key configuration files, in the `samples/audit-sample` directory, are as follows: + +* `conf/provisioner.openicf-scriptedsql.json` shows the configuration of the Scripted SQL implementation of the Groovy Connector. For more information, see xref:../connectors-guide/chap-groovy.adoc#chap-groovy["Groovy Connector Toolkit"] in the __Connectors Guide__. + +* `conf/provisioner.openicf-xml.json` shows the configuration of the xref:../connectors-guide/chap-xml.adoc#chap-xml["XML File Connector"] in the __Connectors Guide__. + +* `conf/audit.json` configures audit logging on the router, to a remote system, as discussed in xref:../integrators-guide/chap-auditing.adoc#configure-audit-service["Configuring the Audit Service"] in the __Integrator's Guide__. ++ + +* `conf/sync.json` shows mappings between managed users and the data set attached through the XML File Connector. + +* `data/sample_audit_db.mysql` includes a schema that supports tables in the external MySQL database. + +* Groovy scripts in the `tools/` subdirectory supports communications between the Scripted SQL connector and the MySQL database. + + + +[#external-audit-mysql-sample] +==== Configuration with MySQL + +You need to set up communications between OpenIDM and an external MySQL database server. + +Make sure MySQL is running. Follow the configuration instructions described in xref:../install-guide/chap-repository.adoc#repository-mysql["To Set Up OpenIDM With MySQL"] in the __Installation Guide__. +The sample expects the following configuration for MySQL: + +* The database is available on the local system. + +* The database listens on the standard MySQL port, 3306. + +* You can connect to the MySQL database over the network. + +* OpenIDM should include the MySQL connector .jar file in the `/path/to/openidm/bundle` directory. If you need to download this file, see xref:../install-guide/chap-repository.adoc#repository-mysql["To Set Up OpenIDM With MySQL"] in the __Installation Guide__ for instructions. + +* MySQL serves a database called `audit` with six tables: `auditaccess`, `auditactivity`, `auditauthentication`, `auditconfig`, `auditrecon`, and `auditsync`. + +* For more information on the database schema, examine the following data definition language file: `openidm/samples/audit-sample/data/sample_audit_db.mysql`. Import the file into MySQL before running the sample. ++ + +[source, console] +---- +$ mysql -u root -p < /path/to/openidm/samples/audit-sample/data/sample_audit_db.mysql + +Enter password: +$ +---- + +Now review the format of the audit database sample, created from the `sample_audit_db.mysql` file, at the MySQL prompt. To access that prompt, run the following command: + +[source, console] +---- +$ mysql -u root -p +mysql > use audit; +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 +---- +You can now review the current state of each noted audit log with the following commands: + +[source, console] +---- +select * from auditaccess; +select * from auditactivity; +select * from auditauthentication; +select * from auditconfig; +select * from auditrecon; +select * from auditsync; +---- +Unless you enable scheduled reconciliation, you will not see audit reconciliation data until you run reconciliation manually. + + +[#install-sample-audit] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the audit sample. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/audit-sample +---- + + +[#run-sample-audit] +==== Running the Sample + +Run reconciliation over the REST interface. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemXmlfileAccounts_managedUser&waitForCompletion=true" +---- +You can also run this reconciliation from the Admin UI, at `\https://localhost:8443/admin`. + +[WARNING] +==== +The default password for the OpenIDM administrative user, `openidm-admin`, is `openidm-admin`. + +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +==== +You can now review the results in the MySQL database, from the MySQL prompt, using the commands described earlier. + +If you have retained the default `"useForQueries" : true` option in the `conf/audit.json` file, you can also `GET` the same data with a REST call. For examples on how you can query audit logs, see xref:../integrators-guide/chap-auditing.adoc#querying-audit-over-rest["Querying Audit Logs Over REST"] in the __Integrator's Guide__. + + +[#audit-sample-more] +==== Additional Audit Configuration + +You can configure a variety of audit event handlers, event topics, audit filter policies, and scripts. You can even set up auditing, by topic, in CSV files. For more information, see xref:../integrators-guide/chap-auditing.adoc#chap-auditing["Using Audit Logs"] in the __Integrator's Guide__. + +You can see how this works from the Admin UI. After you log in with the OpenIDM administrative account, click Configure > System Preferences > Audit. + + + +[#jms-audit-sample] +=== Show Audit Events Published on a JMS Topic + +Starting with OpenIDM 4.5.0, you can configure a Java Message Service (JMS) Audit Event Handler. JMS is an API that supports Java-based peer-to-peer messages between clients. The JMS API can create, send, receive, and read messages, reliably and asynchronously. You can now set up a JMS audit event handler, which can publish messages that comply with the link:http://download.oracle.com/otndocs/jcp/7195-jms-1.1-fr-spec-oth-JSpec/[Java(TM) Message Service Specification Final Release 1.1, window=\_blank]. + +[WARNING] +==== +JMS topics are not related to ForgeRock audit event topics. The ForgeRock implementation of JMS topics use the link:http://docs.oracle.com/javaee/6/tutorial/doc/bncdx.html#bnced[publish/subscribe messaging domain, window=\_blank], and may direct messages to the JMS audit event handler. In contrast, ForgeRock audit event topics specify categories of events. +==== +In this sample, we demonstrate the use of the JMS audit event handler. This sample is based on xref:chap-xml-samples.adoc#more-sample-1["First OpenIDM Sample - Reconciling an XML File Resource"]. You will set up communications between OpenIDM and an external JMS Message Broker, as well as link:http://activemq.apache.org/[Apache Active MQ, window=\_blank] as the JMS provider and message broker. + +[#section-jms-bundles] +==== Adding Required Bundles for the JMS Audit Event Handler + +To test this sample, you'll download a total of five JAR files. The first four are OSGi Bundles: + +* link:https://repository.apache.org/content/repositories/releases/org/apache/activemq/activemq-client/[ActiveMQ Client, window=\_top] + +* The link:http://bnd.bndtools.org/[bnd, window=\_blank] JAR for working with OSGi bundles, which you can download from link:https://repo1.maven.org/maven2/biz/aQute/bnd/1.50.0/bnd-1.50.0.jar[bnd-1.50.0.jar, window=\_top]. + +* The Apache Geronimo J2EE management bundle, `geronimo-j2ee-management_1.1_spec-1.0.1.jar`, which you can download from link:https://repo1.maven.org/maven2/org/apache/geronimo/specs/geronimo-j2ee-management_1.1_spec/1.0.1/[https://repo1.maven.org/maven2/org/apache/geronimo/specs/geronimo-j2ee-management_1.1_spec/1.0.1/, window=\_top]. + +* The link:https://github.com/chirino/hawtbuf[hawtbuf, window=\_blank] Maven-based protocol buffer compiler JAR, which you can download from link:https://repo1.maven.org/maven2/org/fusesource/hawtbuf/hawtbuf/1.11/[hawtbuf-1.11.jar, window=\_top]. + +* The ActiveMQ 5.13.2 binary, which you can download from link:http://activemq.apache.org/activemq-5132-release.html[http://activemq.apache.org/activemq-5132-release.html, window=\_top]. + + +[NOTE] +==== +The JMS audit event handler has been tested and documented with the noted versions of the JAR files that you've just downloaded. +==== +Make sure at least the first two JAR files, for __the Active MQ Client__ and __bnd__, are in the same directory. Navigate to that directory, and create an OSGi bundle with the following steps: + +==== + +. Create a BND file named `activemq.bnd` with the following contents: ++ + +[source, console] +---- +version=5.13.2 +Export-Package: *;version=${version} +Bundle-Name: ActiveMQ :: Client +Bundle-SymbolicName: org.apache.activemq +Bundle-Version: ${version} +---- + +. Run the following command to create the OSGi bundle archive file: ++ + +[source, console] +---- +$ java \ +-jar \ +bnd-1.50.0.jar \ +wrap \ +-properties \ +activemq.bnd \ +activemq-client-5.13.2.jar +---- + +. Rename the `activemq-client-5.13.2.bar` file that appears to `activemq-client-5.13.2-osgi.jar` and copy it to the `/path/to/openidm/bundle` directory. + +==== +Copy the other two bundle files, __Apache Geronimo__ and __hawtbuf__, to the `/path/to/openidm/bundle` directory. + + +[#jms-sample-start] +==== Starting the ActiveMQ Broker and OpenIDM + +With the appropriate bundles in the `/path/to/openidm/bundles` directory, you're ready to start the ActiveMQ message broker, as well as OpenIDM with the JMS Audit Sample. + +Navigate to the directory where you unpacked the ActiveMQ binary, possibly `/path/to/apache-activemq-5.13.0/`. If you need SSL protection for your audit data, edit the ActiveMQ configuration file, `activemq.xml`, in the `conf/` subdirectory. Find the code block associated with ``, and add the following line within that block: + +[source, xml] +---- + +---- +To start the ActiveMQ broker, navigate to the directory where you unpacked the ActiveMQ binary, and run the following command: + +[source, console] +---- +$ bin/activemq start +INFO: Loading '/path/to/apache-activemq-5.13.0/bin/env' +INFO: Using java '/usr/bin/java' +INFO: Starting - inspect logfiles specified in logging.properties and log4j.properties to get details +INFO: pidfile created : '/path/to/apache-activemq-5.13.0/data/activemq.pid' (pid '22671') +---- +Now start OpenIDM, with the sample in the `/path/to/openidm/samples/audit-jms-sample` directory: + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/audit-jms-sample +---- + +[NOTE] +==== +If you see the following error in the OpenIDM console, you may have forgotten to go through the steps shown in xref:#section-jms-bundles["Adding Required Bundles for the JMS Audit Event Handler"]; you also need to start the ActiveMQ broker. + +[source, console] +---- +SEVERE: Unable to create JmsAuditEventHandler 'jms': null +---- +==== + + +[#jms-sample-consume] +==== Configuring and Using a JMS Consumer Application + +To take advantage of the Apache ActiveMQ event broker, the JMS audit sample includes a Java consumer in the following directory: `/path/to/openidm/samples/audit-jms-sample/consumer/` + +Assuming you have Apache Maven installed on the local system, you can compile that sample consumer with the following commands: + +[source, console] +---- +$ cd /path/to/openidm/samples/audit-jms-sample/consumer/ +$ mvn clean install +---- +When the build process is complete, you'll see a `BUILD SUCCESS` message: + +[source, console] +---- +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 12.638 s +[INFO] Finished at: 2016-04-15T15:18:31-07:00 +[INFO] Final Memory: 13M/119M +[INFO] ------------------------------------------------------------------------ +---- + +[NOTE] +==== +You may see `[WARNING]` messages during the build. As long as the messages end with `BUILD SUCCESS`, you can proceed with the JMS consumer application. +==== +You can then run the following command to output audit messages related to OpenIDM actions: + +[source, console] +---- +$ mvn \ +exec:java \ +-Dexec.mainClass="SimpleConsumer" \ +-Dexec.args="tcp://localhost:61616" + [INFO] ------------------------------------------------------------------------ +[INFO] Building SimpleConsumer 1.0-SNAPSHOT +[INFO] ------------------------------------------------------------------------ +[INFO] +[INFO] --- exec-maven-plugin:1.4.0:java (default-cli) @ SimpleConsumer --- +Connection factory=org.apache.activemq.ActiveMQConnectionFactory +READY, listening for messages. (Press 'Enter' to exit) +---- +If you've configured ActiveMQ on a secure port, as described in xref:#jms-sample-start["Starting the ActiveMQ Broker and OpenIDM"], you can run this alternative command: + +[source, console] +---- +$ mvn \ +exec:java \ +-Dexec.mainClass="SimpleConsumer" \ +-Dexec.args="ssl://localhost:61617?daemon=true&socket.enabledCipherSuites= + SSL_RSA_WITH_RC4_128_SHA,SSL_DH_anon_WITH_3DES_EDE_CBC_SHA" +---- +Try some actions on OpenIDM, either in a different console or in the Admin UI. Watch the output in the `SimpleConsumer` console. As an example, you might see output similar to the following when you are xref:chap-xml-samples.adoc#sample-running-reconciliation["Running Reconciliation"] on the data in this sample: + +[source, javascript] +---- +{ + "event": { + "_id": "88b3da4d-e427-4f21-881c-036d7a854ccc-2559", + "reconId": "88b3da4d-e427-4f21-881c-036d7a854ccc-2546", + "mapping": "systemXmlfileAccounts_managedUser", + "linkQualifier": "default", + "exception": null, + "action": "UPDATE", + "userId": "openidm-admin", + "eventName": "recon", + "timestamp": "2016-04-16T13:40:35.974Z", + "transactionId": "88b3da4d-e427-4f21-881c-036d7a854ccc-2546", + "message": null, + "situation": "CONFIRMED", + "sourceObjectId": "system/xmlfile/account/scarter", + "status": "SUCCESS", + "targetObjectId": "managed/user/scarter", + "reconciling": "source", + "ambiguousTargetObjectIds": "", + "entryType": "entry" + }, + "auditTopic": "recon" +} +---- + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-endpoint-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-endpoint-sample.adoc new file mode 100644 index 000000000..096283fcf --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-endpoint-sample.adoc @@ -0,0 +1,198 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-endpoint-sample] +== Custom Endpoint Sample + +This chapter describes the custom endpoint sample delivered with OpenIDM. + +OpenIDM supports scriptable custom endpoints that enable you to launch arbitrary scripts through an OpenIDM REST URI. For information about how custom endpoints are configured, see xref:../integrators-guide/chap-scripting.adoc#custom-endpoints["Creating Custom Endpoints to Launch Scripts"] in the __Integrator's Guide__. + +The sample endpoint provided in `/path/to/openidm/samples/customendpoint` illustrates the configuration of a custom endpoint, and the structure of custom endpoint scripts. + +The purpose of this custom endpoint is to return a list of variables available to each method used in a script. The scripts show the complete set of methods that can be used. These methods map to the standard HTTP verbs - create, read, update, delete, patch, query, and action. A sample JavaScript and Groovy script is provided. + +==== +To run the sample: + +. Copy the endpoint configuration file (`samples/customendpoint/conf/endpoint-echo.json` to your project's `conf` directory. + +. Copy either the JavaScript file (`samples/customendpoint/script/echo.js`) or Groovy script file (`samples/customendpoint/script/echo.groovy`) to your project's `script` directory. + +. Open the endpoint configuration file in a text editor: ++ + +[source, javascript] +---- +{ + "file" : "echo.groovy", + "type" : "groovy", + "_file" : "echo.js", + "_type" : "text/javascript" +} +---- ++ +The configuration file contains nothing more than a reference to the endpoint scripts. In this case, the JavaScript script is commented out (with an underscore before the `file` and `type` properties. If you want to use the JavaScript endpoint script, uncomment these lines and comment out the lines that correspond to the Groovy script in the same way. ++ +Endpoint configuration files can include a `context` property that specifies the route to the endpoint, for example: ++ + +[source, javascript] +---- +"context" : "endpoint/linkedView/*" +---- ++ +If no `context` is specified, the route to the endpoint is taken from the file name, in this case `endpoint/echo`. + +. Test each method in succession to return the expected request structure of that method. The following examples show the request structure of the read, create and patch methods. The configuration file has been edited to use the JavaScript file, rather than the Groovy file. The output shown in these examples has been cropped for legibility. For a description of each parameter, see xref:../integrators-guide/chap-scripting.adoc#custom-endpoint-scripts["Writing Custom Endpoint Scripts"] in the __Integrator's Guide__. ++ +The following command performs a read on the echo endpoint and returns the request structure of a read request: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/endpoint/echo +{ + "_id": "", + "method": "read", + "resourceName": "", + "parameters": {}, + "context": { + ... + } +} +---- ++ +The following command performs a query on the echo endpoint and returns the request structure of a query request: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/endpoint/echo?_queryId=query-all-ids" +{ + "result": [ + { + "method": "query", + "resourceName": "", + "pagedResultsCookie": null, + "pagedResultsOffset": 0, + "pageSize": 0, + "queryExpression": null, + "queryId": "query-all-ids", + "queryFilter": "null", + "parameters": {}, + "content": null, + "context": { + ... + } + } + ], + ... +} +---- ++ +The following command sends a create request to the echo endpoint. No user is actually created. The endpoint script merely returns the request structure of a create request. The `content` parameter in this case provides the JSON object that was sent with the request: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "Content-Type: application/json" \ + --data '{ + "userName":"steve", + "givenName":"Steve", + "sn":"Carter", + "telephoneNumber":"0828290289", + "mail":"scarter@example.com", + "password":"Passw0rd" + }' \ + --request POST \ + "http://localhost:8080/openidm/endpoint/echo?_action=create" +{ + "_id": "", + "method": "create", + "resourceName": "", + "newResourceId": null, + "parameters": {}, + "content": { + "userName": "steve", + "givenName": "Steve", + "sn": "Carter", + "telephoneNumber": "0828290289", + "mail": "scarter@example.com", + "password": "Passw0rd" + }, + "context": { + ... + } +} +---- ++ +The following command sends a patch request to the echo endpoint. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "Content-Type: application/json" \ + --data '[ + { + "operation":"replace", + "field":"/givenName", + "value":"Steven" + } + ]' \ + --request PATCH \ + "http://localhost:8080/openidm/endpoint/echo" +{ + "_id": "", + "method": "patch", + "resourceName": "", + "revision": null, + "patch": [ + { + "operation": "replace", + "field": "/givenName", + "value": "Steven" + } + ], + "parameters": {}, + "context": { + ... + } +} +---- + +==== + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-fullstack-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-fullstack-sample.adoc new file mode 100644 index 000000000..b551d51a4 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-fullstack-sample.adoc @@ -0,0 +1,338 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-fullstack-sample] +== Full Stack Sample - Using OpenIDM in the ForgeRock Identity Platform + +This sample demonstrates the integration of three ForgeRock products: OpenIDM, OpenDJ, and OpenAM. With this sample, you can see how you can use OpenAM for authentication, for users maintained with OpenIDM, based on a data store of users in OpenDJ. + +It may take some time to set up this sample. The instructions that follow describe how you set up OpenDJ with a custom data store, sync that to OpenIDM. You will also configure OpenAM to use that same instance of OpenDJ. When you finish this sample, you will know how make OpenIDM, OpenDJ, and OpenAM work together. When your setup is complete, OpenIDM will authorize, and OpenAM will protect your users. + +Now let us get started. In this sample, you will integrate OpenDJ as the data store for both OpenIDM and OpenAM. + +OpenAM requires the use of a Fully-Qualified Domain Name (FQDN). For this sample, you may set up FQDNs for OpenAM, OpenDJ, and OpenIDM on either an appropriate DNS server or the `hosts` file for each system. + +This sample assumes that you have assigned the following FQDNs to the OpenAM, OpenDJ, and OpenIDM systems, respectively: + +* `openam.example.com` + +* `opendj.example.com` + +* `openidm.example.com` + +This sample assumes that you set up OpenIDM in a "two-way" mapping with an instance of OpenDJ, in a fashion similar to Sample 2b or 2c. It also assumes that you configure OpenAM to use the same instance of OpenDJ as its data store. + +To prepare this sample, you should first prepare OpenDJ and OpenAM. You will then start and customize OpenIDM configuration files via the Admin UI, or alternatively, via a text editor. + +[#external-ldap-config-full-stack] +=== External OpenDJ Configuration + +Configure the OpenDJ server as described in xref:chap-ldap-samples.adoc#external-ldap-config-2["LDAP Server Configuration"]. + +You need to configure the OpenDJ server with write access. This allows you to create users from OpenIDM or OpenAM on the same LDAP server. When you configure the LDAP server, import the LDIF file associated with the Full Stack sample: (`openidm/samples/fullStack/data/Example.ldif.`) + +When you configure OpenAM, you need the following information to configure OpenDJ as an external data store: + +* Access URL and port for the LDAP server, such as opendj.example.com:1389. + +* LDAP Bind DN, normally `cn=Directory Manager` + +* LDAP Bind Password, which should match the password configured the LDAP server. + +* LDAP SSL/TLS, which assumes that you've configured OpenDJ to communicate over the secure LDAP port, such as 1636. + + + +[#external-fullstack-openam-config] +=== OpenAM Server Configuration + +This sample assumes that you will configure OpenAM on a separate system from OpenIDM. + +Install OpenAM as described in link:../../../openam/13/install-guide/#configure-openam-custom[To Custom Configure OpenAM, window=\_blank]. + +During the installation process, include the parameters described in xref:#external-ldap-config-full-stack["External OpenDJ Configuration"]. Alternatively, if you already have an operational instance of OpenAM, set up an external instance of OpenDJ as described in link:../../../openam/13/admin-guide/#realm-data-store[To Configure a Data Store, window=\_blank]. + + +[#install-fullstack] +=== Install the Sample + +Prepare OpenIDM, as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the Full Stack Sample. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/fullStack +---- + + +[#configure-fullstack-sample] +=== Configuring OpenIDM for the Full Stack Sample + +You will need to change three configuration files, related to authentication and provisioning. These files are + +* `fullStack/conf/authentication.json` + +* `fullStack/conf/ui-configuration.json` + +* `fullStack/conf/provisioner.openicf-ldap.json` + +You can make these changes on configuration files or via the Admin UI. In the following sections, you will see how to do so using both methods. + +[NOTE] +==== +Before configuring the `OPENAM_SESSION` module, be sure to configure at least one regular user with the `openidm-admin` authorization role. + +In the Admin UI, click Manage > User > select a user > Authorization Roles > Add Authorization Roles. + +image::images/openidm-admin-role.png[] +Making this change ensures that you can still access OpenIDM after you have activated the `OPENAM_SESSION` module. + +You'll also have to modify the session module as described in the following section: xref:../integrators-guide/chap-auth.adoc#supported-session-modules["Supported Session Module"] in the __Integrator's Guide__. Specifically, you'll need to limit token lifetime properties to 5 seconds. +==== + +[#configure-fullstack-ui] +==== Configuring the OPENAM_SESSION Module in the UI + +Now you can configure the `OPENAM_SESSION` module. To do so, take the following steps: + +* Navigate to `\https://openidm.example.com:8443/admin`. + +* Click Configure > System Preferences > Authentication. + +* Scroll down to the OpenAM Session module. Click the Edit icon to view Basic Properties for that module. ++ + +image::images/openam-auth-basic.png[] + +* OpenIDM does not use OpenAM for authentication until you set `Module Enabled` to true. Make sure that the `OpenAM Deployment Url` matches the FQDN of your instance of OpenAM, such as `\https://openam.example.com:8443/openam`. ++ +For detailed information on the options associated with the `OPENAM_SESSION` module, see xref:../integrators-guide/appendix-auth-modules.adoc#openam-module-details["OPENAM_SESSION Module Configuration Options"] in the __Integrator's Guide__. + +* Select Advanced Properties to access additional options. Typically, you should only have to modify the `OpenAM Login URL` to match the Login URL of the OpenAM server. If you are working with the OpenAM Top-Level Realm, as described in link:../../../openam/13/admin-guide/#chap-realms[Configuring Realms, window=\_blank], and the given FQDN (`openam.example.com`), with an OpenAM web archive file of `openam.war` the following URL should work in this text box: `\https://openam.example.com:8443/openam/XUI/#login` ++ + +image::images/openam-auth-adv.png[] + +When configured, the Admin UI writes the properties to the `authentication.json` and `ui-configuration.json` files. To identify which property is saved in which file, see xref:../integrators-guide/appendix-auth-modules.adoc#openam-module-details["OPENAM_SESSION Module Configuration Options"] in the __Integrator's Guide__. + + +[#configure-fullstack-config] +==== Configuring the OPENAM_SESSION Module with Configuration Files + +To configure the `OPENAM_SESSION` module, you will need to configure two different files: `authentication.json` and `ui-configuration.json`. You can find those files in the `samples/fullStack/conf` directory. + +[#configure-fullstack-authjson] +===== Changes to authentication.json + +Under `"authModules"`, find the `"OPENAM_SESSION"` authentication module. The default version of the `authentication.json` file includes one entry which you will probably change, to match the URL of your instance of OpenAM: + +[source, javascript] +---- +"authModules" : [ +... + { + "name" : "OPENAM_SESSION", + "properties" : { + "openamDeploymentUrl" : "http://example.com:8081/openam", + "groupRoleMapping" : { + "openidm-admin" : [ + "cn=idmAdmins,ou=Groups,dc=example,dc=com" + ] + }, + "openamSSOTokenCookieName" : "iPlanetDirectoryPro", + "openamUserAttribute" : "uid", + "queryOnResource" : "system/ldap/account", + "propertyMapping" : { + "authenticationId" : "uid", + "groupMembership" : "ldapGroups" + }, + "defaultUserRoles" : [ + "openidm-authorized" + ], + "groupComparisonMethod" : "ldap", + "augmentSecurityContext" : { + "type" : "text/javascript", + "file" : "auth/populateAsManagedUser.js" + }, + "truststoreType" : "&{openidm.truststore.type}", + "truststoreFile" : "&{openidm.truststore.location}", + "truststorePassword" : "&{openidm.truststore.password}" + }, + "enabled" : false + } +] +---- +Based on a standard `openidm-admin` user and a URL for OpenAM of openam.example.com, you would change the first part of the code snippet to: + +[source, javascript] +---- +"authModules" : [ +... + { + "name" : "OPENAM_SESSION", + "properties" : { + "openamDeploymentUrl" : "https://openam.example.com:8443/openam", + "groupRoleMapping" : { + "openidm-admin" : [ + "cn=idmAdmins,ou=Groups,dc=example,dc=com" + ] + }, +---- +Remember to include the configured OpenAM webapps subdirectory, typically `/openam`, in the `"openamDeploymentUrl"`. After the Java EE container used for OpenAM starts, it unpacks a file such as `openam.war` so that you can access it on the `/openam` endpoint. + +The `openamDeploymentUrl` shown above assumes that you are using SSL. If you have a signed certificate, you should import that into the OpenIDM truststore file. For more information, see xref:../integrators-guide/chap-security.adoc#security-management-service["Accessing the Security Management Service"] in the __Integrator's Guide__. + +Look at the `enabled` property. By default the `OPENAM_SESSION` module is disabled, as shown here: + +[source, console] +---- +"enabled" : false +---- +To enable the module, change `false` to `true`. + + +[#configure-fullstack-uiconfig] +===== Changes to ui-configuration.json + +For the `OPENAM_SESSION` module, you may want to modify some of the properties in the following excerpt of the `ui-configuration.json` configuration file. + +[source, javascript] +---- +"defaultNotificationType" : "info", +"openamLoginUrl" : "http://example.com:8081/openam/XUI/#login/", +"openamUseExclusively" : false, +"openamAuthEnabled" : true, +"openamLoginLinkText" : "Login with OpenAM" +---- +When `openamAuthEnabled` is true, you should also change the `"openamLoginUrl"` URL to match the login URL of your instance of OpenAM. If you want users to connect, securely, to the openam.example.com FQDN, on the top-level OpenAM realm, change this property to `"https://openam.example.com:8443/openam/XUI/#/login/"`. + +For details on each of these properties, see xref:../integrators-guide/appendix-auth-modules.adoc#openam-module-details["OPENAM_SESSION Module Configuration Options"] in the __Integrator's Guide__. + + + +[#configure-fullstack-sample-prov] +==== Configure Provisioning for the Full Stack Sample + +This section describes how you might customize the `provisioner.openicf-ldap.json` file. + +If you want to configure this provisioner from the Admin UI, navigate to `\https://openidm.example.com:8443/admin`, and edit the LDAP connector. + +image::images/openam-djconn.png[] +Edit connector details as required. For consistency, with OpenAM requirements, change the `Host name or IP` to match the FQDN of your configured instance of OpenDJ, opendj.example.com. Be consistent with the `Port` number; if you set this port to 1389, configure OpenDJ to communicate over the same port. Configure OpenAM to use the same data store. + +Open the noted provisioner file from the `samples/fullStack/conf` directory. The default version of this file should look similar to the following: + +[source, javascript] +---- +"configurationProperties" : { + "host" : "localhost", + "port" : 1389, + "ssl" : false, + "principal" : "cn=Directory Manager", + "credentials" : { + "$crypto" : { + "value" : { + "iv" : "XUfvN7eE471b/1MG8bF60g==", + "data" : "Y4M22LQehQ95MRQTJCmKdw==", + "cipher" : "AES/CBC/PKCS5Padding", + "key" : "openidm-sym-default" + }, + "type" : "x-simple-encryption" + } +}, +"baseContexts" : [ + "dc=example,dc=com" +], +"baseContextsToSynchronize" : [ + "dc=example,dc=com" +], +---- +This snippet already matches the noted base context of `"dc=example,dc=com"` with a principal of `"cn=Directory Manager"`. + +Make sure that the following settings are consistent with the way you have configured OpenDJ and OpenAM. + +Change the `"localhost"` entry to the FQDN where OpenDJ is installed. In this case, that FQDN is `opendj.example.com`. Depending on whether you want to set up communications over a regular or secure LDAP port, you might change the `"port"` number to to something like 1389 or 1636. The following excerpt illustrates the change to an LDAP connector configuration: + +[source, javascript] +---- +"configurationProperties" : { + "host" : "opendj.example.com", + "port" : 1389, + "ssl" : false, + "principal" : "cn=Directory Manager", + "credentials" : "password", + "$crypto" : { + "value" : { + "iv" : "XUfvN7eE371b/1MG8bF60g==", + "data" : "Y4M77LQehQ95MRQTJCmKdw==", + "cipher" : "AES/CBC/PKCS5Padding", + "key" : "openidm-sym-default" + }, + "type" : "x-simple-encryption" + } + }, + "baseContexts" : [ + "dc=example,dc=com" + ], + "baseContextsToSynchronize" : [ + "dc=example,dc=com" + ], +... +---- +If you want to configure secure communications between OpenIDM and OpenDJ, do remember to configure OpenDJ to communicate securely. + +When configuring OpenDJ, you may have a self-signed certificate. You may even have a certificate from a Certificate Authority. In either case, import that OpenDJ certificate into the OpenIDM truststore. For more information, see xref:../integrators-guide/chap-security.adoc#security-management-service["Accessing the Security Management Service"] in the __Integrator's Guide__. + + + +[#run-fullstack] +=== Run the Sample + +The mapping configuration file (`sync.json`) for this sample includes two mappings, `systemLdapAccounts_managedUser`, which synchronizes users from the source LDAP server with the target OpenIDM repository, and `managedUser_systemLdapAccounts`, which synchronizes changes from the OpenIDM repository to the LDAP server. + +You can run this part of the sample by using the `curl` command-line utility, or by using the OpenIDM Administration UI. + +This part of the operation is identical to that shown in the relevant part of Sample 2b, xref:chap-ldap-samples.adoc#run-sample2b["Run the Sample"]. + +After you complete the steps described in Sample 2b, do one more thing. Navigate to the Admin UI at `\https://openidm.example.com:8443/admin`, and select Manage > User. Select a specific user, such as `bjensen`. Click Change Password and change that password. + +Return to the Admin UI, select Mappings, and select the managedUser_systemLdapAccounts mapping. Click Reconcile Now to propagate the password you just changed to OpenDJ. You are now ready for the next section. + + +[#verify-openam] +=== Verify the Sample on OpenAM + +When you reconciled data stores for this OpenIDM sample, you should see the standard users for this sample reconciled into the OpenAM Data store. + +In OpenAM, access the list of users. Navigate to OpenAM at `\https://openam.example.com:8443/openam`. Log in with the administrative account, which by default is `amadmin`. Navigate to Access Control > Realm > Subjects > User. You should see the same users as you see in the OpenIDM Self-Service UI. + +[#openam-user-list] +image::images/fullstack-openam-users.png[] +Log out of OpenAM. + +Return to OpenIDM. In the login window that appears, click Login with OpenAM. + +You should be redirected to to the OpenAM login screen at `\https://openam.example.com:8443/openam`. + +Login as user `bjensen`, with the password that you just changed in OpenIDM. If successful, you should now be logged into the __OpenIDM__ Self-Service UI screen, as user `bjensen`. + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-google-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-google-sample.adoc new file mode 100644 index 000000000..3dea43830 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-google-sample.adoc @@ -0,0 +1,498 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-google-sample] +== Google Sample - Connecting to Google With the Google Apps Connector + +OpenICF provides a Google Apps Connector that enables you to interact with Google's web applications. + +[NOTE] +==== +The Google Apps Connector, and this corresponding sample, are provided only with the OpenIDM Enterprise build, available on the link:https://backstage.forgerock.com[ForgeRock Backstage, window=\_blank] site. +==== +This sample demonstrates the creation of users and groups on an external Google system, using OpenIDM's REST interface. The sample requires that you have a Google Apps account. Obtaining a Google Apps account is described in the link:https://support.google.com/a/answer/53926?hl=en[Google documentation, window=\_blank]. + +[#google-apps-] +=== Before You Start + +To set up OpenIDM to connect to your Google Apps account, you must have a Google Apps project (or create a new project) that authorizes consent for OpenIDM. + +==== + +. Log in to the Google Apps Developers Console (at https://console.developers.google.com/start) and update your project or create a new project for OpenIDM. + +. Enable the following APIs for your OpenIDM project: ++ + +* Admin SDK API + +* Enterprise License Manager API + + +. Set up an OAuth2 Client ID. ++ +The Google Apps connector uses OAuth2 to authorize the connection to the Google service. Set up an OAuth2 Client ID as follows: ++ + +.. In the Google Apps Developers Console, select Credentials > New Credentials > OAuth Client ID. + +.. Click Configure Consent Screen and enter a Product Name. ++ +This is the name that will be shown for all applications registered in this project. ++ +For the purposes of this example, we use the Product Name `OpenIDM`. ++ +Click Save. + +.. Select Credentials > OAuth Client ID > Web application. ++ +Under Authorized redirect URIs, enter the callback URL (the URL at which your clients will access your application). The default OpenIDM callback URL is `\https://localhost:8443/admin/oauth.html`. Click Create to set up the callback URL. ++ +Click Create again to set up the client ID. ++ +This step generates an OAuth Client ID and Client, similar to the following: ++ + +image::images/oauth-credentials.png[] ++ +Copy and paste these values into a text file as you will need them when you configure the Google Apps connector. + + +==== + + +[#configure-google-apps-connector] +=== Configuring the Google Apps Connector + +This procedure uses the OpenIDM Admin UI to set up the Google Apps connector. + +==== + +. To configure the connector, start OpenIDM with the Google Apps sample configuration: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/google-connector +Executing ./startup.sh... +Using OPENIDM_HOME: /path/to/openidm +Using PROJECT_HOME: /path/to/openidm/samples/google-connector/ +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/samples/google-connector/conf/boot/boot.properties +-> OpenIDM ready +---- + +. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ +This URL reflects the host on which OpenIDM is installed and corresponds to the callback URL that you specified in the previous section. The URL must be included in the list of Authorized redirect URIs for your project. + +. Select Configure > Connectors and click on the Google Apps connector. + +. On the Details tab, set the Enabled field to True. + +. Enter the Oauth2 Client ID and Client Secret that you obtained in the previous section. + +. Click Save Connector Changes. + +. You are redirected to Google's Login page. ++ +When you have logged in, Google requests that you allow access from your project, in this case, OpenIDM. ++ + +image::images/google-apps-allow.png[] ++ +Click Allow. ++ +If you click Deny here, you will need to return to the Connector Configuration > Details tab in the Admin UI and save your changes again. ++ +When you allow access, you are redirected to the Connectors page in the OpenIDM Admin UI, where the Google Apps Connector should now be Active. ++ + +image::images/google-apps-active.png[] + +==== + + +[#running-the-google-apps-sample] +=== Running the Google Apps Sample + +This procedure uses create, read, update, and delete (CRUD) operations to the Google resource, to verify that the connector is working as expected. The procedure uses a combination of REST commands, to manage objects on the Google system, and the Admin UI, to manage reconciliation from the Google system to the manage user repository. + +The sample configuration has one mapping __from__ the Google system __to__ the managed user repository. + +All of the commands shown here assume that your domain is `example.com`. Adjust the examples to manage your domain. + +==== + +. Create a user entry on your Google resource, over REST. ++ +When you create resources for Google, note that the equals (`=`) character cannot be used in any attribute value. ++ +The following command creates an entry for user `Sam Carter`: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__": "samcarter@example.com", + "__PASSWORD__" : "password", + "givenName" : "Sam", + "familyName": "Carter", + "agreedToTerms": true, + "changePasswordAtNextLogin" : false + }' \ + "https://localhost:8443/openidm/system/google/__ACCOUNT__?_action=create" +{ + "_id": "103567435255251233551", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/LWHPMXXG8M0cjQAPITM95Y636cM\"", + "orgUnitPath": "/", + "isAdmin": false, + "fullName": "Sam Carter", + "customerId": "C02rsqddz", + "relations": null, + "nonEditableAliases": null, + "suspensionReason": null, + "includeInGlobalAddressList": true, + "givenName": "Sam", + "addresses": null, + "isDelegatedAdmin": false, + "changePasswordAtNextLogin": false, + "isMailboxSetup": true, + "__NAME__": "samcarter@example.com", + "agreedToTerms": true, + "externalIds": null, + "ipWhitelisted": false, + "aliases": null, + "lastLoginTime": [ + "1970-01-01T00:00:00.000Z" + ], + "organizations": null, + "suspended": false, + "deletionTime": null, + "familyName": "Carter", + "ims": null, + "creationTime": [ + "2016-02-02T12:52:30.000Z" + ], + "thumbnailPhotoUrl": null, + "emails": [ + { + "address": "samcarter@example.com", + "primary": true + } + ], + "phones": null +} +---- ++ +Note the ID of the new user (`103567435255251233551` in this example). You will need this ID for the update commands in this section. + +. Reconcile the Google resource with the managed user repository. ++ +This step should create the new user, Sam Carter (and any other users in your Google resource) in the OpenIDM managed user repository. ++ +To run reconciliation follow these steps: ++ + +.. In the Admin UI, select Configure > Mappings. + +.. Click on the sourceGoogle__ACCOUNT___managedUser mapping, and click Reconcile Now. + +.. Select Manage > User and verify that the user Sam Carter has been created in the repository. + + +. Update Sam Carter's phone number in your Google resource by sending a PUT request with the updated data, and specifying the user `_id` in the request: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --header "If-Match : *" \ + --data '{ + "__NAME__": "samcarter@example.com", + "__PASSWORD__" : "password", + "givenName" : "Sam", + "familyName": "Carter", + "agreedToTerms": true, + "changePasswordAtNextLogin" : false, + "phones" : + [ + { + "value": "1234567890", + "type": "home" + }, + { + "value": "0987654321", + "type":"work" + } + ] + }' \ + "https://localhost:8443/openidm/system/google/__ACCOUNT__/103567435255251233551" +{ + "_id": "103567435255251233551", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/vfSJgHt-STUUto4lM_4ESO9izR4\"", +... + "emails": [ + { + "address": "samcarter@example.com", + "primary": true + } + ], + "phones": [ + { + "value": "1234567890", + "type": "home" + }, + { + "value": "0987654321", + "type": "work" + } + ] +} +---- + +. Read Sam Carter's entry from your Google resource by including his `_id` in the URL: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/system/google/__ACCOUNT__/103567435255251233551" +{ + "_id": "103567435255251233551", + "__NAME__": "samcarter@example.com", +... + "phones": [ + { + "value": "1234567890", + "type": "home" + }, + { + "value": "0987654321", + "type": "work" + } + ] +} +---- + +. Create a group entry on your Google resource: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "__NAME__": "testGroup@example.com", + "__DESCRIPTION__": "Group used for google-connector sample.", + "name": "TestGroup" + }' \ + "https://localhost:8443/openidm/system/google/__GROUP__?_action=create" + +{ + "_id": "00meukdy40gpg98", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/LLhHx2plMJPKeY1-h6eX_OVDi4c\"", + "adminCreated": true, + "__NAME__": "testgroup@example.com", + "aliases": null, + "nonEditableAliases": null, + "__DESCRIPTION__": "Group used for google-connector sample.", + "name": "TestGroup", + "directMembersCount": 0 +} +---- + +. Add Sam Carter to the test group you have just created. Include the `Member` endpoint, and Sam Carter's `_id` in the URL. Specify the `_id` of the group you created as the value of the `groupKey` in the JSON payload: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "groupKey" : "00meukdy40gpg98", + "role": "MEMBER", + "__NAME__": "samcarter@example.com", + "email": "samcarter@example.com", + "type": "MEMBER" + }' \ + "https://localhost:8443/openidm/system/google/Member/103567435255251233551" +{ + "_id": "00meukdy40gpg98/samcarter@example.com", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/CPNpkRnowkGWRvNQvUK9ev6gQ90\"", + "__NAME__": "00meukdy40gpg98/samcarter@example.com", + "role": "MEMBER", + "email": "samcarter@example.com", + "type": "USER", + "groupKey": "103567435255251233551" +} +---- + +. Read the group entry by specifying the group `_id` in the request URL. Notice that the group has one member (`"directMembersCount": 1`). ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/system/google/__GROUP__/00meukdy40gpg98" + +{ + "_id": "00meukdy40gpg98", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/chUdq5m5_cycV2G4sdl7ZKAF75A\"", + "adminCreated": true, + "__NAME__": "testgroup@example.com", + "aliases": null, + "nonEditableAliases": [ + "testGroup@example.test-google-a.com" + ], + "__DESCRIPTION__": "Group used for google-connector sample.", + "name": "TestGroup", + "directMembersCount": 1 +} +---- + +. Delete the group entry. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "https://localhost:8443/openidm/system/google/__GROUP__/00meukdy40gpg98" +{ + "_id": "00meukdy40gpg98", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/chUdq5m5_cycV2G4sdl7ZKAF75A\"", + "adminCreated": true, + "__NAME__": "testgroup@example.com", + "aliases": null, + "nonEditableAliases": [ + "testGroup@example.com.test-google-a.com" + ], + "__DESCRIPTION__": "Group used for google-connector sample.", + "name": "TestGroup", + "directMembersCount": 1 +} +---- ++ +The delete request returns the complete group object. + +. Delete Sam Carter, to return your Google resource to its original state. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "https://localhost:8443/openidm/system/google/__ACCOUNT__/103567435255251233551" +{ + "_id": "103567435255251233551", + "_rev": "\"iwpzoDgSq9BJw-XzORg0bILYPVc/ah6xBLujMAHieSWSisPa1CV6T3Q\"", + "orgUnitPath": "/", + "isAdmin": false, + "fullName": "Sam Carter", + "customerId": "C02rsqddz", + "relations": null, + "nonEditableAliases": [ + "samcarter@example.com.test-google-a.com" + ], + "suspensionReason": null, + "includeInGlobalAddressList": true, + "givenName": "Sam", + "addresses": null, + "isDelegatedAdmin": false, + "changePasswordAtNextLogin": false, + "isMailboxSetup": true, + "__NAME__": "samcarter@example.com", + "agreedToTerms": true, + "externalIds": null, + "ipWhitelisted": false, + "aliases": null, + "lastLoginTime": [ + "1970-01-01T00:00:00.000Z" + ], + "organizations": null, + "suspended": false, + "deletionTime": null, + "familyName": "Carter", + "ims": null, + "creationTime": [ + "2016-02-02T12:52:30.000Z" + ], + "thumbnailPhotoUrl": null, + "emails": [ + { + "address": "samcarter@example.com", + "primary": true + } + ], + "phones": [ + { + "value": "1234567890", + "type": "home" + }, + { + "value": "0987654321", + "type": "work" + } + ] +} +---- + +==== +In this sample, you used the Google Apps connector to add and delete user and group objects in your Google application, and to reconcile users from your Google application to the OpenIDM managed user repository. You can expand on this sample by customizing the connector configuration to provide additional synchronization functionality between OpenIDM and your Google applications. For more information on configuring connectors, see xref:../integrators-guide/chap-resource-conf.adoc#chap-resource-conf["Connecting to External Resources"] in the __Integrator's Guide__. + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-groovy-samples.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-groovy-samples.adoc new file mode 100644 index 000000000..550df458a --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-groovy-samples.adoc @@ -0,0 +1,1491 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-groovy-samples] +== Samples That Use the Groovy Connector Toolkit to Create Scripted Connectors + +OpenIDM 4.5 includes a generic Groovy Connector Toolkit that enables you to run Groovy scripts on any external resource. + +The Groovy Connector Toolkit is not a complete connector, in the traditional sense. Rather, it is a framework within which you must write your own Groovy scripts to address the requirements of your implementation. Specific scripts are provided within these samples, which demonstrate how the Groovy Connector Toolkit can be used. These scripts cannot be used "as is" in your deployment, but are a good starting point on which to base your customization. + +To facilitate creating your own scripted connectors with the Groovy Connector Toolkit, OpenIDM provides a scripted connector __bundler__. The first sample in this chapter uses the connector bundler to create a new connector, and its configuration. The connector bundler is described in detail in the link:http://openicf.forgerock.org/doc/bootstrap/dev-guide/index.html#chap-custom-bundler[OpenICF Developers Guide, window=\_blank]. + +[#more-sample3] +=== Sample 3 - Using the Custom Scripted Connector Bundler to Build a ScriptedSQL Connector + +This sample demonstrates the following OpenIDM functionality: + +* Custom scripted connector bundler ++ +The sample uses the custom scripted connector bundler to create a new custom connector. The connector bundler generates a scripted connector, the connector configuration and the Groovy scripts required to communicate with an external MySQL database (HRDB). + +* Complex data types ++ +Complex data types can be stored, retrieved and synchronized like any other object property. They are stored in the managed data as JSON objects, represented as a string, but can be mapped to external resources in any format required. You can customize the mapping to do additional work with or transformations on the complex data types. ++ +This sample defines one complex data type, `cars`, discussed in more detail later in this section. + +* Event hooks to perform actions ++ +The mapping from the internal repository to the external `hrdb` database (`managedUser_systemHrdb`), (defined in the `sync.json` file), includes two script hooks. The first hook is for an `onCreate` event and the second is for an `onUpdate` event. Using these event hooks, OpenIDM logs a statement to the log when a user is created or updated in the external system. In this sample, the script source is included in the mapping. However, a script can also be called from an external file. For more information on event hooks, see xref:../integrators-guide/appendix-scripting.adoc#script-places["Places to Trigger Scripts"] in the __Integrator's Guide__. + +* Custom scripted endpoints ++ +All scripted connectors support the configuration of custom scripted endpoints. These are configured in the provisioner configuration file and allow you to execute custom scripts over REST. This example uses a custom scripted endpoint to reset the database and populate it with data. Custom scripted endpoints are illustrated in the the custom script step< of xref:#build-custom-connector["Building the Custom ScriptedSQL Connector"]. + + +[CAUTION] +==== +Because MySQL cannot "un-hash" user passwords there is no way for a reconciliation operation to retrieve and store the password from MySQL and store it in the managed user object. This issue might impact configurations that support multiple external resources in that passwords might not be synchronized immediately after reconciliation from MySQL to the managed/user repository. Users who are missing from managed/user will be created by the reconciliation but their passwords will be empty. When those users are synchronized to other external resources, they will have empty passwords in those resources. Additional scripting might be required to handle this situation, depending on the requirements of your deployment. +==== +The Groovy scripts required for the sample are located in the `sample3/tools` directory. You will need to customize these scripts to address the requirements of your specific deployment, however, the sample scripts are a good starting point on which to base your customization. + +The scripted connector bundler takes a configuration file, in JSON format (`sample3/data/scriptedsql.json`). This file includes the details of the connection to the MySQL server, and the list of object types and properties that will be used in the sample. You can use this configuration file as the basis for creating your own connector with the custom scripted connector bundler. + +[#sample3-before-you-start] +==== Before You Start + +Before you start with this sample, complete the following steps: + +* Prepare a fresh installation of OpenIDM. (See xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"]). + +* Download and install the link:https://maven.apache.org/install.html[Apache Maven, window=\_blank] build tool. + +* Configure an external MySQL database, as follows: ++ + +. Download link:http://dev.mysql.com/downloads/connector/j/5.1.html[MySQL Connector/J, window=\_blank], version 5.1 or later from the MySQL website. Unpack the delivery, and copy the .jar into the `openidm/bundle` directory. ++ + +[source, console] +---- +$ cp mysql-connector-java-version-bin.jar /path/to/openidm/bundle/ +---- + +. Set up MySQL to listen on localhost, port 3306. You will connect to the database as user `root` with password `password`. ++ +If want to use an existing MySQL instance that runs on a different host or port, adjust the configuration file for the sample (`sample3/data/scriptedsql.json`) before you launch the connector bundler. The default generated configuration is as follows: ++ + +[source, javascript] +---- +"configurationProperties" : { + "username" : "root", + "password" : "password", + "driverClassName" : "com.mysql.jdbc.Driver", + "url" : "jdbc:mysql://localhost:3306/hrdb", +---- + +. Create the `hrdb` database, with which OpenIDM will synchronize its managed user repository. ++ + +[source, console] +---- +$ mysql -u root -p +Enter password: +Welcome to the MySQL monitor. Commands end with ; or \g. +Your MySQL connection id is 58 +Server version: 5.7.10 + +Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + +Oracle is a registered trademark of Oracle Corporation and/or its +affiliates. Other names may be trademarks of their respective +owners. + +Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. +mysql> CREATE DATABASE hrdb CHARACTER SET utf8 COLLATE utf8_bin; +Query OK, 1 row affected (0.00 sec) +mysql> quit +Bye +---- + + + + +[#build-custom-connector] +==== Building the Custom ScriptedSQL Connector + +This section uses the custom scripted connector bundler to generate the classes and configuration files required to build a new connector. The custom connector that you build in this section will be used to complete the sample. + +==== + +. Create a new directory named `create-connector` in the `openidm/samples/sample3` directory and change to that new directory. ++ + +[source, console] +---- +$ mkdir /path/to/openidm/samples/sample3/create-connector +$ cd /path/to/openidm/samples/sample3/create-connector +---- + +. Run the custom scripted connector bundler .jar, with the configuration file for this sample (`sample3/data/scriptedsql.json`). ++ + +[source, console] +---- +$ java -jar ../../../tools/custom-scripted-connector-bundler-4.5.1-20.jar -c ../data/scriptedsql.json +Custom Scripted Connector Bundler for OpenIDM v4.5.1-20 +Generating connector sources for HRDB-ScriptedSQLConnector +---- ++ +This step generates a Maven project (`pom.xml` file) and a `src` directory that contains the packages to be bundled into the connector. + +. In addition to the generated packages, you must add the scripts required to perform operations on your resource. The scripts to access the resource illustrated in this sample are provided in the `sample3/tools` directory. Copy these scripts into the generated `resources/script/hrdb/` directory, so that they can be bundled with the connector. ++ + +[source, console] +---- +$ cp ../tools/* src/main/resources/script/hrdb/ +---- ++ +You can customize these scripts before you bundle them, to suit the requirements of your deployment. For more information about writing Groovy scripts to interact with a resource, see the link:http://openicf.forgerock.org/doc/bootstrap/dev-guide/index.html#chap-groovy-connectors[OpenICF Developer's Guide, window=\_blank]. + +. Use the Maven build tool to build the custom connector, with the configuration and scripts that you provided in the previous steps. ++ +To run this command, you must be in the `create-connector` directory, in which your Maven project (`pom.xml`) is located. ++ + +[source, console] +---- +$ mvn install +[INFO] Scanning for projects... +Downloading: http://maven.forgerock.org/repo/releases/org/forgerock/openicf/connectors/ + connectors-parent/1.5.0.0/connectors-parent-1.5.0.0.pom +Downloaded: http://maven.forgerock.org/repo/releases/org/forgerock/openicf/connectors/ + connectors-parent/1.5.0.0/connectors-parent-1.5.0.0.pom (21 KB at 9.2 KB/sec) +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] Building 1.4.1.0 +[INFO] ------------------------------------------------------------------------ +... +[INFO] Writing OBR metadata +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 48.313 s +[INFO] Finished at: 2015-12-10T14:03:02+02:00 +[INFO] Final Memory: 37M/320M +[INFO] ------------------------------------------------------------------------ +---- ++ +This step generates a connector .jar file (`hrdb-connector-1.4.1.0.jar`) in the `target` directory. This connector .jar will be used in the rest of this sample. + +. Copy the new connector .jar file to the `openidm/connectors` directory, so that it can be picked up by OpenIDM. ++ + +[source, console] +---- +$ cd /path/to/openim/samples/sample3 +$ cp create-connector/target/hrdb-connector-1.4.1.0.jar ../../connectors/ +---- ++ +You now have a custom-built connector that includes all the required files for it to be displayed in the OpenIDM Admin UI. The bundled connector also includes the scripts and provisioner configuration that enable it to be used with OpenIDM. + +. Extract the connector configuration file (`provisioner.openicf-hrdb.json`) from the bundled connector into your sample's `conf` directory. ++ + +[source, console] +---- +$ jar -xvf ../../connectors/hrdb-connector-1.4.1.0.jar conf/provisioner.openicf-hrdb.json + inflated: conf/provisioner.openicf-hrdb.json +---- + +. The generated connector configuration file includes no system actions by default. ++ +Edit the value of the `"systemActions"` property in the connector configuration file, to call a custom script (`tools/ResetDatabaseScript.groovy`) over the REST interface. This script will reset the `hrdb` database and populate it with sample data. ++ +The edited excerpt of the `conf/provisioner.openicf-hrdb.json` file should appear as follows: ++ + +[source, javascript] +---- +"systemActions": [ + { + "scriptId": "ResetDatabase", + "actions": [ + { + "systemType": ".*HRDBConnector", + "actionType": "Groovy", + "actionFile": "tools\/ResetDatabaseScript.groovy" + } + ] + } +], +---- ++ +Currently, only Groovy scripts are supported for these types of actions. + +. Finally, add the generated HTML template file to the UI extensions folder, to enable the new connector to be viewed and configured in the Admin UI. ++ +Inside the connector jar, locate the file that contains the string `1.4.html`. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ jar -tvf connectors/hrdb-connector-1.4.1.0.jar | grep "1.4.html" + 12775 Thu Dec 10 14:00:22 SAST 2015 ui/org.forgerock.openicf.connectors.hrdb.HRDBConnector_1.4.html +---- ++ +Create a new extension directory for the connector template. ++ + +[source, console] +---- +$ mkdir -p ui/admin/extension/templates/connector +---- ++ +Extract the HTML template file that you found in the preceding step and then move it into that directory ++ + +[source, console] +---- +$ jar -xvf connectors/hrdb-connector-1.4.1.0.jar ui/org.forgerock.openicf.connectors.hrdb.HRDBConnector_1.4.html +inflated: ui/org.forgerock.openicf.connectors.hrdb.HRDBConnector_1.4.html +$ mv ui/org.forgerock.openicf.connectors.hrdb.HRDBConnector_1.4.html ui/admin/extension/templates/connector +---- + +==== + + +[#run-sample3] +==== Run the Sample + + +==== + +. Start OpenIDM with the configuration for sample 3. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample3 +Executing ./startup.sh... +Using OPENIDM_HOME: /path/to/openidm +Using PROJECT_HOME: /path/to/openidm/samples/sample3/ +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/samples/sample3//conf/logging.properties +Using boot properties at /path/to/openidm/samples/sample3/conf/boot/boot.properties +-> OpenIDM ready +---- + +. Run the custom script described in the previous section to reset the database and populate it with sample data. ++ +You can run the script again, at any point, to reset the database. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/hrdb?_action=script&scriptId=ResetDatabase" +{ + "actions": [ + { + "result": "Database reset successful." + } + ] +} +---- ++ +The `hrdb` database should now be populated with sample data. ++ +You can review the contents of the database as follows: ++ + +[source, console] +---- +$ mysql -u root -p +Enter password: +... +mysql > use hrdb; +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 +mysql > select * from users; + ++----+--------+--------------+-----------+----------+---------------+--------... +| id | uid | password | firstname | lastname | fullname | email ... ++----+--------+------------------------------------------+-----------+-------... +| 1 | bob | e38ad2149... | Bob | Fleming | Bob Fleming | Bob.Fle... +| 2 | rowley | 2aa60a8ff... | Rowley | Birkin | Rowley Birkin | Rowley.... +| 3 | louis | 1119cfd37... | Louis | Balfour | Louis Balfour | Louis.B... +| 4 | john | a1d7584da... | John | Smith | John Smith | John.Sm... +| 5 | jdoe | edba955d0... | John | Doe | John Doe | John.Do... ++----+--------+------------------------------------------+-----------+-------... +5 rows in set (0.00 sec) +---- ++ + +[NOTE] +====== +The passwords in the output shown above are hashed to the SHA-1 standard, as they cannot be read into OpenIDM as clear text. The SHA-1 Hash function is used for compatibility reasons. Use a more secure algorithm in a production database. +====== + +==== + + +[#reconcile-sample3] +==== Reconciling the Repository + + +==== + +. The mapping configuration file (`sync.json`) for this sample includes the mapping `systemHrdb_managedUser`, which synchronizes users from the source `hrdb` database with the target OpenIDM repository. ++ +You can test this part of the sample by using the `curl` command-line utility, or the OpenIDM Administration UI. ++ + +* To reconcile the repository by using the Administration UI: ++ + +.. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ + +[WARNING] +====== +To protect your deployment in production, change the default administrative password. To do so, select Self-Service from the dropdown list at the top right of the screen and click Change Password. +Return to the Admin View to continue with the sample. (Select Admin View from the top right dropdown list.) +====== + +.. Select Configure > Mappings. ++ +The Mappings page shows two configured mappings, one from the `hrdb` database to the OpenIDM repository (`managed/user`), and one in the opposite direction. + +.. Click the first mapping (systemHrdb_managedUser) and click Reconcile Now. + + +* To reconcile the repository by using the command-line, launch the reconciliation operation with the following command: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +"http://localhost:8080/openidm/recon?_action=recon&mapping=systemHrdb_managedUser&waitForCompletion=true" +{ + "state": "SUCCESS", + "_id": "f3c618aa-cc3b-49ed-9a3a-00b012db2513" +} +---- + ++ +The reconciliation operation creates the five users from the MySQL database in the OpenIDM repository. + +. Retrieve the list of users from the repository. ++ + +* To retrieve the users in the repository from the Admin UI: ++ + +.. Select Manage > User to display the User List. ++ +The five users from the `hrdb` database have been reconciled to the OpenIDM repository. + +.. To retrieve the details of a specific user, click that user entry. + + +* To retrieve the users from the repository by using the command-line, query the IDs in the repository as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "9d7c304a-fd89-4b58-bd6a-99b2a6a94691", + "_rev": "1" + }, + { + "_id": "53479e98-5460-421c-9e81-0f3a7cc45881", + "_rev": "1" + }, + { + "_id": "4103b904-c7d6-45c2-a9ca-8e563a975fa8", + "_rev": "1" + }, + { + "_id": "1ea17866-aaed-4c51-b3a8-5fa8eb600e04", + "_rev": "1" + }, + { + "_id": "074588a6-64f8-4cce-bb2f-33490aab90ae", + "_rev": "1" + } + ], + "resultCount": 5, + "pagedResultsCookie": null, + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "remainingPagedResults": -1 +} +---- ++ +To retrieve a complete user record, query the userName of the individual user entry. The following query returns the record for the user `Rowley Birkin`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/?_queryId=for-userName&uid=rowley" +{ + "result": [ + { + "_id": "53479e98-5460-421c-9e81-0f3a7cc45881", + "_rev": "1", + "mail": "Rowley.Birkin@example.com", + "userName": "rowley", + "sn": "Birkin", + "organization": "SALES", + "givenName": "Rowley", + "cars": [ + { + "year": "2013", + "make": "BMW", + "model": "328ci" + }, + { + "year": "2010", + "make": "Lexus", + "model": "ES300" + } + ], + "accountStatus": "active", +... + } +---- + ++ +Regardless of how you have retrieved Rowley Birkin's entry, note the `cars` property in this user's entry. This property demonstrates a complex object, stored in JSON format in the user entry, as a list that contains multiple objects. In the MySQL database, the `car` table joins to the `users` table through a `cars.users_id` column. The Groovy scripts read this data from MySQL and repackage it in a way that OpenIDM can understand. With support for complex objects, the data is passed through to OpenIDM as a list of `car` objects. Data is synchronized from OpenIDM to MySQL in the same way. Complex objects can also be nested to any depth. ++ +Group membership (not demonstrated here) is maintained with a traditional "join table" in MySQL (`groups_users`). OpenIDM does not maintain group membership in this way, so the Groovy scripts do the work to translate membership between the two resources. + +==== + + +[#sample3-paging] +==== Using Paging With Sample 3 + +All OpenICF connectors from version 1.4 onwards support the use of paging parameters to restrict query results. The following command indicates that only two records should be returned (`_pageSize=2`) and that the records should be sorted according to their `timestamp` and `_id` (`_sortKeys=timestamp,id`). Including the `timestamp` in the sort ensures that, as you page through the set, changes to records that have already been visited are not lost. Instead, those records are pushed onto the last page: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/hrdb/account?_queryFilter=uid+sw+%22%22&_pageSize=2&_sortKeys=timestamp,id" +{ + "result": [ + { + "_id": "1", + "email": "Bob.Fleming@example.com", + "cars": [ + { + "year": "1979", + "make": "Ford", + "model": "Pinto" + } + ], + "uid": "bob", + "organization": "HR", + "firstName": "Bob", + "fullName": "Bob Fleming", + "lastName": "Fleming" + }, + { + "_id": "2", + "email": "Rowley.Birkin@example.com", + "cars": [ + { + "year": "2013", + "make": "BMW", + "model": "328ci" + } + ], + "uid": "rowley", + "organization": "SALES", + "firstName": "Rowley", + "fullName": "Rowley Birkin", + "lastName": "Birkin" + } + ], + "resultCount": 2, + "pagedResultsCookie": "2015-12-10 14:16:46.0,2", + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "remainingPagedResults": -1 +} +---- +The `pagedResultsCookie` is used by the server to keep track of the position in the search results. You can ignore the `"remainingPagedResults": -1` in the output. The real value of this property is not returned because the scripts that the connector uses do not do any counting of the records in the resource. + +Using the `pagedResultsCookie` from the previous step, run a similar query, to retrieve the following set of records in the database. Note that the value of the `pagedResultsCookie` must be URL-encoded, as shown in the following example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/hrdb/account?_queryId=query-all-ids&_pageSize=2&_sortKeys=timestamp,id&_pagedResultsCookie=2015-12-10+14%3A16%3A46.0%2C2" +{ + "result": [ + { + "_id": "3", + "uid": "louis" + }, + { + "_id": "4", + "uid": "john" + } + ], + "resultCount": 2, + "pagedResultsCookie": "2015-12-10 14:16:46.0,4", + "totalPagedResultsPolicy": "NONE", + "totalPagedResults": -1, + "remainingPagedResults": -1 +} +---- +For more information about paging support, see xref:../integrators-guide/chap-data.adoc#paging-query-results["Paging and Counting Query Results"] in the __Integrator's Guide__. + + + +[#sample-scripted-rest] +=== Sample - Using the Groovy Connector Toolkit to Connect to OpenDJ With ScriptedREST + +This sample uses the Groovy Connector Toolkit to implement a ScriptedREST connector, which interacts with the OpenDJ REST API. + +The Groovy Connector Toolkit is bundled with OpenIDM 4.5, in the JAR `openidm/connectors/groovy-connector-1.4.2.1.jar`. + +The connector configuration file for this sample (`samples/scriptedrest2dj/conf/provisioner.openicf-scriptedrest.json`) indicates the ScriptedREST implementation of the Groovy connector as follows: + +[source, javascript] +---- +{ + "name": "scriptedrest", + "connectorRef": { + "connectorHostRef": "#LOCAL", + "connectorName": "org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConnector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "bundleVersion": "[1.4.0.0,2.0.0.0)" + }, +... +---- +The Groovy scripts required for the sample are located in the `samples/scriptedrest2dj/tools` directory. You will need to customize these scripts to address the requirements of your specific deployment, however, the sample scripts are a good starting point on which to base your customization. + +[IMPORTANT] +==== +The Rest2ldap HTTP endpoint provided with OpenDJ is an evolving interface. As such, compatibility between versions is not guaranteed. This sample is designed to work with OpenDJ 3.0.0 and does not work, out of the box, with OpenDJ 3.5.0. +==== + +[#sample-scripted-rest-opendj] +==== Setting Up OpenDJ + +This sample assumes an OpenDJ server, running on the localhost. Follow these steps to install and configure an OpenDJ instance. + +==== + +. Download and extract the OpenDJ zip archive from link:https://forgerock.org/downloads/[https://forgerock.org/downloads/, window=\_blank]. + +. Install OpenDJ using the command-line setup, as follows: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ ./setup --cli \ + --hostname localhost \ + --ldapPort 1389 \ + --rootUserDN "cn=Directory Manager" \ + --rootUserPassword password \ + --adminConnectorPort 4444 \ + --addBaseEntry \ + --baseDN dc=com \ + --acceptLicense \ + --no-prompt +... +Configuring Directory Server ..... Done. +Creating Base Entry dc=com ..... Done. +Starting Directory Server ....... Done. +... +---- ++ +The sample assumes the following configuration: ++ + +* The server is installed on the localhost. + +* The server listens for LDAP connections on port 1389. + +* The administration connector port is 4444. + +* The root user DN is `cn=Directory Manager`. + +* The root user password is `password`. + + +. Configure the OpenDJ server for replication. ++ +To enable LiveSync, this server must be configured for replication, even if it does not actually participate in a replication topology. The following commands configure the server for replication. ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./dsconfig create-replication-server \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --provider-name "Multimaster Synchronization" \ + --set replication-port:8989 \ + --set replication-server-id:2 \ + --type generic \ + --trustAll \ + --no-prompt + +$ ./dsconfig create-replication-domain \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --provider-name "Multimaster Synchronization" \ + --domain-name example_com \ + --set base-dn:dc=example,dc=com \ + --set replication-server:localhost:8989 \ + --set server-id:3 \ + --type generic \ + --trustAll \ + --no-prompt +---- + +. Enable HTTP access to the OpenDJ directory server as follows: ++ + +[source, console] +---- +$ ./dsconfig set-connection-handler-prop \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --handler-name "HTTP Connection Handler" \ + --set enabled:true \ + --set listen-port:8090 \ + --no-prompt \ + --trustAll +---- + +. Enable the OpenDJ HTTP access log. ++ + +[source, console] +---- +$ ./dsconfig set-log-publisher-prop \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --publisher-name "File-Based HTTP Access Logger" \ + --set enabled:true \ + --no-prompt \ + --trustAll +---- + +. Import the LDIF data required for the sample. ++ + +[source, console] +---- +$ ./ldapmodify \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --hostname localhost \ + --port 1389 \ + --filename /path/to/openidm/samples/scriptedrest2dj/data/ldap.ldif +Processing ADD request for dc=example,dc=com +ADD operation successful for DN dc=example,dc=com +Processing ADD request for ou=Administrators,dc=example,dc=com +ADD operation successful for DN ou=Administrators,dc=example,dc=com +Processing ADD request for uid=idm,ou=Administrators,dc=example,dc=com +ADD operation successful for DN uid=idm,ou=Administrators,dc=example,dc=com +Processing ADD request for ou=People,dc=example,dc=com +ADD operation successful for DN ou=People,dc=example,dc=com +Processing ADD request for ou=Groups,dc=example,dc=com +ADD operation successful for DN ou=Groups,dc=example,dc=com +---- + +. To configure the mapping between JSON resources and LDAP entries, copy the the configuration file for the HTTP connection handler (`scriptedrest2dj/data/http-config.json`) to OpenDJ's configuration directory. ++ + +[source, console] +---- +$ cd /path/to/opendj +$ cp /path/to/openidm/samples/scriptedrest2dj/data/http-config.json config/ +---- + +. Restart OpenDJ for the configuration change to take effect. ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./stop-ds --restart +Stopping Server... +The Directory Server has started successfully +---- + +==== +OpenDJ is now configured for this sample. + + +[#sample-scripted-rest-running] +==== Running the Sample + +This section illustrates the basic CRUD operations on users and groups using the ScriptedREST connector and the OpenDJ REST API. Note that the power of the Groovy connector is in the associated Groovy scripts, and their application in your particular deployment. The scripts provided with this sample are specific to the sample and customization of the scripts is required. + +==== + +. Start OpenIDM with the configuration for the ScriptedREST sample. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/scriptedrest2dj/ +---- + +. Check the connector configuration is correct by obtaining the status of the connector, over REST. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/scriptedrest?_action=test" +{ + "name": "scriptedrest", + "enabled": true, + "config": "config/provisioner.openicf/scriptedrest", + "objectTypes": [ + "__ALL__", + "account", + "group" + ], + "connectorRef": { + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "connectorName": "org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConnector", + "bundleVersion": "[1.4.0.0,2.0.0.0)" + }, + "displayName": "Scripted REST Connector", + "ok": true +} +---- + +. Create a group entry on the OpenDJ server. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "_id" : "group1" + }' \ + "http://localhost:8080/openidm/system/scriptedrest/group?_action=create" +{ + "_id": "group1", + "cn": "group1", + "members": null, + "lastModified": null, + "created": "2014-09-24T17:34:27Z", + "displayName": "group1" +} +---- + +. Create a user entry on the OpenDJ server. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "givenName" : "Steven", + "familyName" : "Carter", + "emailAddress" : "scarter@example.com", + "telephoneNumber" : "444-444-4444", + "password" : "Passw0rd", + "displayName" : "Steven.Carter", + "uid" : "scarter" + }' \ + http://localhost:8080/openidm/system/scriptedrest/account?_action=create +{ + "_id": "scarter", + "displayName": "Steven.Carter", + "uid": "scarter", + "groups": null, + "familyName": "Carter", + "emailAddress": "steven.carter@example.com", + "givenName": "Steven", + "created": "2014-09-24T17:35:46Z", + "telephoneNumber": "444-444-4444" +} +---- ++ +Notice that at this stage, the user is not a member of any group. + +. Update Steven Carter's entry, by modifying his telephone number. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "givenName" : "Steven", + "familyName" : "Carter", + "emailAddress" : "scarter@example.com", + "telephoneNumber" : "555-555-5555", + "password" : "Passw0rd", + "displayName" : "Steven.Carter", + "uid" : "scarter" + }' \ + http://localhost:8080/openidm/system/scriptedrest/account/scarter +{ + "_id": "scarter", + "displayName": "Steven.Carter", + "uid": "scarter", + "groups": null, + "familyName": "Carter", + "emailAddress": "steven.carter@example.com", + "givenName": "Steven", + "created": "2014-09-24T17:35:46Z", + "telephoneNumber": "555-555-5555" +} +---- + +. Add Steven Carter to the group you created previously, by updating the group entry. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "_id" : "group1", + "members" : [{"_id" : "scarter"}] + }' \ + http://localhost:8080/openidm/system/scriptedrest/group/group1 +{ + "_id": "group1", + "cn": "group1", + "members": [ + { + "displayName": "Steven.Carter", + "_id": "scarter" + } + ], + "lastModified": "2014-09-24T17:31:42Z", + "created": "2014-09-24T17:27:37Z", + "displayName": "group1" +} +---- + +. Read Steven Carter's entry, to verify that he is now a member of group1. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + http://localhost:8080/openidm/system/scriptedrest/account/scarter +{ + "_id": "scarter", + "displayName": "Steven.Carter", + "uid": "scarter", + "groups": [ + { + "_id": "group1" + } + ], + "familyName": "Carter", + "emailAddress": "steven.carter@example.com", + "givenName": "Steven", + "created": "2014-09-24T17:31:04Z", + "telephoneNumber": "555-555-5555" +} +---- + +. Read the group entry to verify its members. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + http://localhost:8080/openidm/system/scriptedrest/group/group1 +{ + "_id": "group1", + "cn": "group1", + "members": [ + { + "displayName": "Steven.Carter", + "_id": "scarter" + } + ], + "lastModified": "2014-09-24T17:31:42Z", + "created": "2014-09-24T17:27:37Z", + "displayName": "group1" +} +---- + +. Delete the user and group entries, returning the OpenDJ server to its initial state. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + http://localhost:8080/openidm/system/scriptedrest/account/scarter +{ + "_id": "scarter" +} +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + http://localhost:8080/openidm/system/scriptedrest/group/group1 +{ + "_id": "group1" +} +---- + +==== + + + +[#sample-scripted-crest] +=== Using the Groovy Connector Toolkit to Connect to OpenDJ With ScriptedCREST + +This sample uses the Groovy Connector Toolkit to implement a ScriptedCREST connector, which interacts with the ForgeRock Commons REST (CREST) API to connect to an OpenDJ instance. The main difference between a CREST-based API and a generic REST API is that the CREST API is inherently recognizable by all ForgeRock products. As such, the sample can leverage CREST resources in the groovy scripts, to create CREST requests. + +The Groovy Connector Toolkit is bundled with OpenIDM 4.5, in the JAR `openidm/connectors/groovy-connector-1.4.2.1.jar`. + +The connector configuration file for this sample (`samples/scriptedcrest2dj/conf/provisioner.openicf-scriptedcrest.json`) indicates the ScriptedCREST implementation of the Groovy Connector Toolkit as follows: + +[source, javascript] +---- +{ + "name": "scriptedcrest", + "connectorRef": { + "connectorHostRef": "#LOCAL", + "connectorName": "org.forgerock.openicf.connectors.scriptedcrest.ScriptedCRESTConnector", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "bundleVersion": "[1.4.0.0,2.0.0.0)" + }, +... +---- +The Groovy scripts required for the sample are located in the `samples/scriptedcrest2dj/tools` directory. You will need to customize these scripts to address the requirements of your specific deployment, however, the sample scripts are a good starting point on which to base your customization. + +[IMPORTANT] +==== +The Rest2ldap HTTP endpoint provided with OpenDJ is an evolving interface. As such, compatibility between versions is not guaranteed. This sample is designed to work with OpenDJ 3.0.0 and does not work, out of the box, with OpenDJ 3.5.0. +==== + +[#sample-scripted-crest-opendj] +==== Setting Up OpenDJ + +This sample assumes an OpenDJ server, running on the localhost. Follow these steps to install and configure an OpenDJ instance. + +==== + +. Download and extract the OpenDJ zip archive from link:https://forgerock.org/downloads/[https://forgerock.org/downloads/, window=\_blank]. + +. Install OpenDJ using the command-line setup, as follows: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ ./setup --cli \ + --hostname localhost \ + --ldapPort 1389 \ + --rootUserDN "cn=Directory Manager" \ + --rootUserPassword password \ + --adminConnectorPort 4444 \ + --addBaseEntry \ + --baseDN dc=com \ + --acceptLicense \ + --no-prompt +... +Configuring Directory Server ..... Done. +Creating Base Entry dc=com ..... Done. +Starting Directory Server ....... Done. +... +---- ++ +The sample assumes the following configuration: ++ + +* The server is installed on the localhost. + +* The server listens for LDAP connections on port 1389. + +* The administration connector port is 4444. + +* The root user DN is `cn=Directory Manager`. + +* The root user password is `password`. + + +. Configure the OpenDJ server for replication. ++ +To enable liveSync, this server must be configured for replication, even if it does not actually participate in a replication topology. The following commands configure the server for replication. ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./dsconfig create-replication-server \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --provider-name "Multimaster Synchronization" \ + --set replication-port:8989 \ + --set replication-server-id:2 \ + --type generic \ + --trustAll \ + --no-prompt + +$ ./dsconfig create-replication-domain \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --provider-name "Multimaster Synchronization" \ + --domain-name example_com \ + --set base-dn:dc=example,dc=com \ + --set replication-server:localhost:8989 \ + --set server-id:3 \ + --type generic \ + --trustAll \ + --no-prompt +---- + +. Enable HTTP access to the OpenDJ directory server as follows: ++ + +[source, console] +---- +$ ./dsconfig set-connection-handler-prop \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --handler-name "HTTP Connection Handler" \ + --set enabled:true \ + --set listen-port:8090 \ + --no-prompt \ + --trustAll +---- + +. Enable the OpenDJ HTTP access log. ++ + +[source, console] +---- +$ ./dsconfig set-log-publisher-prop \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --publisher-name "File-Based HTTP Access Logger" \ + --set enabled:true \ + --no-prompt \ + --trustAll +---- + +. Import the LDIF data required for the sample. ++ + +[source, console] +---- +$ ./ldapmodify \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --hostname localhost \ + --port 1389 \ + --filename /path/to/openidm/samples/scriptedcrest2dj/data/ldap.ldif +Processing ADD request for dc=example,dc=com +ADD operation successful for DN dc=example,dc=com +Processing ADD request for ou=Administrators,dc=example,dc=com +ADD operation successful for DN ou=Administrators,dc=example,dc=com +Processing ADD request for uid=idm,ou=Administrators,dc=example,dc=com +ADD operation successful for DN uid=idm,ou=Administrators,dc=example,dc=com +Processing ADD request for ou=People,dc=example,dc=com +ADD operation successful for DN ou=People,dc=example,dc=com +Processing ADD request for ou=Groups,dc=example,dc=com +ADD operation successful for DN ou=Groups,dc=example,dc=com +---- + +. To configure the mapping between JSON resources and LDAP entries, copy the the configuration file for the HTTP connection handler (`scriptedcrest2dj/data/http-config.json`) to OpenDJ's configuration directory. ++ + +[source, console] +---- +$ cd /path/to/opendj +$ cp /path/to/openidm/samples/scriptedcrest2dj/data/http-config.json config/ +---- + +. Restart OpenDJ for the configuration change to take effect. ++ + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./stop-ds --restart +Stopping Server... +The Directory Server has started successfully +---- + +==== +OpenDJ is now configured for this sample. + + +[#sample-scripted-crest-running] +==== Running the Sample + +This section illustrates the basic CRUD operations on users and groups using the ScriptedCREST connector implementation and the OpenDJ REST API. Note that the power of the Groovy connector is in the associated Groovy scripts, and their application in your specific deployment. The scripts provided with this sample are specific to the sample and customization of the scripts is required. + +==== + +. Start OpenIDM with the configuration for the ScriptedCREST sample. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/scriptedcrest2dj/ +---- + +. Check the connector configuration is correct by obtaining the status of the connector, over REST. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/scriptedcrest?_action=test" +{ + "ok": true, + "connectorRef": { + "bundleVersion": "[1.4.0.0,2.0.0.0)", + "bundleName": "org.forgerock.openicf.connectors.groovy-connector", + "connectorName": "org.forgerock.openicf.connectors.scriptedcrest.ScriptedCRESTConnector" + }, + "objectTypes": [ + "groups", + "users" + ], + "config": "config/provisioner.openicf/scriptedcrest", + "enabled": true, + "name": "scriptedcrest" +} +---- + +. Create a group entry on the OpenDJ server. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "_id" : "group1" + }' \ + "http://localhost:8080/openidm/system/scriptedcrest/groups?_action=create" +{ + "_rev": "0000000028f53bdf", + "_id": "group1", + "displayName": "group1", + "meta": { + "created": "2014-10-17T07:43:13Z" + } +} +---- + +. Create a user entry on the OpenDJ server. ++ + +[source, console] +---- +$ curl \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name": { + "familyName": "Carter", + "givenName" : "Steven" + }, + "contactInformation": { + "emailAddress" : "scarter@example.com", + "telephoneNumber" : "444-444-4444" + }, + "password" : "TestPassw0rd", + "displayName" : "Steven.Carter", + "_id" : "scarter" + }' \ + "http://localhost:8080/openidm/system/scriptedcrest/users?_action=create" +{ + "_rev": "00000000d84482de", + "meta": { + "created": "2014-10-17T08:07:46Z" + }, + "userName": "scarter@example.com", + "contactInformation": { + "emailAddress": "scarter@example.com", + "telephoneNumber": "444-444-4444" + }, + "name": { + "givenName": "Steven", + "familyName": "Carter" + }, + "displayName": "Steven.Carter", + "_id": "scarter" +} +---- ++ +Notice that at this stage, the user is not a member of any group. + +. Update Steven Carter's entry, by modifying his telephone number. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "name": { + "familyName": "Carter", + "givenName" : "Steven" + }, + "contactInformation": { + "emailAddress" : "scarter@example.com", + "telephoneNumber" : "555-555-5555" + }, + "password" : "TestPassw0rd", + "displayName" : "Steven.Carter", + "_id" : "scarter" + }' \ + "http://localhost:8080/openidm/system/scriptedcrest/users/scarter" +{ + "_rev": "00000000eb8ba31c", + "meta": { + "created": "2014-10-17T08:07:46Z", + "lastModified": "2014-10-17T08:25:05Z" + }, + "userName": "scarter@example.com", + "contactInformation": { + "emailAddress": "scarter@example.com", + "telephoneNumber": "555-555-5555" + }, + "name": { + "givenName": "Steven", + "familyName": "Carter" + }, + "displayName": "Steven.Carter", + "_id": "scarter" +} +---- + +. Add Steven Carter to the group you created previously, by updating the members of the group entry. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --header "If-Match: *" \ + --request PUT \ + --data '{ + "_id" : "group1", + "members" : [{"_id" : "scarter"}] + }' \ + "http://localhost:8080/openidm/system/scriptedcrest/groups/group1" +{ + "_rev": "0000000011ed6ea1", + "members": [ + { + "displayName": "Steven.Carter", + "_id": "scarter" + } + ], + "_id": "group1", + "displayName": "group1", + "meta": { + "created": "2014-10-17T07:43:13Z", + "lastModified": "2014-10-17T08:26:41Z" + } +} +---- + +. Read Steven Carter's entry, to verify that he is now a member of group1. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/scriptedcrest/users/scarter" +{ + "_rev": "00000000eb8ba31c", + "groups": [ + { + "_id": "group1" + } + ], + "meta": { + "created": "2014-10-17T08:07:46Z", + "lastModified": "2014-10-17T08:25:05Z" + }, + "userName": "scarter@example.com", + "contactInformation": { + "emailAddress": "scarter@example.com", + "telephoneNumber": "555-555-5555" + }, + "name": { + "givenName": "Steven", + "familyName": "Carter" + }, + "displayName": "Steven.Carter", + "_id": "scarter" +} +---- + +. Read the group entry to verify its members. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/scriptedcrest/groups/group1" +{ + "_rev": "0000000011ed6ea1", + "members": [ + { + "displayName": "Steven.Carter", + "_id": "scarter" + } + ], + "_id": "group1", + "displayName": "group1", + "meta": { + "created": "2014-10-17T07:43:13Z", + "lastModified": "2014-10-17T08:26:41Z" + } +} +---- + +. Delete the user and group entries, returning the OpenDJ server to its initial state. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/system/scriptedcrest/users/scarter" +{ + "_id": "scarter" +} +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/system/scriptedcrest/groups/group1" +{ + "_id": "group1" +} +---- + +==== + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-kerberos-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-kerberos-sample.adoc new file mode 100644 index 000000000..169de9df0 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-kerberos-sample.adoc @@ -0,0 +1,493 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-kerberos-sample] +== Scripted Kerberos Connector Sample + +New in OpenIDM 4.5.0, the scripted Kerberos connector sample demonstrates how to manage Kerberos user principals and how to reconcile user principals with OpenIDM managed user objects. + +The connector configuration (`/path/to/openidm/samples/kerberos/conf/provisioner.openicf-kerberos.json)`) assumes that OpenIDM is running on a host that is separate from the Kerberos host. + +This sample assumes that the default realm is `EXAMPLE.COM` and that there is an existing user principal `openidm/admin`. Adjust the sample to match your Kerberos realm and principals. + +[#edit-kerberos-connector] +=== Editing the Kerberos Connector Configuration + +Before you run this sample, edit the connector configuration file to match your Kerberos environment. Specifically, set the correct values for the following properties: +-- + +`host`:: +The host name or IP address of the machine on which Kerberos is running. + +`port`:: +The SSH port on that machine. + ++ +Default: `22` (the default SSH port) + +`user`:: +The username of the account that is used to connect to the SSH server. + +`password`:: +The password of the account that is used to connect to the SSH server. + +`prompt`:: +A string that represents the remote SSH session prompt. This must be the exact prompt string, in the format `username@target:`, for example `root@localhost:~$`. The easiest way to obtain this string is to `ssh` into the machine and copy paste the prompt. + +`customConfiguration`:: +The details of the admin user principal and the default realm. + ++ +This example assumes an admin user principal of `openidm/admin`. + ++ +For more information on setting this property, see xref:../connectors-guide/chap-kerberos.adoc#customConfiguration[customConfiguration] in the __Connectors Guide__. + +[#customSensitiveConfiguration] +`customSensitiveConfiguration`:: +The password for the user principal. + ++ +For more information on setting this property, see xref:../connectors-guide/chap-kerberos.adoc#customSensitiveConfiguration[customSensitiveConfiguration] in the __Connectors Guide__. + +-- +Your connector configuration should look something like the following: + +[source, javascript] +---- +... + "configurationProperties" : { + "host" : "192.0.2.0", + "port" : 22, + "user" : "admin", + "password" : "Passw0rd", + "prompt" : "admin@myhost:~$", + "sudoCommand" : "/usr/bin/sudo", + "echoOff" : true, + "terminalType" : "vt102", + "setLocale" : false, + "locale" : "en_US.utf8", + "connectionTimeout" : 5000, + "expectTimeout" : 5000, + "authenticationType" : "PASSWORD", + "throwOperationTimeoutException" : true, + "customConfiguration" : "kadmin { cmd = '/usr/sbin/kadmin.local'; user='openidm/admin'; default_realm='EXAMPLE.COM' }", + "customSensitiveConfiguration" : "kadmin { password = 'Passw0rd'}", + ... +---- +OpenIDM will encrypt all passwords in the configuration when it starts up, or whenever it reloads the configuration file. + +For information about the complete Kerberos connector configuration, see xref:../connectors-guide/chap-kerberos.adoc#ssh-kerberos-config["Configuring the Kerberos Connector"] in the __Connectors Guide__. + +[CAUTION] +==== +Do not modify the value of the `scriptRoots` or `classpath` properties unless you have extracted the scripts from the connector bundle and placed them on the filesystem. +==== + + +[#kerberos-sample-run] +=== Running the Kerberos Sample + +The commands in this section achieve the following: + +. Start OpenIDM and check that the connector can reach the Kerberos server. + +. Create two users in the OpenIDM managed repository. + +. Reconcile the managed repository with the Kerberos server so that the new users are created in Kerberos. + +. Retrieve the details of one of the new Kerberos principals from the server. + +. Delete one of the managed users. + +. Reconcile the managed repository again and note that the corresponding Kerberos principal has been deleted. + + +==== + +. Start OpenIDM with the configuration for the Kerberos sample: ++ + +[source, console] +---- +$ cd /path/to/openidm +---- ++ + +[source, console] +---- +$ startup.sh -p samples/kerberos +---- + +. Test that your connector configuration is correct and that OpenIDM can reach your Kerberos server, with the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system?_action=test" +[ + { + "name": "kerberos", + "enabled": true, + "config": "config/provisioner.openicf/kerberos", + "objectTypes": [ + "__ALL__", + "account" + ], + "connectorRef": { + "bundleName": "org.forgerock.openicf.connectors.kerberos-connector", + "connectorName": "org.forgerock.openicf.connectors.kerberos.KerberosConnector", + "bundleVersion": "1.4.0.0" + }, + "displayName": "Kerberos Connector", + "ok": true + } +] +---- ++ +If the command returns `"ok": true`, as in the preceding output, your configuration is correct and you can continue with the sample. + +. Retrieve a list of the existing user principals in the Kerberos database: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/kerberos/account?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "K/M@EXAMPLE.COM", + "principal": "K/M@EXAMPLE.COM" + }, + { + "_id": "kadmin/admin@EXAMPLE.COM", + "principal": "kadmin/admin@EXAMPLE.COM" + }, + { + "_id": "kadmin/changepw@EXAMPLE.COM", + "principal": "kadmin/changepw@EXAMPLE.COM" + }, + { + "_id": "kadmin/krb1.example.com@EXAMPLE.COM", + "principal": "kadmin/krb1.example.com@EXAMPLE.COM" + }, + { + "_id": "kiprop/krb1.example.com@EXAMPLE.COM", + "principal": "kiprop/krb1.example.com@EXAMPLE.COM" + }, + { + "_id": "krbtgt/EXAMPLE.COM@EXAMPLE.COM", + "principal": "krbtgt/EXAMPLE.COM@EXAMPLE.COM" + }, + { + "_id": "openidm/admin@EXAMPLE.COM", + "principal": "openidm/admin@EXAMPLE.COM" + } + ], + ... +} +---- + +. Create two new managed users, either over REST or by using the Admin UI. ++ +The following command creates users bjensen and scarter over REST. To create similar users by using the Admin UI, select Managed > User and click New User: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-type: application/json" \ + --request POST \ + --data '{ + "userName": "bjensen", + "givenName": "Barbara", + "sn" : "Jensen", + "password" : "Passw0rd", + "displayName" : "Barbara Jensen", + "mail" : "bjensen@example.com" + }' \ + "http://localhost:8080/openidm/managed/user?_action=create" +{ + "_id": "ce3d9b8f-1d15-4950-82c1-f87596aadcb6", + "_rev": "2", + "userName": "bjensen", + "givenName": "Barbara", + "sn": "Jensen", + "displayName": "Barbara Jensen", + "mail": "bjensen@example.com", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-type: application/json" \ + --request POST \ + --data '{ + "userName": "scarter", + "givenName": "Steven", + "sn" : "Carter", + "password" : "Passw0rd", + "displayName" : "Steven Carter", + "mail" : "scarter@example.com" + }' \ + "http://localhost:8080/openidm/managed/user?_action=create" +{ + "_id": "a204ca60-b0fc-42f8-bf93-65bb30131361", + "_rev": "2", + "userName": "scarter", + "givenName": "Steven", + "sn": "Carter", + "displayName": "Steven Carter", + "mail": "scarter@example.com", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +. Run a reconciliation operation between the managed user repository and the Kerberos database to create the new users bjensen and scarter in Kerberos. You can run the reconciliation over REST, or using the Admin UI. ++ +The following command creates runs the reconciliation over REST: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=managedUser_systemKerberos" +{ + "_id": "862ab9ba-d1d9-4058-b6bc-a23a94b68776-234", + "state": "ACTIVE" +} +---- ++ +To run the reconciliation by using the Admin UI, select Configure > Mappings, click on the `managedUser_systemKerberos` mapping, and click Reconcile Now. + +. Retrieve the list of Kerberos user principals again. You should now see bjensen and scarter in this list: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/kerberos/account?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "bjensen@EXAMPLE.COM", + "principal": "bjensen@EXAMPLE.COM" + }, + { + "_id": "scarter@EXAMPLE.COM", + "principal": "scarter@EXAMPLE.COM" + }, + ... + { + "_id": "openidm/admin@EXAMPLE.COM", + "principal": "openidm/admin@EXAMPLE.COM" + } + ], + ... +} +---- + +. Retrieve bjensen's complete user principal from the Kerberos server: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/kerberos/account/bjensen@EXAMPLE.COM" +{ + "_id": "bjensen@EXAMPLE.COM", + "lastFailedAuthentication": "[never]", + "passwordExpiration": "[none]", + "lastSuccessfulAuthentication": "[never]", + "maximumTicketLife": "0 days 10:00:00", + "lastModified": "Tue May 24 04:05:45 EDT 2016 (openidm/admin@EXAMPLE.COM)", + "policy": "user [does not exist]", + "expirationDate": "[never]", + "failedPasswordAttempts": "0", + "maximumRenewableLife": "7 days 00:00:00", + "principal": "bjensen@EXAMPLE.COM", + "lastPasswordChange": "Tue May 24 04:05:45 EDT 2016" +} +---- ++ +Note the default values for properties such as `maximumRenewableLife`. These values are set in your connector configuration. For more information, see xref:../connectors-guide/chap-kerberos.adoc#ssh-kerberos-config["Configuring the Kerberos Connector"] in the __Connectors Guide__. ++ +To perform this step in the Admin UI, select Manage > User, click bjensen's entry, and click the Linked Systems tab to display her corresponding entry on the Kerberos server. + +. Delete the managed user bjensen by specifying her managed object ID in the DELETE request. ++ +First, obtain her ID by querying for her userName: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=userName+eq+'bjensen'" +{ + "result": [ + { + "_id": "ce3d9b8f-1d15-4950-82c1-f87596aadcb6", + "_rev": "3", + "userName": "bjensen", + "givenName": "Barbara", + "sn": "Jensen", + "displayName": "Barbara Jensen", + "mail": "bjensen@example.com", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] + } + ], + ... +} +---- ++ +Now delete the user with ID `ce3d9b8f-1d15-4950-82c1-f87596aadcb6`. This ID will obviously be different in your example. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/user/ce3d9b8f-1d15-4950-82c1-f87596aadcb6" +{ + "_id": "ce3d9b8f-1d15-4950-82c1-f87596aadcb6", + "_rev": "3", + "userName": "bjensen", + "givenName": "Barbara", + "sn": "Jensen", + "displayName": "Barbara Jensen", + "mail": "bjensen@example.com", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- ++ +To delete bjensen's managed user entry by using the Admin UI, select Manage > User, click on bjensen's entry, select the checkbox next to her entry, and click Delete Selected. + +. Reconcile the managed user repository and the Kerberos database again: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=managedUser_systemKerberos" +{ + "_id": "862ab9ba-d1d9-4058-b6bc-a23a94b68776-584", + "state": "ACTIVE" +} +---- + +. Retrieve the list of Kerberos user principals again. The Kerberos principal for bjensen should have been been removed from the list: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/kerberos/account?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "K/M@EXAMPLE.COM", + "principal": "K/M@EXAMPLE.COM" + }, + { + "_id": "kadmin/admin@EXAMPLE.COM", + "principal": "kadmin/admin@EXAMPLE.COM" + }, + { + "_id": "kadmin/changepw@EXAMPLE.COM", + "principal": "kadmin/changepw@EXAMPLE.COM" + }, + { + "_id": "kadmin/krb1.example.com@EXAMPLE.COM", + "principal": "kadmin/krb1.example.com@EXAMPLE.COM" + }, + { + "_id": "kiprop/krb1.example.com@EXAMPLE.COM", + "principal": "kiprop/krb1.example.com@EXAMPLE.COM" + }, + { + "_id": "krbtgt/EXAMPLE.COM@EXAMPLE.COM", + "principal": "krbtgt/EXAMPLE.COM@EXAMPLE.COM" + }, + { + "_id": "scarter@EXAMPLE.COM", + "principal": "scarter@EXAMPLE.COM" + }, + { + "_id": "openidm/admin@EXAMPLE.COM", + "principal": "openidm/admin@EXAMPLE.COM" + } + ], + ... +} +---- + +==== + +[NOTE] +==== +Some user IDs in Kerberos include characters such as a forward slash (`/`) and an "at sign" (`@`) that prevent them from being used directly in a REST URL. For example, `openidm/system/kerberos/account/kadmin/admin@EXAMPLE.COM`, where the ID is `kadmin/admin@EXAMPLE.COM`. To retrieve such entries directly over REST, you must URL-encode the Kerberos ID as follows: + +[source, console] +---- +"http://localhost:8080/openidm/system/kerberos/account/kadmin%2Fadmin%40EXAMPLE.COM" +---- +==== + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-ldap-samples.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-ldap-samples.adoc new file mode 100644 index 000000000..9c857dcea --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-ldap-samples.adoc @@ -0,0 +1,2703 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-ldap-samples] +== LDAP Samples - Reconciling Data Between OpenIDM and One or More LDAP Directories + +This chapter walks you through the LDAP samples (those samples labeled 2, 2b, 2c, 2d, 5, 5b and 6 in the `openidm/samples` directory). For a complete list of the samples provided with OpenIDM, and an overview of each sample, see xref:chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"]. + +[#more-sample-2] +=== Sample 2 - LDAP One Way + +Sample 2 resembles xref:chap-xml-samples.adoc#more-sample-1["First OpenIDM Sample - Reconciling an XML File Resource"], but in sample 2 OpenIDM is connected to a local LDAP server. The sample has been tested with link:http://www.forgerock.org/opendj.html[OpenDJ, window=\_blank], but should work with any LDAPv3-compliant server. + +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. + +[#external-ldap-config-2] +==== LDAP Server Configuration + +Sample 2 expects the following configuration for the external LDAP server: + +* The LDAP server runs on the local host. + +* The LDAP server listens on port 1389. + +* A user with DN `cn=Directory Manager` and password `password` has read access to the LDAP server. + +* Directory data for that server is stored under base DN `dc=example,dc=com`. + +* User objects for that server are stored under base DN `ou=People,dc=example,dc=com`. + +* User objects have the object class `inetOrgPerson`. + +* User objects have the following attributes: ++ + +** `cn` + +** `description` + +** `givenName` + +** `mail` + +** `sn` + +** `telephoneNumber` + +** `uid` + +** `userPassword` + ++ +An example user object follows. ++ + +[source, 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 +---- + +The following steps provide setup instructions for an OpenDJ server. Adjust these instructions if you are using an alternative LDAP server. + +==== + +. Download OpenDJ from ForgeRock's link:https://forgerock.org/downloads/[download site, window=\_top] and extract the zip archive. ++ +The LDIF data for this sample is provided in the file `openidm/samples/sample2/data/Example.ldif`. You will need to import this data during your OpenDJ setup. + +. Install OpenDJ using the command-line setup. ++ +Substitute the `--ldifFile` argument with the path to the `Example.ldif` file in your OpenIDM installation: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ ./setup --cli \ +--hostname localhost \ +--ldapPort 1389 \ +--rootUserDN "cn=Directory Manager" \ +--rootUserPassword password \ +--adminConnectorPort 4444 \ +--baseDN dc=com \ +--ldifFile /path/to/openidm/samples/sample2/data/Example.ldif \ +--acceptLicense \ +--no-prompt +... +Configuring Directory Server ..... Done. +Importing LDIF file /path/to/openidm/samples/sample2/data/Example.ldif ...... Done. +Starting Directory Server ...... Done.. +... +---- + +==== + + +[#install-sample2] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for sample 2. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample2 +---- + + +[#run-sample2] +==== Reconcile the Repository + +The mapping configuration file (`sync.json`) for this sample includes the mapping `systemLdapAccounts_managedUser`, which synchronize users from the source LDAP server with the target OpenIDM repository. + +You can run this part of the sample by using the `curl` command-line utility, or by using the OpenIDM Administration UI. This section provides instructions for both methods. + +[#d5830e1489] +.Run the Sample Using the Command Line +==== + +. Reconcile the repository by running the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser&waitForCompletion=true" +{ + "_id": "b1394d10-29b0-4ccf-81d8-c88948ea121c-4", + "state": "SUCCESS" +} +---- ++ +The reconciliation operation creates the two users from the LDAP server in the OpenIDM repository, assigning the new objects random unique IDs. + +. To retrieve the users from the repository, query their IDs as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "f52df646-7108-45e1-9342-1a17f257b497", + "_rev": "1" + }, + { + "_id": "f7fccf54-e76a-404c-93f0-7486d30f1dc3", + "_rev": "1" + } + ], +... +} +---- + +. To retrieve individual user objects, include the ID in the URL, for example: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/0a5546d6-149b-4f8b-b3be-4afa8a267d45" +{ + "_id": "f7fccf54-e76a-404c-93f0-7486d30f1dc3", + "_rev": "1", + "displayName": "Barbara Jensen", + "description": "Created for OpenIDM", + "givenName": "Barbara", + "mail": "bjensen@example.com", + "sn": "Jensen", + "telephoneNumber": "1-360-229-7105", + "userName": "bjensen", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +==== + +[#d5830e1528] +.Run the Sample Using the Admin UI +==== + +. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ + +[WARNING] +====== +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +====== + +. Click Configure > Mappings. ++ +This page shows one configured mapping, from the `ldap` server to the OpenIDM repository (`managed/user`). ++ + +image::images/sample2-mappings.png[] + +. Click anywhere on the mapping and click Reconcile Now. ++ +The reconciliation operation creates the two users from the LDAP server in the OpenIDM repository. + +. Retrieve the users in the repository. Click Manage > User. ++ +You should now see two users from the LDAP server, reconciled to the OpenIDM repository. + +. When you click a username, you can view the details of that user account. + +==== + + + +[#more-sample-2b] +=== Sample 2b - LDAP Two Way + +Like sample 2, sample 2b connects to an external LDAP server. However, sample 2b has two mappings configured, one from the LDAP server to the OpenIDM repository, and the other from the OpenIDM repository to the LDAP server. + +[#external-ldap-config-2b] +==== External LDAP Configuration + +As demonstrated for sample 2, you can use OpenDJ as an LDAP server. The LDIF data for this sample is provided in the file `openidm/samples/sample2b/data/Example.ldif`. You will need to import this data during your OpenDJ setup. + +Configure the LDAP server as for sample 2, xref:#external-ldap-config-2["LDAP Server Configuration"], but import the LDIF file that is specific to Sample 2b during the setup. The LDAP user must have write access to create users from OpenIDM on the LDAP server. + + +[#install-sample2b] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for sample 2b. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample2b +---- + + +[#run-sample2b] +==== Run the Sample + +The mapping configuration file (`sync.json`) for this sample includes two mappings, `systemLdapAccounts_managedUser`, which synchronizes users from the source LDAP server with the target OpenIDM repository, and `managedUser_systemLdapAccounts`, which synchronizes changes from the OpenIDM repository to the LDAP server. + +You can run this part of the sample by using the `curl` command-line utility, or by using the OpenIDM Administration UI. This section provides instructions for both methods. + +[#d5830e1629] +.Run the Sample Using the Command Line +==== + +. Reconcile the repository over the REST interface by running the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser&waitForCompletion=true" +{ + "state": "SUCCESS", + "_id": "027e25e3-7a33-4858-9080-161c2b40a6bf-2" +} +---- ++ +The reconciliation operation returns a reconciliation run ID and the status of the operation. Reconciliation creates user objects from LDAP in the OpenIDM repository, assigning the new objects random unique IDs. + +. To retrieve the users from the repository, query their IDs as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "d460ed00-74f9-48fb-8cc1-7829be60ddac", + "_rev": "1" + }, + { + "_id": "74fe2d25-4eb1-4148-a3ae-ff80f194b3a6", + "_rev": "1" + } + ], +... +} +---- + +. To retrieve individual user objects, include the ID in the URL, for example: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/d460ed00-74f9-48fb-8cc1-7829be60ddac" +{ + "_id": "d460ed00-74f9-48fb-8cc1-7829be60ddac", + "_rev": "1", + "displayName": "Barbara Jensen", + "description": "Created for OpenIDM", + "givenName": "Barbara", + "mail": "bjensen@example.com", + "telephoneNumber": "1-360-229-7105", + "sn": "Jensen", + "userName": "bjensen", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +. Test the second mapping by creating a user in the OpenIDM repository. ++ + +[source, console] +---- +$ curl \ + --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:8080/openidm/managed/user?_action=create" +{ + "_id": "90d1f388-d8c3-4438-893c-be4e498e7a1c", + "_rev": "1", + "mail": "fdoe@example.com", + "sn": "Doe", + "telephoneNumber": "555-1234", + "userName": "fdoe", + "givenName": "Felicitas", + "description": "Felicitas Doe", + "displayName": "fdoe", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +. By default, __implicit synchronization__ is enabled for mappings __from__ the `managed/user` repository __to__ any external resource. This means that when you update a managed object, any mappings defined in the `sync.json` file that have the managed object as the source are automatically executed to update the target system. For more information, see xref:../integrators-guide/chap-synchronization.adoc#synchronization-mappings-file["Mapping Source Objects to Target Objects"] in the __Integrator's Guide__. ++ +Test that the implicit synchronization has been successful by querying the users in the LDAP directory over REST, as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "dn": "uid=jdoe,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=bjensen,ou=People,dc=example,dc=com", + "dn": "uid=bjensen,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=fdoe,ou=People,dc=example,dc=com", + "dn": "uid=fdoe,ou=People,dc=example,dc=com" + } + ], +... +} +---- ++ +Note the new entry for user `fdoe`. + +. Query the complete entry by including `fdoe`'s ID in the URL. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account/uid=fdoe,ou=People,dc=example,dc=com" +{ + "_id": "uid=fdoe,ou=People,dc=example,dc=com", + "mail": "fdoe@example.com", + "employeeType": null, + "ldapGroups": [], + "telephoneNumber": "555-1234", + "givenName": "Felicitas", + "cn": "fdoe", + "dn": "uid=fdoe,ou=People,dc=example,dc=com", + "uid": "fdoe", + "sn": "Doe", + "description": "Felicitas Doe" +} +---- + +==== + +[#d5830e1727] +.Run the Sample Using the Admin UI +==== + +. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ + +[WARNING] +====== +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +====== + +. Click Configure > Mappings. ++ +This tab shows two configured mappings, one from the `ldap` server to the OpenIDM repository (`managed/user`) and one from the OpenIDM repository to the `ldap` server. + +. Click anywhere on the first mapping and click Reconcile Now. ++ +The reconciliation operation creates the two users from the LDAP server in the OpenIDM repository. + +. Retrieve the users in the repository. Click Manage > User. + +. You should see two users from the LDAP server, reconciled to the OpenIDM repository. + +. To retrieve the details of a specific user, click that username in the User List page. + +. Add a new user in the OpenIDM repository by clicking New User in the User List page. ++ +Complete the user details and click Save. + +. By default, __implicit synchronization__ is enabled for mappings __from__ the `managed/user` repository __to__ any external resource. This means that when you update a managed object, any mappings defined in the `sync.json` file that have the managed object as the source are automatically executed to update the target system. For more information, see xref:../integrators-guide/chap-synchronization.adoc#synchronization-mappings-file["Mapping Source Objects to Target Objects"] in the __Integrator's Guide__. ++ +To test that the implicit synchronization has been successful, look at `fdoe`'s record, and click the Linked Systems tab. The information under this tab includes the external resource to which this user entry is mapped. + +==== + + + +[#more-sample-2c] +=== Sample 2c - Synchronizing LDAP Group Membership + +Like sample 2b, sample 2c connects to an external LDAP server and has mappings from the LDAP server to the OpenIDM repository, and from the OpenIDM repository to the LDAP server. However, in sample 2c, LDAP group memberships are synchronized, in addition to user entries. + +As demonstrated for sample 2, you can use OpenDJ as an LDAP server. The LDIF data for this sample is provided in the file `openidm/samples/sample2c/data/Example.ldif`. + +[#external-ldap-config-2c] +==== External LDAP Configuration + +Configure the LDAP server as for sample 2, xref:#external-ldap-config-2["LDAP Server Configuration"]. The LDAP user must have write access to create users from OpenIDM on the LDAP server. When you configure the LDAP server, import the LDIF file customized for this sample, `openidm/samples/sample2c/data/Example.ldif`. This file includes two LDAP groups: + +[source, 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 +---- +The users with DNs `uid=jdoe,ou=People,dc=example,dc=com` and `uid=bjensen,ou=People,dc=example,dc=com` are also imported with the `Example.ldif` file. + + +[#install-sample2c] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for sample 2c. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample2c +---- + + +[#run-sample2c] +==== Run the Sample + +The mapping configuration file (`sync.json`) for this sample includes two mappings, `systemLdapAccounts_managedUser`, which synchronizes users from the source LDAP server with the target OpenIDM repository, and `managedUser_systemLdapAccounts`, which synchronizes changes from the OpenIDM repository to the LDAP server. + +You can run this part of the sample by using the `curl` command-line utility, or by using the OpenIDM Administration UI. This section provides instructions for both methods. + +[#d5830e1874] +.Run the Sample Using the Command Line +==== + +. Reconcile the repository over the REST interface by running the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser&waitForCompletion=true" +{ + "_id": "6652c292-5309-40e5-b272-b74d67dd95c9-4", + "state": "SUCCESS" +} +---- ++ +The reconciliation operation returns a reconciliation run ID and the status of the operation. Reconciliation creates user objects from LDAP in the OpenIDM repository, assigning the new objects random unique IDs. + +. To retrieve the users from the repository, query their IDs as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "b63fb9a7-99bc-4eb4-8bfd-15f14a756e5b", + "_rev": "1" + }, + { + "_id": "8462fe0c-2ab2-459a-a25e-474474889c9e", + "_rev": "1" + } + ], +... +} +---- + +. To retrieve individual user objects, include the ID in the URL. The following request retrieves the user object for John Doe: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/8462fe0c-2ab2-459a-a25e-474474889c9e" +{ + "_id": "8462fe0c-2ab2-459a-a25e-474474889c9e", + "_rev": "1", + "displayName": "John Doe", + "description": "Created for OpenIDM", + "givenName": "John", + "mail": "jdoe@example.com", + "telephoneNumber": "1-415-599-1100", + "sn": "Doe", + "userName": "jdoe", + "ldapGroups": [ + "cn=openidm,ou=Groups,dc=example,dc=com" + ], + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- ++ +Note that John Doe's user object contains an `ldapGroups` property, the value of which indicates his groups on the LDAP server: ++ + +[source] +---- +"ldapGroups":["cn=openidm,ou=Groups,dc=example,dc=com"] +---- + +. Update John Doe's `ldapGroups` property, to change his membership from the `openidm` group to the `openidm2` group. ++ + +[source, console] +---- +$ curl \ + --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"] + } + ]' \ + "http://localhost:8080/openidm/managed/user?_action=patch&_queryId=for-userName&uid=jdoe" +{ + "displayName": "John Doe", + "description": "Created for OpenIDM", + "givenName": "John", + "mail": "jdoe@example.com", + "telephoneNumber": "1-415-599-1100", + "sn": "Doe", + "userName": "jdoe", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [], + "_id": "8462fe0c-2ab2-459a-a25e-474474889c9e", + "_rev": "2", + "ldapGroups": [ + "cn=openidm2,ou=Groups,dc=example,dc=com" + ] +} +---- ++ +This command changes John Doe's `ldapGroups` property in the OpenIDM repository, from `"cn=openidm,ou=Groups,dc=example,dc=com"` to `"cn=openidm2,ou=Groups,dc=example,dc=com"`. As a result of implicit synchronization, the change is propagated to the LDAP server. John Doe is removed from the first LDAP group and added to the second LDAP group in OpenDJ. + +. You can verify this change by querying John Doe's record on the LDAP server, as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account/uid=jdoe,ou=People,dc=example,dc=com" +{ + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "telephoneNumber": "1-415-599-1100", + "description": "Created for OpenIDM", + "sn": "Doe", + "dn": "uid=jdoe,ou=People,dc=example,dc=com", + "ldapGroups": [ + "cn=openidm2,ou=Groups,dc=example,dc=com" + ], + "uid": "jdoe", + "cn": "John Doe", + "givenName": "John", + "mail": "jdoe@example.com" +} +---- + +==== + +[#d5830e1964] +.Run the Sample Using the Admin UI +==== + +. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ + +[WARNING] +====== +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +====== + +. Click Configure > Mappings. ++ +This window shows two configured mappings, one from the `ldap` server to the OpenIDM repository (`managed/user`) and one from the OpenIDM repository to the `ldap` server. + +. Click anywhere on the first mapping and click Reconcile Now. ++ +The reconciliation operation creates the two users from the LDAP server in the OpenIDM repository. + +. Click Manage > User. Examine the users reconciled from the LDAP server to the internal repository. + +. Examine the two users from the LDAP server that have been reconciled to the OpenIDM repository. + +. To retrieve the details of a specific user, click that username. In this case, click on user `jdoe`. ++ +Examine the information stored for user `jdoe`. Click the Linked Systems tab. The Linked Resource item indicates the external resource on which John Doe's managed object is mapped, in this case, `ldap account`. ++ +In this linked resource, John Doe's `ldapGroups` are displayed. Currently, John Doe is a member of `cn=openidm,ou=Groups,dc=example,dc=com`. + +. Update John Doe's `ldapGroups` property to change his membership from the `openidm` group to the `openidm2` group. Currently, you can only do this over the REST interface, as follows: ++ + +[source, console] +---- +$ curl \ + --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"] + } + ]' \ + "http://localhost:8080/openidm/managed/user?_action=patch&_queryId=for-userName&uid=jdoe" +{ + "displayName": "John Doe", + "description": "Created for OpenIDM", + "givenName": "John", + "mail": "jdoe@example.com", + "telephoneNumber": "1-415-599-1100", + "sn": "Doe", + "userName": "jdoe", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [], + "_id": "8462fe0c-2ab2-459a-a25e-474474889c9e", + "_rev": "2", + "ldapGroups": [ + "cn=openidm2,ou=Groups,dc=example,dc=com" + ] +} +---- ++ +This command changes John Doe's `ldapGroups` property in the OpenIDM repository, from `"cn=openidm,ou=Groups,dc=example,dc=com"` to `"cn=openidm2,ou=Groups,dc=example,dc=com"`. As a result of implicit synchronization, the change is propagated to the LDAP server. John Doe is removed from the first LDAP group and added to the second LDAP group in OpenDJ. + +. You can verify this change by reloading John Doe's user information, clicking Linked Systems, and examining the value of his `ldapGroups` property. + +==== + + + +[#more-sample-2d] +=== Sample 2d - Synchronizing LDAP Groups + +Sample 2d also connects to an external LDAP server. This sample focuses on LDAP Group synchronization. + +As demonstrated for sample 2, you can use OpenDJ as an LDAP server. Before installing OpenDJ, you may need an LDIF file. The OpenIDM installation includes the following LDIF file, customized for this sample: `openidm/samples/sample2d/data/Example.ldif`. If you need a copy of this file, download and install OpenIDM as described in xref:../install-guide/chap-install.adoc#install-openidm["To Install OpenIDM Services"] in the __Installation Guide__. + +[#external-ldap-config-2d] +==== External LDAP Configuration + +Configure the LDAP server as for sample 2, xref:#external-ldap-config-2["LDAP Server Configuration"]. The LDAP user must have write access to create users from OpenIDM on the LDAP server. + +In addition, two LDAP Groups should be created, as found in the following LDIF file: `openidm/samples/sample2d/data/Example.ldif` (if they have not already been added through sample 2c): + +[source, 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 +---- +The user with dn `uid=jdoe,ou=People,dc=example,dc=com` is also imported with the `Example.ldif` file. + +There is an additional user, `bjensen` in the sample LDIF file. This user is essentially a "dummy" user, provided for compliance with RFC 4519, which stipulates that every `groupOfUniqueNames` object must contain at least one `uniqueMember`. `bjensen` is not actually used in this sample. + + +[#install-sample2d] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for sample 2d. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample2d +---- + + +[#run-sample2d] +==== Running the Sample + +The mapping configuration file (`sync.json`) for this sample includes three mappings: + +* `systemLdapAccounts_managedUser` ++ +Synchronizes users from the source LDAP server with the target OpenIDM repository, + +* `managedUser_systemLdapAccounts` ++ +Synchronizes changes from the OpenIDM repository to the LDAP server. + +* `systemLdapGroups_managedGroup` ++ +Synchronizes groups from the source LDAP server with the target OpenIDM repository. + +Due to the similarity with other OpenIDM samples, especially samples 2b and 2c, the focus of this sample is on the mapping unique to this sample, `systemLdapGroups_managedGroup`. + +You can run this part of the sample by using the `curl` command-line utility, or by using the OpenIDM Administration UI. This section provides instructions for both methods. + +[#d5830e2171] +.Run the Sample Using the Command Line +==== + +. Reconcile the repository over the REST interface for the group mapping, `systemLdapGroups_managedGroup` by running the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemLdapGroups_managedGroup&waitForCompletion=true" +---- ++ +The reconciliation operation returns a reconciliation run ID, and the status of the operation. + +. 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. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/group?_queryFilter=true" +---- ++ +The resulting JSON object should include content similar to the following. ++ + +[source, javascript] +---- +{ + "result" : [ { + "dn" : "cn=openidm,ou=Groups,dc=example,dc=com", + "_id" : "837df489-35d6-48d1-81a5-23792b49838a", + "_rev" : "1", + "description" : [ ], + "uniqueMember" : [ "uid=jdoe,ou=People,dc=example,dc=com" ], + "name" : [ "openidm" ] + }, { + "dn" : "cn=openidm2,ou=Groups,dc=example,dc=com", + "_id" : "7575c1c7-86cf-43bc-bf1d-5c9cfc539124", + "_rev" : "1", + "description" : [ ], + "uniqueMember" : [ "uid=bjensen,ou=People,dc=example,dc=com" ], + "name" : [ "openidm2" ] + } ], +... + } +---- + +==== + +[#d5830e2200] +.Run the Sample Using the Admin UI +==== + +. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ + +[WARNING] +====== +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +====== + +. Click Configure > Mappings. ++ +This page shows three configured mappings, from the `ldap` server accounts repository to the OpenIDM repository (`managed/user`), from the OpenIDM repository back to the `ldap` server, and from the `ldap` server group accounts repository to the OpenIDM `managed/group` repository. + +. Click anywhere on the third mapping and click Reconcile Now. ++ +The reconciliation operation creates the two groups from the LDAP server in the OpenIDM repository. + +. Retrieve the groups in the repository by clicking the Association tab below the mapping. Scroll down to Data Association Management. ++ + +image::images/sample2d-groups.png[] ++ +The three groups from the LDAP server (source) have been reconciled to the OpenIDM repository (target). + +==== + + + +[#more-sample-5] +=== Sample 5 - Synchronization of Two LDAP Resources + +Sample 5 demonstrates the flow of data from one external resource to another. The resources are named `LDAP` and `AD` but in the sample, both resources are simulated with XML files. + +You can optionally configure an outbound email service, if you want to receive emailed reconciliation summaries, as described in the following section. + +[#email-sample5] +==== Configure Email for the Sample + +If you do not configure the email service, the functionality of the sample does not change. However, you might see the following message in the OSGi console when you run a reconciliation operation: + +[source, console] +---- +Email service not configured; report not generated. +---- + +==== +To configure OpenIDM to send a reconciliation summary by email, follow these steps: + +. Copy the template `external.email.json` file from the `samples/misc` directory to the `conf` directory of Sample 5: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ cp samples/misc/external.email.json samples/sample5/conf +---- + +. Edit the `external.email.json` file for outbound email, as described in xref:../integrators-guide/chap-mail.adoc#chap-mail["Sending Email"] in the __Integrator's Guide__. + +. Edit the `reconStats.js` file from the `sample5/script` directory. Near the start of the file, configure the OpenIDM email service to send statistics to the email addresses of your choice: ++ + +[source, javascript] +---- +var email = { + //UPDATE THESE VALUES + from : "openidm@example.com", + to : "youremail@example.com", + cc : "idmadmin2@example.com,idmadmin3@example.com", + subject : "Recon stats for " + global.mappingName, + type : "text/html" +}, +template, +... +---- + +==== + + +[#install-sample5] +==== Install the Sample + +No external configuration is required for this sample. Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration of sample 5. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample5 +---- +The XML files that simulate the resources are located in the `openidm/samples/sample5/data/` folder. When you start OpenIDM with the sample 5 configuration, OpenIDM creates the `xml_AD_Data.xml` file, which does not contain users until you run reconciliation. + + +[#run-sample5] +==== Run the Sample + +Run a reconciliation operation, to synchronize the contents of the simulated LDAP resource to the OpenIDM repository. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser&waitForCompletion=true" +---- +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. + +Review the contents of `xml_AD_Data.xml`. It should now contain information for the same user that was present in the startup version of the `xml_LDAP_Data.xml` file. + +Alternatively, you can list users in the AD resource with the following command: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ad/account?_queryId=query-all-ids" + + { + "result" : [ { + "name" : "DDOE1", + "__UID__" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98", + "_id" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98" + } ], +... +} +---- +You can use the `_id` of the user to read the user information from the AD resource, for example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ad/account/8dad9df3-820d-41ea-a3ab-a80c241bbc98" +{ + "email" : [ "mail1@example.com" ], + "name" : "DDOE1", + "__UID__" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98", + "firstname" : "Darth", + "lastname" : "Doe", + "_id" : "8dad9df3-820d-41ea-a3ab-a80c241bbc98" +}[ +---- +To verify that the sample is working, repeat the process. Set up a second user in the `xml_LDAP_Data.xml` file. An example of how that file might appear with a second user (`GDOE1`) is shown here: + +[source, xml] +---- + + + + 1 + TestPassw0rd2 + Darth + Created By XML1 + DDOE1 + mail1@example.com + Doe + + + 2 + TestPassw0rd2 + Garth + Created By XML1 + GDOE1 + mail2@example.com + Doe + + +---- +Rerun the reconciliation and query REST commands shown previously. The reconciliation operation creates the new user from the simulated LDAP resource in the OpenIDM repository. An implicit synchronization operation then creates that user in the AD resource. + + + +[#more-sample-5b] +=== Sample 5b - Failure Compensation With Multiple Resources + +The compensated synchronization mechanism depicted in this sample can help manage the risks associated with synchronizing data across multiple resources. + +Typically, when a managed/user object is changed, implicit synchronization replays that change to all configured external resources. If synchronization fails for one target resource (for example, due to a policy validation failure on the target, or the target being unavailable), the synchronization operation stops at that point. The effect is that a record might be changed in the repository, and in the targets on which synchronization was successful, but not on the failed target, or any targets that would have been synchronized after the failure. This situation can result in disparate data sets across resources. While a reconciliation operation would eventually bring all targets back in sync, reconciliation can be an expensive operation with large data sets. + +The compensated synchronization mechanism ensures that either all resources are synchronized successfully, or that the original change is rolled back. This mechanism uses an `onSync` script hook configured with a `compensate.js` script that can be used to "revert" the partial change to managed/user and to the corresponding external resources. + +Sample 5b is similar to sample 5 in that it simulates two external resources with XML files (located in the `sample5b/data` directory). The `xml_LDAP_Data.xml` file simulates an LDAP data source. OpenIDM creates the `xml_AD_Data.xml` file when you start OpenIDM with the sample. Sample 5b adds the `onSync` script hook to the process, configured in the `sample5b/conf/managed.json` file. + +The following excerpt of the `managed.json` file shows the `onSync` hook, which calls the `compensate.js` script, provided in the `/path/to/openidm/bin/defaults/script` directory. + +[source, javascript] +---- +... +}, +"onSync" : { + "type" : "text/javascript", + "file" : "compensate.js" +}, +---- +You can use the `onSync` script hook to ensure that changes made in the repository are synchronized to all external resources, or that no changes are made. For more information about how implicit synchronization uses the `onSync` script hook, see xref:../integrators-guide/chap-synchronization.adoc#sync-failure-compensation["Configuring Synchronization Failure Compensation"] in the __Integrator's Guide__. + +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:#email-sample5["Configure Email for the Sample"]). + +[#install-sample5b] +==== Install the Sample + +No external configuration is required for this sample. Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration of sample 5b. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample5b +---- +The XML files that simulate an external LDAP and AD resource are now located in the `openidm/samples/sample5b/data/` directory. The simulated AD data store file, `xml_AD_Data.xml`, does not contain users until you run reconciliation. + +Run the sample in exactly the same way that you did for Sample 5, following the steps in xref:#run-sample5["Run the Sample"]. Those steps will reconcile a user to your internal managed user repository. + +Unless you run the steps in xref:#run-sample5["Run the Sample"], you will not be able to run the steps in the next section. + + +[#onsync-sample5b] +==== Demonstrate onSync + +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. + +Log into the OpenIDM UI as the administrative user. On a local system, navigate to `\https://localhost:8443/admin`. The default administrative account and password are both `openidm-admin`. + +Select Manage > User. Make a change to the data of an existing user (`DDOE1`). As a result of the implicit synchronization from the managed object repository, that change is reflected almost immediately on the external resources. For sample 5b, you should see the changes in both XML files in the `sample5b/data` directory. Alternatively, you can query the external resources over REST, as described previously. + +The synchronization is successful, across all configured external resources, so the updated user record can be seen in both the `xml_LDAP_Data.xml` and `xml_AD_Data.xml` files. + +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: + +[source, console] +---- +$ cd /path/to/openidm/samples/sample5b/data +$ sudo mv xml_LDAP_Data.xml xml_LDAP_Data.xml.bak +---- +In the UI, now try another update to user `DDOE1`. With the modified filename of the simulated LDAP resource, implicit synchronization cannot write to this resource. An error similar to the following is displayed in the log file, `openidm0.log.0`: + +[source, console] +---- +Data file does not exist: +/path/to/openidm/samples/sample5b/data/xml_LDAP_Data.xml +---- +Although the AD resource is available, implicit synchronization will not reach this resource, because the mapping is specified __after__ the managed/user to LDAP mapping in the `sync.json` file. + +When the implicit synchronization operation fails for the LDAP resource, the `onSync` hook invokes the `compensate.js` 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. + +Because the LDAP resource is still unreadable, the synchronization to LDAP fails again, which triggers the `compensate.js` script again. This time, however, the script recognizes that the change was originally called as a result of a compensation and aborts. + +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. + +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 `sync.json`). 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. + + + +[#more-sample-6] +=== Sample 6 - LiveSync With an AD Server + +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 `sample6/alternatives` directory. Depending on your scenario, copy the corresponding file to the `sample6/conf` directory. +-- + +Active AD Deployment:: +If you have an actual AD deployment available, copy the `provisioner.openicf-realad.json` file to the `conf/` subdirectory. You can then configure synchronization between an OpenDJ Directory Server and an active AD deployment. + ++ +As this sample demonstrates synchronization __from__ the AD server __to__ OpenDJ, data on the AD server is not changed. + +Simulated AD Deployment:: +If you need to simulate an AD deployment, copy the `provisioner.openicf-fakead.json` file to the `conf/` subdirectory. You can then configure synchronization between an OpenDJ Directory server and a simulated AD server. + ++ +This sample simulates an AD server on the same instance of OpenDJ, using a different base DN. + +-- +The options shown in the associated configuration files can be easily modified to work with any standard LDAP server. + +[#more-sample6-realad] +==== Sample 6 with an Active AD Deployment + +If you have an existing, active instance of AD, set up OpenDJ, as described in the link:../../../opendj/3.5/install-guide[OpenDJ Installation Guide, window=\_blank]. + +During installation, populate OpenDJ with the data in the `Example.ldif` file, available in the `sample6/data` directory. + +The actions run in this sample should not change any data on the AD server. + + +[#d5830e2637] +==== Sample 6 with a Simulated AD Deployment + +In this sample, an AD deployment is simulated with a different baseDN (`dc=fakead,dc=com`) 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 `AD.ldif`. The data for the OpenDJ instance is contained in the file `Example.ldif`. + + +[#external-resource-sample6] +==== External Configuration + + +[#prepare-livesync-sample6] +===== Prepare OpenDJ For LiveSync + +This sample assumes a replicated OpenDJ server on the localhost system. When configured, OpenDJ replication includes an External Change Log (ECL), required to support LiveSync. LiveSync detects changes in OpenDJ by reading the ECL. + +Follow these steps to install and configure an OpenDJ instance. + +==== + +. Download and extract the OpenDJ zip archive from link:https://forgerock.org/downloads/[https://forgerock.org/downloads/, window=\_blank]. + +. Install OpenDJ using the command-line setup, and import the data file for this sample, as follows: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ ./setup --cli \ +--hostname localhost \ +--ldapPort 1389 \ +--rootUserDN "cn=Directory Manager" \ +--rootUserPassword password \ +--adminConnectorPort 4444 \ +--baseDN dc=com \ +--ldifFile /path/to/openidm/samples/sample6/data/Example.ldif \ +--acceptLicense \ +--no-prompt +... +Configuring Directory Server ..... Done. +Creating Base Entry dc=com ..... Done. +Starting Directory Server ....... Done. +... +---- ++ +The sample assumes the following configuration: ++ + +* The OpenDJ server is installed on the localhost. + +* The server listens for LDAP connections on port 1389. + +* The administration connector port is 4444. + +* The root user DN is `cn=Directory Manager`. + +* The root user password is `password`. + + +. Configure the OpenDJ server for replication. ++ +To enable liveSync, this server must be configured for replication, even if it does not actually participate in a replication topology. The following commands configure the server for replication. ++ + +[source, console] +---- +$ ./dsconfig create-replication-server \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --provider-name "Multimaster Synchronization" \ + --set replication-port:8989 \ + --set replication-server-id:2 \ + --type generic \ + --trustAll \ + --no-prompt + + $ ./dsconfig create-replication-domain \ + --hostname localhost \ + --port 4444 \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --provider-name "Multimaster Synchronization" \ + --domain-name fakead_com \ + --set base-dn:dc=fakead,dc=com \ + --set replication-server:localhost:8989 \ + --set server-id:3 \ + --type generic \ + --trustAll \ + --no-prompt +---- + +==== +Once OpenDJ is configured, you can proceed with either an active or simulated AD deployment. + + +[#external-resource-sample6-active] +===== External Configuration for an Active AD Deployment + +To configure an active AD deployment for sample 6, open the `provisioner.openicf-realad.json` 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: + +[#realad-json-configuration] +.Key Parameters for an Active AD Configuration +[cols="40%,60%"] +|=== +|Option |Description + +a|host +a|The hostname/IP address of the AD server + +a|port +a|The LDAP port; the default is 389. + +a|ssl +a|By default, SSL is not used. + +a|principal +a|The full DN of the account to bind with, such as "CN=Administrator,CN=Users,DC=example,DC=com" + +a|credentials +a|If a password is used, replace null with that password. When OpenIDM starts, it encrypts that password in the `provisioner.openicf-realad.conf` file. + +a|baseContexts +a|The DNs for account containers, such as ["CN=Users,DC=Example,DC=com"] + +a|baseContextsToSynchronize +a|Set to the same value as `baseContexts` + +a|accountSearchFilter +a|Default searches for active user (not computer) accounts + +a|accountSynchronizationFilter +a|Default synchronizes with active user (not computer) accounts +|=== +If you do not want to filter out computer and disabled user accounts, set the `accountSearchFilter` and `accountSynchronizationFilter` to `null`. + + +[#external-resource-sample6-simulated] +===== External Configuration for a Simulated AD Deployment + +Not everyone has a testable instance of AD readily available. For such administrators, you can use the `AD.ldif` file from the `data/` subdirectory to simulate an AD deployment. + +If you have not already done so, copy the `provisioner.openicf-fakead.json` file to the `conf` subdirectory. + +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. + +Open the `provisioner.openicf-fakead.json` file and note the following: + +* 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. + +* The simulated AD server uses the base DN `dc=fakead,dc=com`. + +To load the data for the simulated AD instance, launch the OpenDJ control panel, add the simulated AD baseDN (`dc=fakead,dc=com`), and then import the `sample6/data/AD.ldif` file. When you import the `AD.ldif` file, select "Append to Existing Data", not "Overwrite Existing Data". Otherwise, the data in the `dc=example,dc=com` baseDN will be overwritten. + +Alternatively, you could run the following command: + +[source, console] +---- +$ cd /path/to/opendj/bin +$ ./ldapmodify \ +--defaultAdd \ +--bindDN "cn=Directory Manager" \ +--bindPassword password \ +--hostname localhost \ +--port 1389 \ +--filename /path/to/openidm/samples/sample6/data/AD.ldif +---- + + + +[#install-sample6] +==== Start OpenIDM with Sample 6 + +Now that OpenDJ and a real or simulated AD database is configured, prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"]. You can then start OpenIDM with the configuration for sample 6. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample6 +---- + + +[#run-sample6] +==== Run the Sample + +The following sections show how to run the sample with command-based reconciliation with a REST call, and to configure scheduled reconciliation with LiveSync. + +[#run-sample6-reconciliation] +===== Using Reconciliation + +Now that OpenIDM is in operation, review the entries in the OpenDJ data store. When you run reconciliation, any entries that share the same `uid` with the AD data store will be updated with the contents from AD. + +If you have set up the simulated AD data store as described in xref:#external-resource-sample6-simulated["External Configuration for a Simulated AD Deployment"], compare the entries for `uid=jdoe` as shown in the `AD.ldif` and `Example.ldif` files. Note the different values of `givenName` for `uid=jdoe`. + +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 `jdoe` imported from the `AD.ldif` file, with a `givenName` of Johnny, and synchronizes that information to the LDAP database, overwriting the `givenName` of John for that same user `jdoe`. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemAdAccounts_managedUser&waitForCompletion=true" +---- +The reconciliation operation returns a reconciliation run ID, and the status of the operation. + +[source, console] +---- +{ + "state": "SUCCESS", + "_id": "985ee939-fbe1-4607-a757-00b404b4ef77" +} +---- +The reconciliation operation synchronizes the data in the AD server with the OpenIDM repository (managed/user). That information is then automatically synchronized to the OpenDJ server, as described in xref:../integrators-guide/chap-synchronization.adoc#handling-sync["Synchronization Situations and Actions"] in the __Integrator's Guide__. + +After reconciliation, list all users in the OpenDJ server data store. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" +---- +The result should resemble the following JSON object. + +[source, 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" + } ], +... +} +---- +You see only two entries, as the `uid=jdoe` entry from `dc=fakead,dc=com` overwrites the original LDAP entry for `uid=jdoe` in the reconciled LDAP data store. + +To read the user object in the OpenDJ server, run the `ldapsearch` command. The following example returns the entry for user `uid=jdoe`: + +[source, console] +---- +$ ./ldapsearch \ + --port 1389 \ + --baseDN dc=example,dc=com \ + "(uid=jdoe)" +---- + + +[#run-sample6-live-sync] +===== Using LiveSync + +You can trigger a reconciliation operation by configuring a schedule, or by launching the operation directly over the REST interface. You can also launch a LiveSync operation over REST, but LiveSync requires a configured schedule to poll for changes. When this sample's default LiveSync schedule (`schedule-activeSynchroniser_systemAdAccount.json`) is enabled, a LiveSync operation is launched every 15 seconds. + +LiveSync pushes changes made in the AD data store to the OpenIDM repository, automatically. + +The LiveSync schedule is disabled by default. To activate LiveSync, change the value of the `"enabled"` property from `false` to `true`. + +[source, javascript] +---- +{ + "enabled" : false, + "type" : "cron", + "schedule" : "0/15 * * * * ?", + "invokeService" : "provisioner", + "invokeContext" : { + "action" : "liveSync", + "source" : "system/ad/account" + }, + "invokeLogLevel" : "debug" +} +---- + +[#test-live-sync] +.Testing LiveSync +==== +Now you can test LiveSync. This procedure assumes that you have configured OpenDJ using the parameters and commands described in this section. + +. Create an LDIF file with a new user entry (`uid=bsmith`) that will be added to the simulated AD data store. + +. The following is the contents of a sample `bsmith.ldif` file for demonstration purposes: ++ + +[source, console] +---- +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 +---- ++ + +. Navigate to the `/path/to/opendj/bin` directory. + +. Use the `ldapmodify` command to add the `bsmith.ldif` file to the directory. ++ + +[source, console] +---- +$ ./ldapmodify \ + --port 1389 \ + --defaultAdd \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --filename /path/to/bsmith.ldif +---- + +. 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 `bsmith`, with password `passw0rd`. 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. + +. Implicit synchronization pushes this change out to the OpenDJ data store. To test this synchronization operation, search the OpenDJ baseDN for the new user entry. ++ + +[source, console] +---- +$ ./ldapsearch \ + --port 1389 \ + --baseDN ou=people,dc=example,dc=com \ + "(uid=bsmith)" +---- + +==== + + + + +[#sample-historical-accounts] +=== Linking Historical Accounts + +This sample demonstrates the retention of inactive (historical) LDAP accounts that have been linked to a corresponding managed user account. The sample builds on sample 2b and uses the LDAP connector to connect to an OpenDJ instance. You can use any LDAP-v3 compliant directory server. + +In this sample, OpenIDM is the source resource. Managed users in the OpenIDM repository maintain a list of the accounts that they have been linked to on the local LDAP server. This list is stored in the `historicalAccounts` field of the managed user entry. The list contains a reference to all past and current LDAP accounts. Each LDAP account in the list is represented as a __relationship__ and includes information about the date the accounts were linked or unlinked, and whether the account is currently active. For more information about relationship objects, see xref:../integrators-guide/chap-users-groups-roles.adoc#managing-relationships["Managing Relationships Between Objects"] in the __Integrator's Guide__. +This sample includes the following custom scripts, in its `script` directory: + +* `onLink-managedUser_systemLdapAccounts.js` ++ +When a managed user object is linked to a target LDAP object, this script creates the relationship entry in the managed user's `historicalAccounts` property. The script adds two relationship properties: ++ + +** `linkDate` — specifies the date that the link was created. + +** `active` — boolean true/false. When set to true, this property indicates that the target object is __currently__ linked to the managed user account. + + +* `onUnlink-managedUser_systemLdapAccounts.js` ++ +When a managed user object is unlinked from a target LDAP object, this script updates that relationship entry's properties with an `unlinkDate` that specifies when the target was unlinked, and sets the `active` property to false, indicating that the target object is no longer linked. + +* `check_account_state_change.js` ++ +During LiveSync or reconciliation, this script checks if the LDAP account state has changed. If the state has changed, the script updates the historical account properties to indicate the new state (enabled or disabled), and the date that the state was changed. This date can only be approximated and is set to the time that the change was detected by the script. + +* `ldapBackCorrelationQuery.js` ++ +This script correlates entries in the LDAP directory with managed user identities in OpenIDM. + + +[#ldap-config-historical-accounts] +==== Configuring the LDAP Server + +This sample expects the configuration for the external LDAP server to be the same as described in xref:#external-ldap-config-2["LDAP Server Configuration"]. + +The following steps provide setup instructions for an OpenDJ server. Adjust these instructions if you are using an alternative LDAP server. + +The LDIF data for this sample is provided in the file `openidm/samples/historicalaccountlinking/data/Example.ldif`. You will need to import this data during your OpenDJ setup. + +Although there is only one LDAP server in this example, you must enable __replication__ on that server, so that the server has an external change log. The change log is required for LiveSync between OpenDJ and OpenIDM. + +==== + +. Download OpenDJ from ForgeRock's link:https://forgerock.org/downloads/[download site, window=\_top] and extract the zip archive. + +. Install OpenDJ, using either the UI or the command-line setup. ++ + +* If you use the UI, make sure that you enable replication, and import the Example.ldif file during the install. + +* If you use the command-line setup, import the Example.ldif file during the setup, then enable replication when the server has started: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ ./setup --cli \ +--hostname localhost \ +--ldapPort 1389 \ +--rootUserDN "cn=Directory Manager" \ +--rootUserPassword password \ +--adminConnectorPort 4444 \ +--baseDN dc=com \ +--ldifFile /path/to/openidm/samples/historicalaccountlinking/data/Example.ldif \ +--acceptLicense \ +--no-prompt +... +Configuring Directory Server ..... Done. +Importing LDIF file /path/to/openidm/samples/sample2/data/Example.ldif ...... Done. +Starting Directory Server ...... Done.. +... +---- ++ + +[source, console] +---- +$ bin/dsconfig create-replication-server \ +--hostname localhost \ +--port 4444 \ +-D "cn=Directory Manager" \ +-w password \ +--trustAll \ +--no-prompt \ +--provider-name "Multimaster Synchronization" \ +--set replication-port:8989 \ +--set replication-server-id:2 \ +--type generic +$ bin/dsconfig create-replication-domain \ +--hostname localhost \ +--port 4444 \ +-D "cn=Directory Manager" \ +-w password \ +--trustAll \ +--no-prompt \ +--provider-name "Multimaster Synchronization" \ +--set base-dn:dc=example,dc=com \ +--set replication-server:localhost:8989 \ +--set server-id:3 \ +--type generic \ +--domain-name example_com +---- + + +==== + + +[#run-sample-historical-accounts] +==== Running the Historical Accounts Sample + +This section walks you through each step of the sample to demonstrate how historical accounts are stored. + +==== + +. Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the historical accounts sample: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/historicalaccountlinking/ +Executing ./startup.sh... +Using OPENIDM_HOME: /path/to/openidm +Using PROJECT_HOME: /path/to/openidm/samples/historicalaccountlinking/ +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: -Djava.util.logging.config.file= + /path/to/openidm/samples/historicalaccountlinking//conf/logging.properties +Using boot properties at + /path/to/openidm/samples/historicalaccountlinking/conf/boot/boot.properties +-> OpenIDM ready +---- + +. Create a user, Joe Smith, in OpenIDM. ++ +The following command creates the user over REST, and assigns the user the ID `joesmith`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "userName": "joe.smith", + "givenName": "Joe", + "sn" : "Smith", + "password" : "Passw0rd", + "displayName" : "Joe Smith", + "mail" : "joe.smith@example.com", + "_id" : "joesmith" + }' \ + "http://localhost:8080/openidm/managed/user?_action=create" +{ + "_id": "joesmith", + "_rev": "1", + "userName": "joe.smith", + "givenName": "Joe", + "sn": "Smith", + "displayName": "Joe Smith", + "mail": "joe.smith@example.com", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- + +. Verify that the user Joe Smith was created in OpenDJ. ++ +Because implicit synchronization is enabled by default, any change to the managed/user repository should be propagated to OpenDJ. For more information about implicit synchronization, see xref:../integrators-guide/chap-synchronization.adoc#sync-types["Types of Synchronization"] in the __Integrator's Guide__. ++ +The following command returns all IDs in OpenDJ and shows that user joesmith was created successfully: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "dn": "uid=jdoe,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=bjensen,ou=People,dc=example,dc=com", + "dn": "uid=bjensen,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=joe.smith0,ou=People,dc=example,dc=com", + "dn": "uid=joe.smith0,ou=People,dc=example,dc=com" + } + ], +... +} +---- ++ +Note that Joe Smith's `uid` in OpenDJ is appended with a `0`. The `onCreate` script, defined in the mapping (`sync.json`), increments the `uid` each time a new OpenDJ entry is linked to the same managed user object. + +. Verify that the historical account __relationship object__ that corresponds to this linked LDAP account was created in the OpenIDM repository. ++ +The following command returns all of the `historicalAccounts` for user joesmith: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/joesmith/historicalAccounts?_queryId=query-all" +{ + "result": [ + { + "_ref": "system/ldap/account/uid=joe.smith0,ou=People,dc=example,dc=com", + "_refProperties": { + "stateLastChanged": "Mon Nov 30 2015 14:45:22 GMT-0800 (PST)", + "state": "enabled", + "active": true, + "linkDate": "Mon Nov 30 2015 14:45:22 GMT-0800 (PST)", + "_id": "ff6913ce-a252-4dc9-a060-b8b56bb32bf4", + "_rev": "1" + } + } + ], +... +} +---- ++ +At this stage, Joe Smith has only one historical account link — the link to `uid=joe.smith0,ou=People,dc=example,dc=com`. Note that the relationship properties (`_refProperties`) show the following information about the linked accounts: ++ + +* The date on which the accounts were linked + +* The fact that this link is currently active + +* The state of the account in OpenDJ (`enabled`) + + +. Enable the LiveSync schedule to propagate changes made in OpenDJ to the managed user repository. ++ +To start LiveSync, set `enabled` to `true` in the `conf/schedule-liveSync.json` file: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ more samples/historicalaccountlinking/conf/schedule-liveSync.json +{ + "enabled" : true, + "type" : "cron", + "schedule" : "0/15 * * * * ?", +... +---- + +. Use the `manage-account` command in the `opendj/bin` directory to disable Joe Smith's account in OpenDJ: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ bin/manage-account set-account-is-disabled \ +--port 4444 \ +--bindDN "cn=Directory Manager" \ +--bindPassword password \ +--operationValue true \ +--targetDN uid=joe.smith0,ou=people,dc=example,dc=com \ +--trustAll +Account Is Disabled: true +---- ++ +Within 15 seconds, according to the configured schedule, LiveSync should pick up the change. OpenIDM should then adjust the `state` property in Joe Smith's managed user account. + +. Check Joe Smith's historical accounts again, to make sure that the state of this linked account has changed: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/joesmith/historicalAccounts?_queryId=query-all" +{ + "result": [ + { + "_ref": "system/ldap/account/uid=joe.smith0,ou=People,dc=example,dc=com", + "_refProperties": { + "stateLastChanged": "Mon Nov 30 2015 14:54:45 GMT-0800 (PST)", + "state": "disabled", + "active": true, + "linkDate": "Mon Nov 30 2015 14:45:22 GMT-0800 (PST)", + "_id": "ff6913ce-a252-4dc9-a060-b8b56bb32bf4", + "_rev": "2" + } + } + ], +... +} +---- + +. Now, deactivate Joe Smith's managed user account by setting his `accountStatus` property to inactive. ++ +You can deactivate the account over the REST interface, or by using the Admin UI. ++ +To use the Admin UI, simply select Manage > User, select Joe Smith's account and change his Status to inactive, on his Details tab. ++ +The following command deactivates Joe Smith's account over REST: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { "operation" : "replace", + "field" : "accountStatus", + "value" : "inactive" } + ]' \ + "http://localhost:8080/openidm/managed/user/joesmith" +{ + "_id": "joesmith", + "_rev": "3", + "userName": "joe.smith", + ... + "accountStatus": "inactive", + ... +} +---- + +. Request Joe Smith's historical accounts again: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/joesmith/historicalAccounts?_queryId=query-all" +{ + "result": [ + { + "_ref": "system/ldap/account/uid=joe.smith0,ou=People,dc=example,dc=com", + "_refProperties": { + "stateLastChanged": "Mon Nov 30 2015 14:54:45 GMT-0800 (PST)", + "state": "disabled", + "active": false, + "linkDate": "Mon Nov 30 2015 14:45:22 GMT-0800 (PST)", + "unlinkDate": "Mon Nov 30 2015 14:58:30 GMT-0800 (PST)", + "_id": "ff6913ce-a252-4dc9-a060-b8b56bb32bf4", + "_rev": "3" + } + } + ], +... +} +---- + +. Activate Joe Smith's managed user account by setting his `accountStatus` property to active. This action should create a new entry in OpenDJ (with `uid=joe.smith1`), and a new link from Joe Smith's managed user object to that OpenDJ entry. ++ +You can activate the account over the REST interface, or by using the Admin UI, as described previously. ++ +The following command activates Joe Smith's account over REST: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ + { "operation" : "replace", + "field" : "accountStatus", + "value" : "active" } + ]' \ + "http://localhost:8080/openidm/managed/user/joesmith" +{ + "_id": "joesmith", + "_rev": "4", + "userName": "joe.smith", + ... + "accountStatus": "active", + ... +} +---- + +. Verify that a new LDAP entry for user Joe Smith was created in OpenDJ. ++ +The following command returns all IDs in OpenDJ and shows that two OpenDJ entries for Joe Smith `uid=joe.smith0` and `uid=joe.smith1`. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "dn": "uid=jdoe,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=bjensen,ou=People,dc=example,dc=com", + "dn": "uid=bjensen,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=joe.smith0,ou=People,dc=example,dc=com", + "dn": "uid=joe.smith0,ou=People,dc=example,dc=com" + }, + { + "_id": "uid=joe.smith1,ou=People,dc=example,dc=com", + "dn": "uid=joe.smith1,ou=People,dc=example,dc=com" + } + ], +... +} +---- + +. Request Joe Smith's historical accounts again: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/joesmith/historicalAccounts?_queryId=query-all" +{ + "result": [ + { + "_ref": "system/ldap/account/uid=joe.smith0,ou=People,dc=example,dc=com", + "_refProperties": { + "stateLastChanged": "Mon Nov 30 2015 14:54:45 GMT-0800 (PST)", + "state": "disabled", + "active": false, + "linkDate": "Mon Nov 30 2015 14:45:22 GMT-0800 (PST)", + "unlinkDate": "Mon Nov 30 2015 14:58:30 GMT-0800 (PST)", + "_id": "ff6913ce-a252-4dc9-a060-b8b56bb32bf4", + "_rev": "3" + } + }, + { + "_ref": "system/ldap/account/uid=joe.smith1,ou=People,dc=example,dc=com", + "_refProperties": { + "stateLastChanged": "Mon Nov 30 2015 15:00:00 GMT-0800 (PST)", + "state": "enabled", + "active": true, + "linkDate": "Mon Nov 30 2015 15:00:00 GMT-0800 (PST)", + "_id": "08443775-7420-4994-bf86-9b29a986bfc9", + "_rev": "1" + } + } + ], + ... +} +---- ++ +Note that Joe Smith's entry now shows two OpenDJ accounts, but that only the link to `uid=joe.smith1` is `enabled` and `active`. + +==== + + + +[#sample-multiple-passwords] +=== Storing Multiple Passwords For Managed Users + +This sample demonstrates how to set up multiple passwords for managed users and how to synchronize separate passwords to different external resources. The sample assumes the following scenario: + +* The managed/user repository is the source system. + +* There are two target LDAP servers — `ldap` and `ldap2`. ++ +For the purposes of this sample, the two servers are represented by two separate organizational units on a single OpenDJ instance. + +* Managed user objects have two additional password fields, each mapped to one of the two LDAP servers. + +* The two LDAP servers have different requirements for password policy and encryption. + +* Both LDAP servers have a requirement for a password history policy, but the history size differs for the two policies. ++ +The sample shows how to extend the password history policy, described in xref:../integrators-guide/chap-passwords.adoc#password-history["Creating a Password History Policy"] in the __Integrator's Guide__, to apply to multiple password fields. + +* The value of a managed user's `password` field is used by default for the additional passwords __unless__ the CREATE, UPDATE, or PATCH requests on the managed user explicitly contain a value for these additional passwords. + +The sample includes the following customized configuration files in the `conf` directory: + +* `provisioner.openicf-ldap.json` configures the first LDAP connection. + +* `conf/provisioner.openicf-ldap2.json` configures the second LDAP connection. + +* `sync.json` provides the mappings from the OpenIDM managed user repository to the respective LDAP servers. + +* `managed.json` contains a customized schema for managed users that includes the additional password fields. + +For details of the customizations to these configuration files, see the `README` provided with the sample. +The sample includes the following customized scripts in the `script` directory: + +* `onCreate-onUpdate-sync.js` performs custom mapping logic. Specifically, this script maps the pre-hashed password value and sets the target object DN on create events. + +* `storeFields.groovy` stores the pre-hashed values of fields in the context chain, on validate events. + +* `onCreate-user-custom.js` and `onUpdate-user-custom.js` are used for validation of the password history policy when a user is created or updated. + +* `pwpolicy.js` is an additional policy script for the password history policy. + +* `set-additional-passwords.js` populates the values of the additional password fields with the value of the main `password` field if the additional fields are not included in the request content. + + +[NOTE] +==== +This sample does not support creation of new users in the Admin UI. +==== + +[#multiple-passwords-history-policy] +==== Understanding the Password History Policy + +The sample includes a custom password history policy. Although the sample is concerned only about the history of passwords, you can use this policy to enforce history validation on any managed object property. + +The following configuration changes set up the password history policy: + +* A `fieldHistory` property is added to managed users. The value of this field is a map of field names to a list of historical values for that field. These lists of values are used by the policy to determine if a new value has previously been used. + +* The `onCreate-user-custom.js` script performs the standard `onCreate` tasks for a managed user object but also stores the initial value of each of the fields that OpenIDM must keep a history of. The script is passed the following configurable properties: ++ + +** `historyFields` — a list of the fields to store history on. + +** `historySize` — the number of historical fields to store. + + +* The `onUpdate-user-custom.js` compares the old and new values of the historical fields on update events, to determine if the values have changed. When a new value is detected, it is stored in the list of historical values for that field. ++ +This script also contains logic to deal with the comparison of encrypted or hashed field values. The script is passed the following configurable properties: ++ + +** `historyFields` — a list of the fields to store history on. + +** `historySize` — the number of historical fields to store. + + +* The `pwpolicy.js` script contains the additional policy definition for the historical password policy. This script compares the new field value with the values in the list of historical values for each field. ++ +The `policy.json` configuration includes this script in its `additionalFiles` list, so that the policy service loads the new policy definition. The new policy takes a `historyLength` parameter, which indicates the number of historical values to enforce the policy on. This number must not exceed the `historySize` specified in the `onCreate` and `onUpdate` scripts. + +* The `ldapPassword` and `ldap2Password` fields in the managed user schema have been updated with the policy. For the purposes of this sample the `historySize` has been set to 2 for `ldapPassword` and to 4 for `ldap2Password`. + + + +[#ldap-config-multiple-passwords] +==== Configuring the LDAP Server + +This sample expects the configuration for the external LDAP server to be the same as described in xref:#external-ldap-config-2["LDAP Server Configuration"]. + +The following steps provide setup instructions for an OpenDJ server. Adjust these instructions if you are using an alternative LDAP server. + +The LDIF data for this sample is provided in the file `openidm/samples/multiplepasswords/data/Example.ldif`. You will need to import this data during your OpenDJ setup. + +==== + +. Download OpenDJ from ForgeRock's link:https://forgerock.org/downloads/[download site, window=\_top] and extract the zip archive. + +. Install OpenDJ, using either the UI or the command-line setup. ++ +If you use the UI, import the Example.ldif file during the install. ++ +If you use the command-line setup, import the Example.ldif file during the setup as follows: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ ./setup --cli \ +--hostname localhost \ +--ldapPort 1389 \ +--rootUserDN "cn=Directory Manager" \ +--rootUserPassword password \ +--adminConnectorPort 4444 \ +--baseDN dc=com \ +--ldifFile /path/to/openidm/samples/multiplepasswords/data/Example.ldif \ +--acceptLicense \ +--no-prompt +... +Configuring Directory Server ..... Done. +Importing LDIF file /path/to/openidm/samples/multiplepasswords/data/Example.ldif ...... Done. +Starting Directory Server ...... Done. +... +---- + +. Run an `ldapsearch` on the LDAP directory and look at the organizational units: ++ + +[source, console] +---- +$ cd /path/to/opendj +$ bin/ldapsearch \ +--hostname localhost \ +--port 1389 \ +--bindDN "cn=directory manager" \ +--bindPassword password \ +--baseDN "dc=example,dc=com" \ +"ou=*" \ +ou +dn: ou=People,dc=example,dc=com +ou: people + +dn: ou=Customers,dc=example,dc=com +ou: people +ou: Customers +---- ++ +The organizational units, `ou=People` and `ou=Customers`, represent the two different target LDAP systems that our mappings point to. + +==== + + +[#run-sample-multiple-passwords] +==== Demonstrating the Use of Multiple Accounts + +This section starts OpenIDM with the sample configuration, then creates a user with multiple passwords, adhering to the different policies in the configured password policy. The section tests that the user was synchronized to two separate LDAP directories, with the different required passwords, and that the user can bind to each of these LDAP directories. + +==== + +. Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the multiple passwords sample. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/multiplepasswords +---- + +. Create a user in OpenIDM. Include a main `password` field but no additional password fields. The additional password fields (`ldapPassword` and `ldap2Password`) will be populated with the value of the main `password` field as a result of the script described previously. ++ +For the purposes of this example, we will not use the Admin UI, so that the result of each command can be clearly seen. Create the user over REST, by running the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "userName": "jdoe", + "givenName": "John", + "sn" : "Doe", + "displayName" : "John Doe", + "mail" : "john.doe@example.com", + "password" : "Passw0rd" + }' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "code": 403, + "reason": "Forbidden", + "message": "Policy validation failed", + "detail": { + "result": false, + "failedPolicyRequirements": [ + { + "policyRequirements": [ + { + "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS", + "params": { + "numCaps": 2 + } + } + ], + "property": "ldapPassword" + }, + { + "policyRequirements": [ + { + "policyRequirement": "AT_LEAST_X_NUMBERS", + "params": { + "numNums": 2 + } + } + ], + "property": "ldap2Password" + } + ] + } +} +---- ++ +Notice that the create request failed with a policy validation failure on the two new password fields. Although the password met the requirement for the main `password` field, the user could not be created because the password did not meet the requirements of the `ldapPassword` and `ldap2Password` fields. ++ +You can fix this problem either by updating the `password` field to one that passes both of the new requirements, or by updating the individual password fields to specifically pass their individual validation requirements. + +. Now, try to create user jdoe again, this time providing individual values for each of the different password fields, that comply with the three different password policies: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data '{ + "userName": "jdoe", + "givenName": "John", + "sn" : "Doe", + "displayName" : "John Doe", + "mail" : "john.doe@example.com", + "password" : "Passw0rd", + "ldapPassword" : "PPassw0rd", + "ldap2Password" : "Passw00rd" + }' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "_id": "jdoe", + "_rev": "1", + "userName": "jdoe", + "givenName": "John", + "sn": "Doe", + "displayName": "John Doe", + "mail": "john.doe@example.com", + "ldapPassword": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "CpbVZlXAEFL/LUqWyq9Bcks/tLVwJ0pHrc/smLWf8UmC/0BDtEKRo1o2IjB6mNFz" + }, + "type": "salted-hash" + } + }, + "ldap2Password": { + "$crypto": { + "value": { + "iv": "TbJlRF+cSFeOguclh8AZVg==", + "data": "zQ250CXfR3QJ3cBKjpCQhQ==", + "cipher": "AES/CBC/PKCS5Padding", + "key": "openidm-sym-default" + }, + "type": "x-simple-encryption" + } + }, +... +} +---- ++ +The user has been created with three different passwords that comply with three distinct password policies. The passwords have been hashed or encrypted, as defined in the `managed.json` file. + +. As a result of implicit synchronization, two separate LDAP accounts should have been created for user jdoe on our two simulated LDAP servers. For more information about implicit synchronization, see xref:../integrators-guide/chap-synchronization.adoc#sync-types["Types of Synchronization"] in the __Integrator's Guide__. + +. Query the IDs in the LDAP directory as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" +{ + "result" : [ { + "_id" : "uid=jdoe,ou=People,dc=example,dc=com", + "dn" : "uid=jdoe,ou=People,dc=example,dc=com" + }, { + "_id" : "uid=jdoe,ou=Customers,dc=example,dc=com", + "dn" : "uid=jdoe,ou=Customers,dc=example,dc=com" + } ], +... +} +---- ++ +Note that jdoe has two entries - one in `ou=People` and one in `ou=Customers`. + +. Now, see if you can search each LDAP server, as user jdoe, with the separate passwords that you created for each directory. ++ +This step will indicate that the passwords were propagated correctly to the separate LDAP servers. ++ + +[source, console] +---- +$ cd /path/to/opendj +$ bin/ldapsearch \ +--hostname localhost \ +--port 1389 \ +--bindDN uid=jdoe,ou=People,dc=example,dc=com \ +--bindPassword PPassw0rd \ +--baseDN dc=example,dc=com \ +uid=jdoe +dn: uid=jdoe,ou=People,dc=example,dc=com +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +uid: jdoe +mail: john.doe@example.com +sn: Doe +cn: John Doe +userPassword: {SSHA}ot11NT7zidSxXEDtNE+8qQjyfIE3CDbywKGYmQ== +givenName: John + +dn: uid=jdoe,ou=Customers,dc=example,dc=com +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +uid: jdoe +mail: john.doe@example.com +sn: Doe +cn: John Doe +givenName: John +$ bin/ldapsearch \ +--hostname localhost \ +--port 1389 \ +--bindDN uid=jdoe,ou=Customers,dc=example,dc=com \ +--bindPassword Passw00rd \ +--baseDN dc=example,dc=com \ +uid=jdoe +dn: uid=jdoe,ou=People,dc=example,dc=com +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +uid: jdoe +mail: john.doe@example.com +sn: Doe +cn: John Doe +userPassword: {SSHA}ot11NT7zidSxXEDtNE+8qQjyfIE3CDbywKGYmQ== +givenName: John + +dn: uid=jdoe,ou=Customers,dc=example,dc=com +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +uid: jdoe +mail: john.doe@example.com +sn: Doe +cn: John Doe +givenName: John +---- + +. Patch jdoe's managed user entry to change his `ldapPassword`. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ { + "operation" : "replace", + "field" : "ldapPassword", + "value" : "TTestw0rd" + } ]' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "_id": "jdoe", + "_rev": "2", + "userName": "jdoe", + "givenName": "John", + "sn": "Doe", + "displayName": "John Doe", + ... + "ldapPassword": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "Vc6hvmzXaSSdG9WroqOTg3PQVdixhpg9tD/uKT610Z/H5iC6vsoOpE0/R5FaiDUg" + }, + "type": "salted-hash" + } + }, + ... +} +---- + +. Search the "first" LDAP server again, as user jdoe, with this new password to verify that the password change was propagated correctly to the LDAP server. ++ + +[source, console] +---- +$ cd /path/to/opendj +$ bin/ldapsearch \ +--hostname localhost \ +--port 1389 \ +--bindDN uid=jdoe,ou=People,dc=example,dc=com \ +--bindPassword TTestw0rd \ +--baseDN dc=example,dc=com \ +uid=jdoe +dn: uid=jdoe,ou=People,dc=example,dc=com +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +userPassword: {SSHA}pXV9/eZq6L30L/lGTsMV/39Dzjv/zHqIhWpLRw== +uid: jdoe +mail: john.doe@example.com +sn: Doe +givenName: John +cn: John Doe + +dn: uid=jdoe,ou=Customers,dc=example,dc=com +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +uid: jdoe +mail: john.doe@example.com +sn: Doe +cn: John Doe +givenName: John +---- + +==== + + +[#run-sample-multiple-passwords-history] +==== Demonstrating the Use of the Password History Policy + +This section patches managed user jdoe's entry, changing his `ldapPassword` a number of times, to demonstrate the application of the password history policy. + +==== + +. Send the following patch requests, changing the value of jdoe's `ldapPassword` each time: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ { + "operation" : "replace", + "field" : "ldapPassword", + "value" : "TTestw0rd1" + } ]' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "_id": "jdoe", + "_rev": "3", + "userName": "jdoe", + "givenName": "John", + "sn": "Doe", + "displayName": "John Doe", + "mail": "john.doe@example.com", +... + "ldapPassword": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "uFacwvr8JsiDwlfI7I/5M+q6jTmQT8e5BaNqxLRcVR+8JxA+/fqyOc8Wo0GhzIz6" + }, + "type": "salted-hash" + } + }, +} +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ { + "operation" : "replace", + "field" : "ldapPassword", + "value" : "TTestw0rd2" + } ]' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "_id": "jdoe", + "_rev": "4", + "userName": "jdoe", + "givenName": "John", + "sn": "Doe", + "displayName": "John Doe", + ... + "ldapPassword": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "kzxz6Nc38srk28xhaBLNX1DDtVsauKnDERoXyVy/TSYtEiMWd2KitgTn7498lZs0" + }, + "type": "salted-hash" + } + } +} +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ { + "operation" : "replace", + "field" : "ldapPassword", + "value" : "TTestw0rd3" + } ]' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "_id": "jdoe", + "_rev": "5", + "userName": "jdoe", + "givenName": "John", + "sn": "Doe", + "displayName": "John Doe", + ... + "ldapPassword": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "5NEEkfSsUHFOyEHa/C6yXl9x8s3Q5yaLYJgF02Lp/hPQ8DBKmwUU0U37cqFlQLQX" + }, + "type": "salted-hash" + } + } +} +---- ++ +User jdoe now has a __history__ of `ldapPassword` values, that contains `TTestw0rd3`, `TTestw0rd2`, `TTestw0rd1`, and `TTestw0rd`, in that order. You can see the four separate hashed values in the `fieldHistory` property of the user: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/jdoe?_fields=fieldHistory" +{ + "_id": "jdoe", + "_rev": "5", + "fieldHistory": { + ... + "ldapPassword": [ + { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "k1A1udvQo2gAW/5HxFFjs+IG2p34prv36UsVP89YAVv/bALQTAUJjBhml+qrlLBx" + }, + "type": "salted-hash" + } + }, + { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "LWHaTZYSUp6hP1RChZElfHmfFBQQV+FGtZuHJxsdA/j8sOvjyqeGxk+17IFCX/Ol" + }, + "type": "salted-hash" + } + }, + { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "I4nR+Kkh0sO53Sy2V7SUc6Hv3eETC9d0HWopgDTBc9FqRZCV2C9ML0kXGJk8FhfV" + }, + "type": "salted-hash" + } + }, + { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "um9iNdwU7XEVArep2X5I0wr4rRy7nacKXNuzzOc7Oa1f+lINHKwZKxaTyBwGbpX2" + }, + "type": "salted-hash" + } + } + ] + } +} +---- + +. The history size for the `ldapPassword` policy is set to 2. To demonstrate the history policy, attempt to patch jdoe's entry with a password value that was used in his previous 2 password resets: `TTestw0rd2`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ { + "operation" : "replace", + "field" : "ldapPassword", + "value" : "TTestw0rd2" + } ]' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "code": 403, + "reason": "Forbidden", + "message": "Failed policy validation", + "detail": { + "result": false, + "failedPolicyRequirements": [ + { + "policyRequirements": [ + { + "policyRequirement": "IS_NEW" + } + ], + "property": "ldapPassword" + } + ] + } +} +---- ++ +The password reset fails the `IS_NEW` policy requirement. + +. Now, reset jdoe's password to a value that was not used in the previous two updates: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PATCH \ + --data '[ { + "operation" : "replace", + "field" : "ldapPassword", + "value" : "TTestw0rd" + } ]' \ + "http://localhost:8080/openidm/managed/user/jdoe" +{ + "_id": "jdoe", + "_rev": "5", + "userName": "jdoe", + "givenName": "John", + "sn": "Doe", + "displayName": "John Doe", + ... + "ldapPassword": { + "$crypto": { + "value": { + "algorithm": "SHA-256", + "data": "5NEEkfSsUHFOyEHa/C6yXl9x8s3Q5yaLYJgF02Lp/hPQ8DBKmwUU0U37cqFlQLQX" + }, + "type": "salted-hash" + } + } +} +---- ++ +This time, the password reset succeeds. + +==== + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-multiaccount-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-multiaccount-sample.adoc new file mode 100644 index 000000000..afa2b0a6d --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-multiaccount-sample.adoc @@ -0,0 +1,523 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-multiaccount-sample] +== The Multi-Account Linking Sample + +The sample provided in the `samples/multiaccountlinking` directory illustrates how OpenIDM addresses links from multiple accounts to one identity. + +This sample is based on a common use case in the insurance industry, where a company (Example.com) employs agents to sell policies to their insured customers. Most of their agents are also insured. These different roles are sometimes known as the multi-account linking conundrum. + +With minor changes, this sample works for other use cases. For example, you may have a hospital that employs doctors who treat patients. Some of their doctors are also patients of that hospital. + +[#external-ldap-config-multiaccount] +=== External LDAP Configuration + +Configure the LDAP server as for sample 2, (see xref:chap-ldap-samples.adoc#external-ldap-config-2["LDAP Server Configuration"]). + +The LDAP user must have write access to create users from OpenIDM on the LDAP server. When you configure the LDAP server, import the appropriate LDIF file, in this case, __openidm/samples/multiaccountlinking/data/Example.ldif__. + + +[#install-sample-multiaccount] +=== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the following configuration for the Multi-Account Linking sample. + +[source, console] +---- +$ cd /path/to/openidm +---- + +[source, console] +---- +$ ./startup.sh -p samples/multiaccountlinking +---- + + +[#multiaccount-create-users] +=== Create New Identities for the Sample + +For the purpose of this sample, create identities for users John Doe and Barbara Jensen. To create these identities from the Admin UI, navigate to `\https://localhost:8443/admin` and click Manage > User > New User. + +Alternatively, use the following REST calls to set up identities for the noted users: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-Type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +--data '{ + "displayName" : "Barbara Jensen", + "description" : "Created for OpenIDM", + "givenName" : "Barbara", + "mail" : "bjensen@example.com", + "telephoneNumber" : "1-360-229-7105", + "sn" : "Jensen", + "userName" : "bjensen", + "accountStatus" : "active" +}' \ +"https://localhost:8443/openidm/managed/user?_action=create" +---- + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-Type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +--data '{ + "displayName" : "John Doe", + "description" : "Created for OpenIDM", + "givenName" : "John", + "mail" : "jdoe@example.com", + "telephoneNumber" : "1-415-599-1100", + "sn" : "Doe", + "userName" : "jdoe", + "accountStatus" : "active" +}' \ +"https://localhost:8443/openidm/managed/user?_action=create" +---- +In the output, you will see an ID number associated with each user, in the following format: + +[source, console] +---- +"_id" : "35d0a49d-2571-401f-b429-96c66b23a1c0", +---- +Record the `_id` number for each user. You will use those numbers to assign desired roles for each user. + + +[#multiaccount-create-roles] +=== Create New Roles for the Sample + +For this sample, to set up links for multiple accounts on OpenIDM, you need to set up roles. To do so, set up roles for `Agent` and `Insured`. To create these roles in the Admin UI, navigate to `\https://localhost:8443/admin` and click Manage > Role > New Role. + +Alternatively, use the following REST calls to set up the `Agent` and `Insured` roles: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-Type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +--data '{ + "name" : "Agent", + "description" : "Role assigned to insurance agents." +}' \ +"https://localhost:8443/openidm/managed/role?_action=create" +---- + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-Type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +--data '{ + "name" : "Insured", + "description" : "Role assigned to insured customers." +}' \ +"https://localhost:8443/openidm/managed/role?_action=create" +---- +Do record the `_id` output for the `Agent` and `Insured` roles. You will use those numbers to assign desired roles for each user. + +[NOTE] +==== +While you could use `PUT` to create roles with descriptive names, we recommend that you use `POST` to create roles with immutable IDs. +==== + + +[#multiaccount-assign-roles] +=== Assign Roles to Appropriate Users + +Now you can assign roles to appropriate users. To review, user `jdoe` is an `Agent` and user `bjensen` is `Insured`. + +You will need the `_id` value for each user. The `_id` values shown in the following commands are random; substitute the `_id` values that you collected when creating users. + +The following command adds the `Agent` role to user `jdoe`, by their `_id` values: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "If-Match: *" \ +--request PATCH \ +--data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : { + "_ref" : "managed/role/287dc4b1-4b19-49ec-8b4c-28a6c12ede34" + } + } + ]' \ +"https://localhost:8443/openidm/managed/user/8fae84ed-1f30-4542-8087-e7fa6e89541c" +---- +And this next command adds the `Insured` role to user `bjensen`: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "If-Match: *" \ +--request PATCH \ +--data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : { + "_ref" : "managed/role/bb9302c4-5fc1-462c-8be2-b17c87175d1b" + } + } + ]' \ +"https://localhost:8443/openidm/managed/user/d0b79f30-946f-413a-b7d1-d813034fa345" +---- +Now assign the `Insured` role to user `jdoe`, as that user is both an insured customer and an agent: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "If-Match: *" \ +--request PATCH \ +--data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : { + "_ref" : "managed/role/006935c2-b080-45cd-8347-881df42cae0c" + } + } + ]' \ +"https://localhost:8443/openidm/managed/user/a3335177-7366-4656-a66c-8d6e77a5786f" +---- +User `jdoe` should now have two managed roles: + +[source, console] +---- +... +"effectiveRoles" : [ { + "_ref" : "managed/role/6aabe990-ec05-403e-bc5d-ff9b217ba571", + "_refProperties" : { + "_id" : "687714b8-5854-42c7-a190-c781ea5174c5", + "_rev" : "1" + } +}, { + "_ref" : "managed/role/844110ce-3686-43bb-aabf-46b17a14abaa" +} ], +... +---- + + +[#multiaccount-background] +=== Background: Link Qualifiers, Agents, and Insured Customers + +This is a good moment to take a step back, to see how this sample works, based on custom options in the `sync.json` configuration file. + +OpenIDM defines mappings between source and target accounts in the `sync.json` file, which allows you to create a link between one source entry and multiple target entries using a concept known as a "link qualifier," which enables one-to-many relationships in mappings and policies. + +For more information on resource mappings and link qualifiers, see the following sections of the Integrator’s Guide: + +xref:../integrators-guide/chap-synchronization.adoc#synchronization-mappings-file["Mapping Source Objects to Target Objects"] in the __Integrator's Guide__. + +xref:../integrators-guide/chap-synchronization.adoc#linking-multiple-targets["Mapping a Single Source Object to Multiple Target Objects"] in the __Integrator's Guide__. + +In this sample, we use two link qualifiers: + +* `insured`+ +`ou=Customers,dc=example,dc=com` +* `agent`+ +`ou=Contractors,dc=example,dc=com` +Assume that agents and insured customers connect via two different portals. Each group gets access to different features, depending on the portal. + +Agents may have two different accounts; one each for professional and personal use. While the accounts are different, the identity information for each agent should be the same for both accounts. + +To that end, this sample sets up link qualifiers for two categories of users: `insured` and `agent`, under the `managedUser_systemLdapAccounts` mapping: + +[source, javascript] +---- +{ + "name" : "managedUser_systemLdapAccounts", + "source" : "managed/user", + "target" : "system/ldap/account", + "linkQualifiers" : [ + "insured", + "agent" + ], + ..... +} +---- +You can verify this in the Admin UI. Click Configure > Mappings > `managedUser_systemLdapAccounts` > Properties > Link Qualifiers. You should see `insured` and `agent` in the list of configured Link Qualifiers. + +In addition, this sample also includes a transformation script between an LDAP Distinguished Name (`dn`) and the two categories of users. The following excerpt of the `sync.json` file includes that script: + +[source, javascript] +---- +{ + "target" : "dn", + "transform" : { + "type" : "text/javascript", + "globals" : { }, + "source" : + "if (linkQualifier === 'agent') { + 'uid=' + source.userName + ',ou=Contractors,dc=example,dc=com'; + } else if (linkQualifier === 'insured') { + 'uid=' + source.userName + ',ou=Customers,dc=example,dc=com'; + }" +}, +---- +The following validSource script looks through the effective roles of a user, with two objectives: + +* `Agent`+ +`Insured` +* `effectiveRoles`+ +`Agent`+ +`Insured` + +[source, javascript] +---- +"validSource" : { + "type" : "text/javascript", + "globals" : { }, + "source" : "var res = false; + var i=0; + + while (!res && i < source.effectiveRoles.length) { + var roleId = source.effectiveRoles[i]; + if (roleId != null && roleId.indexOf("/") != -1) { + var roleInfo = openidm.read(roleId); + logger.warn("Role Info : {}",roleInfo); + res = (((roleInfo.properties.name === 'Agent') + &&(linkQualifier ==='agent')) + || ((roleInfo.properties.name === 'Insured') + &&(linkQualifier ==='insured'))); + } + i++; + } + res" +} +---- +You can see how correlation queries are configured in the `sync.json` file. + +The structure for the correlation query specifies one of two link qualifiers: `insured` or `agent`. For each link qualifier, the correlation query defines a script that verifies if the subject `dn` belongs in a specific container. For this sample, the container (`ou`) may be Customers or Contractors. + +You can can avoid specifying the structure of the `dn` attribute in two places in the `sync.json` file with the following code, which leverages the expression builder to reuse the construct defined in the `dn` mapping: + +[source, javascript] +---- +"correlationQuery" : [ + { + "linkQualifier" : "insured", + "expressionTree" : { + "all" : [ + "dn" + ] + }, + "mapping" : "managedUser_systemLdapAccounts", + "type" : "text/javascript", + "file" : "ui/correlateTreeToQueryFilter.js" + }, + { + "linkQualifier" : "agent", + "expressionTree" : { + "all" : [ + "dn" + ] + }, + "mapping" : "managedUser_systemLdapAccounts", + "type" : "text/javascript", + "file" : "ui/correlateTreeToQueryFilter.js" + } +], +---- +You can also leverage the expression builder in the UI. Review how the UI illustrates the expression builder. To do so, click Configure > Mapping > select a mapping > Association > Association Rules. Edit either link qualifier. You will see how the expression builder is configured for this sample. + + +[#multiaccount-roles-update] +=== Update Roles With Desired LDAP Attributes + +This use case illustrates how accounts frequently have different functions on target systems. For example, while agents may be members of a Contractor group, insured customers may be part of a Chat Users group (possibly for access to customer service). + +While an `agent` may also be an insured customer, you do not want other `insured` accounts to have the same properties (or memberships) as the `agent` account. In this sample, we ensure that OpenIDM limits role based assignments to the correct account. + +With the following commands you will create two managed assignments which will be used by the `agent` and `insured` roles. + +Record the `_id` number for each user. You will use those numbers to assign desired roles for each user. + +The following command will create an `agent` assignment. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name" : "ldapAgent", + "description" : "LDAP Agent Assignment", + "mapping" : "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "ldapGroups", + "value": [ + "cn=Contractors,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + } + ], + "linkQualifiers": ["agent"] + }' \ +"https://localhost:8443/openidm/managed/assignment?_action=create" +---- +Now repeat the process for the `insured` assignment, with the value set to the `Chat Users` group: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "Content-Type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name" : "ldapCustomer", + "description" : "LDAP Customer Assignment", + "mapping" : "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "ldapGroups", + "value": [ + "cn=Chat Users,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + } + ], + "linkQualifiers": ["insured"] + }' \ +"https://localhost:8443/openidm/managed/assignment?_action=create" +---- +Now you can add the created assignments to their respective roles. + +Add the `insured` assignment to the insured customer role: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "If-Match: *" \ +--request PATCH \ +--data '[ + { + "operation" : "add", + "field" : "/assignments/-", + "value" : { + "_ref" : "managed/assignment/ee5241b2-e571-4736-8fb2-6b9caa9d0554" + } + } + ]' \ +"https://localhost:8443/openidm/managed/role/287dc4b1-4b19-49ec-8b4c-28a6c12ede34" +---- +Add the `agent` assignment to the `agent` role: + +[source, console] +---- +$ curl \ +--cacert self-signed.crt \ +--header "Content-type: application/json" \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "If-Match: *" \ +--request PATCH \ +--data '[ + { + "operation" : "add", + "field" : "/assignments/-", + "value" : { + "_ref" : "managed/assignment/12927c5d-576f-491e-ba65-e228cd218947" + } + } + ]' \ +"https://localhost:8443/openidm/managed/role/bb9302c4-5fc1-462c-8be2-b17c87175d1b" +---- + + +[#multiaccountlinking-recon] +=== Reconciling Managed Users to the External LDAP Server + +Now that you have loaded `Example.ldif` into OpenDJ, and have started OpenIDM, you can perform a reconciliation from the internal Managed Users repository to the external OpenDJ data store: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=managedUser_systemLdapAccounts" +---- +With all of the preparation work that you have done, this reconciliation will create three new accounts on the external LDAP server: + +* `ou=Customers,dc=example,dc=com`+ +`dn`+ +`bjensen`+ +`jdoe` +* `ou=Contractors,dc=example,dc=com`+ +`dn`+ +`jdoe` +Congratulations, you have just created accounts in two different areas of the LDAP Directory Information Tree. + + +[#multilinking-review] +=== Reviewing the Result + +You have already confirmed that user `bjensen` has a `insured` role, and user `jdoe` has both a `insured` and `agent` role. You can confirm the same result in the Admin UI: + +. +. `jdoe` +. `bjensen`+ +`jdoe` + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-overview.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-overview.adoc new file mode 100644 index 000000000..32341a64f --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-overview.adoc @@ -0,0 +1,180 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-overview] +== Overview of the OpenIDM Samples + +This chapter lists all the samples provided with OpenIDM and gives a high-level overview of the purpose of each sample. This chapter also provides information that is required for all of the samples. Read this chapter, specifically xref:#install-samples["Installing the Samples"] and xref:#preparing-openidm["Preparing OpenIDM"] before you try any of the samples provided with OpenIDM. + +[#samples-provided-with-openidm] +=== Overview of the Samples Provided With OpenIDM + +-- +OpenIDM provides a number of samples in the `openidm/samples` directory. This section describes the purpose of each sample: + +xref:chap-xml-samples.adoc#chap-xml-samples["XML Samples - Reconciling Data Between OpenIDM and an XML File"]:: +The XML samples all use the XML file connector to interact with an XML file resource. The samples demonstrate the following OpenIDM functionality: ++ + +* xref:chap-xml-samples.adoc#more-sample-1["First OpenIDM Sample - Reconciling an XML File Resource"] ++ +The basic XML sample demonstrates a connection to an XML file that holds user data. The sample includes one mapping and shows reconciliation from an XML file to the OpenIDM repository. + +* xref:chap-xml-samples.adoc#more-sample-8["Logging Sample - Using Scripts to Generate Log Messages"] ++ +The logging sample demonstrates the logging capabilities available to OpenIDM scripts, and provides an alternative method for debugging scripts. + +* xref:chap-xml-samples.adoc#more-sample-9["Workflow Sample - Demonstrating Asynchronous Reconciling Using a Workflow"] ++ +The workflow sample uses the XML connector to demonstrate asynchronous reconciliation using the workflow mechanism. + + +xref:chap-ldap-samples.adoc#chap-ldap-samples["LDAP Samples - Reconciling Data Between OpenIDM and One or More LDAP Directories"]:: +The LDAP samples all assume a connection to an LDAP directory, usually OpenDJ, or Active Directory. Samples 5 and 5b simulate an LDAP directory with an XML file, and use the XML connector. These samples demonstrate a wide variety of OpenIDM functionality and are broken down as follows: ++ + +* xref:chap-ldap-samples.adoc#more-sample-2["Sample 2 - LDAP One Way"] ++ +This sample uses the generic LDAP connector to connect to an LDAP directory. The sample includes one mapping from the LDAP directory to the managed user repository, and demonstrates reconciliation from the external resource to the repository. + +* xref:chap-ldap-samples.adoc#more-sample-2b["Sample 2b - LDAP Two Way"] ++ +This sample uses the generic LDAP connector to connect to an LDAP directory. The sample includes two mappings, one from the LDAP directory to the managed user repository, and one from the repository to the LDAP directory. The sample demonstrates reconciliation in both directions. + +* xref:chap-ldap-samples.adoc#more-sample-2c["Sample 2c - Synchronizing LDAP Group Membership"] ++ +This sample uses the generic LDAP connector to connect to an LDAP directory. The sample includes two mappings, one from the LDAP directory to the managed user repository, and one from the repository to the LDAP directory. The sample demonstrates synchronization of group membership, that is, how the value of the `ldapGroups` property in a managed user object is mapped to the corresponding user object in LDAP. + +* xref:chap-ldap-samples.adoc#more-sample-2d["Sample 2d - Synchronizing LDAP Groups"] ++ +This sample uses the generic LDAP connector to connect to an LDAP directory. The sample builds on the previous sample by providing an additional mapping, from the LDAP groups object, to the managed groups object. The sample illustrates a new managed object type (groups) and shows how this object type is synchronized with group containers in LDAP. + +* xref:chap-ldap-samples.adoc#more-sample-5["Sample 5 - Synchronization of Two LDAP Resources"] ++ +Although this sample is grouped with the LDAP samples, it actually __simulates__ two LDAP directories with XML files, and uses the XML file connector to connect the two. The purpose of this sample is to demonstrate reconciliation directly between two external resources, without the data passing through the OpenIDM repository. The sample also demonstrates the configuration of an outbound email service to send reconciliation summaries by mail. + +* xref:chap-ldap-samples.adoc#more-sample-5b["Sample 5b - Failure Compensation With Multiple Resources"] ++ +This sample builds on the previous sample to demonstrate a failure compensation mechanism that relies on script event hooks. The failure compensation mechanism ensures that reconciliation changes are propagated throughout a multiple-resource deployment, or rolled back in the case of error. The purpose of this mechanism is to keep the data consistent across multiple resources. + +* xref:chap-ldap-samples.adoc#more-sample-6["Sample 6 - LiveSync With an AD Server"] ++ +This sample illustrates the LiveSync mechanism that pushes changes from an external resource to the OpenIDM repository. The sample uses an LDAP connector to connect to an LDAP directory, either OpenDJ or Active Directory. + +* xref:chap-ldap-samples.adoc#sample-historical-accounts["Linking Historical Accounts"] ++ +This sample demonstrates the retention of inactive (historical) LDAP accounts that have been linked to a corresponding managed user account. The sample builds on sample 2b and uses the LDAP connector to connect to an OpenDJ instance. You can use any LDAP-v3 compliant directory server. + +* xref:chap-ldap-samples.adoc#sample-multiple-passwords["Storing Multiple Passwords For Managed Users"] ++ +This sample demonstrates how to set up multiple passwords for managed users and how to synchronize separate passwords to different external resources. The sample includes two target LDAP servers, each with different password policy and encryption requirements. The sample also shows how to extend the password history policy to apply to multiple password fields. + + +xref:chap-groovy-samples.adoc#chap-groovy-samples["Samples That Use the Groovy Connector Toolkit to Create Scripted Connectors"]:: +The samples in this section use the Groovy Connector Toolkit to create a scripted connector. Because you can use scripted Groovy connectors to connect to a large variety of systems, the samples in this section show connections to several different external resources. The samples are broken down as follows: ++ + +* xref:chap-groovy-samples.adoc#more-sample3["Sample 3 - Using the Custom Scripted Connector Bundler to Build a ScriptedSQL Connector"] ++ +This sample uses the __custom scripted connector bundler__ to create a new scripted connector. The connector bundler generates a scripted connector, the connector configuration and the Groovy scripts required to communicate with an external MySQL database (HRDB). + +* xref:chap-groovy-samples.adoc#sample-scripted-rest["Sample - Using the Groovy Connector Toolkit to Connect to OpenDJ With ScriptedREST"] ++ +This sample uses the Groovy Connector Toolkit to implement a ScriptedREST connector, which interacts with the OpenDJ REST API. + +* xref:chap-groovy-samples.adoc#sample-scripted-crest["Using the Groovy Connector Toolkit to Connect to OpenDJ With ScriptedCREST"] ++ +This sample uses the Groovy Connector Toolkit to create a ScriptedCREST connector, which connects to an OpenDJ instance. The main difference between a CREST-based API and a generic REST API is that the CREST API is inherently recognizable by all ForgeRock products. As such, the sample can leverage CREST resources in the groovy scripts, to create CREST requests. + + +xref:chap-powershell-samples.adoc#chap-powershell-samples["Samples That Use the PowerShell Connector Toolkit to Create Scripted Connectors"]:: +This sample uses the PowerShell Connector Toolkit to create a PowerShell connector, and provides a number of PowerShell scripts that enable you to perform basic CRUD (create, read, update, delete) operations on an Active Directory server. The samples use the MS Active Directory PowerShell module. + +xref:chap-kerberos-sample.adoc#chap-kerberos-sample["Scripted Kerberos Connector Sample"]:: +This sample demonstrates how to use the scripted Kerberos connector to manage Kerberos user principals and to reconcile user principals with OpenIDM managed user objects. + +xref:chap-audit-sample.adoc#chap-audit-sample["Audit Samples"]:: +This sample uses a ScriptedSQL implementation of the Groovy Connector Toolkit to direct audit information to a MySQL database. + +xref:chap-roles-sample.adoc#chap-roles-sample["Roles Samples - Demonstrating the OpenIDM Roles Implementation"]:: +This sample builds on xref:chap-ldap-samples.adoc#more-sample-2["Sample 2 - LDAP One Way"], and extends that sample to demonstrate how roles are implemented in OpenIDM. + +xref:chap-multiaccount-sample.adoc#chap-multiaccount-sample["The Multi-Account Linking Sample"]:: +This sample illustrates how OpenIDM addresses links from multiple accounts to one identity. + +xref:chap-trustedfilter-sample.adoc#chap-trustedfilter-sample["The Trusted Servlet Filter Sample"]:: +This sample demonstrates how to use a custom servlet filter and the Trusted Request Attribute Authentication Module in OpenIDM. Once configured, OpenIDM can use the servlet filter to authenticate through another service. + +xref:chap-fullstack-sample.adoc#chap-fullstack-sample["Full Stack Sample - Using OpenIDM in the ForgeRock Identity Platform"]:: +This sample demonstrates the integration of three ForgeRock products: OpenIDM, OpenDJ, and OpenAM. With this sample, you can see how you can use OpenAM for authentication, for user identities that are maintained with OpenIDM, based on a data store of users in OpenDJ. + +xref:chap-workflow-samples.adoc#chap-workflow-samples["Workflow Samples"]:: +The workflow sample and use cases demonstrate how OpenIDM uses workflows to provision user accounts. The samples demonstrate the use of the Self-Service UI to enable user self-registration, ++ + +* xref:chap-workflow-samples.adoc#example-provisioning-workflow["Sample Workflow - Provisioning User Accounts"] ++ +The provisioning workflow sample demonstrates a typical use case of a workflow — provisioning new users. The sample demonstrates the use of the Admin UI, to configure user self-service and the Self-Service UI that enables users to complete their registration process. + +* xref:chap-workflow-samples.adoc#workflow-use-cases["Workflow Use Cases"] ++ +The workflow use cases work together to provide a complete business story, with the same set of sample data. Each of the use cases is integrated with the Self-Service UI. + + +xref:chap-google-sample.adoc#chap-google-sample["Google Sample - Connecting to Google With the Google Apps Connector"]:: +This sample uses the Google Apps Connector to manage the creation of users and groups on an external Google system, using OpenIDM's REST interface. + +xref:chap-salesforce-sample.adoc#chap-salesforce-sample["Salesforce Sample - Salesforce With the Salesforce Connector"]:: +This sample uses the Salesforce Connector demonstrate reconciliation of user accounts from the OpenIDM repository to Salesforce, and from Salesforce to the OpenIDM repository. + +xref:chap-endpoint-sample.adoc#chap-endpoint-sample["Custom Endpoint Sample"]:: +OpenIDM supports scriptable custom endpoints that enable you to launch arbitrary scripts through an OpenIDM REST URI. This sample shows how custom endpoints are configured and returns a list of variables available to each method used in a custom endpoint script. + +-- + + +[#install-samples] +=== Installing the Samples + +Each sample directory in `openidm/samples/` contains a number of subdirectories, such as `conf/` and `script/`. To start OpenIDM with a sample configuration, navigate to the `/path/to/openidm` directory and use the `-p` option of the `startup` 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. + +Many of the procedures in this guide refer to paths such as `samplex/...`. In each of these cases, the complete path is assumed to be `/path/to/openidm/samples/samplex/...`. + +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 xref:../integrators-guide/chap-configuration.adoc#changing-configuration["Changing the Default Configuration"] in the __Integrator's Guide__. + +The command-line examples in this chapter (and throughout the OpenIDM documentation) assume a UNIX shell. If you are running these samples on Windows, adjust the command-line examples accordingly. For an indication of what the corresponding Windows command would look like, see the examples in xref:chap-xml-samples.adoc#more-sample-1["First OpenIDM Sample - Reconciling an XML File Resource"]. + + +[#preparing-openidm] +=== Preparing OpenIDM + +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. + +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 `openidm/db/openidm` directory. + +[source, console] +---- +$ rm -rf /path/to/openidm/db/openidm +---- +OpenIDM should then be ready to start with a new sample. For a number of the samples in this guide, users are created either with the UI or directly with a commons REST call. Users that have been created in the repository (managed users) should be able to log into the Self-Service UI. + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-powershell-samples.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-powershell-samples.adoc new file mode 100644 index 000000000..7bfe7fcf3 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-powershell-samples.adoc @@ -0,0 +1,1334 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-powershell-samples] +== Samples That Use the PowerShell Connector Toolkit to Create Scripted Connectors + +OpenICF provides a generic PowerShell Connector Toolkit that enables you to run PowerShell scripts on any external resource. The PowerShell Connector Toolkit is not a complete connector, in the traditional sense. Rather, it is a framework within which you must write your own PowerShell scripts to address the requirements of your Microsoft Windows ecosystem. You can use the PowerShell Connector Toolkit to create connectors that can provision any Microsoft system. This chapter describes sample connector implementations that enable you to provision to an Active Directory server instance, and to Azure Active Directory (Azure AD). + +The PowerShell Connector Toolkit is available, with a subscription, from the link:https://backstage.forgerock.com/[ForgeRock Backstage, window=\_blank] site. + +[#powershell-ad] +=== Connect to Active Directory + +This sample provides a number of PowerShell scripts that enable you to perform basic CRUD (create, read, update, delete) operations on an Active Directory server. The samples use the MS Active Directory PowerShell module. For more information on this module, see the corresponding link:http://technet.microsoft.com/en-us/library/hh852274.aspx[Microsoft documentation, window=\_blank]. + +This sample assumes that OpenIDM is running on a Windows system on the localhost. It also assumes that Active Directory and the OpenICF .NET connector server run on a remote Windows server. The PowerShell connector runs on the .NET connector server. + +To use this sample for OpenIDM instances installed on UNIX systems, adjust the relevant commands shown with PowerShell prompts. + +[#powershell-ad-setup] +==== Setting Up the PowerShell Active Directory Sample + + +==== +Run the commands in this procedure from the PowerShell command line. The continuation character used in the command is the back-tick (`). + +. Install, configure, and start the .NET connector server on the machine that is running an Active Directory Domain Controller or on a workstation on which the Microsoft Active Directory PowerShell module is installed. ++ +For instructions on installing the .NET connector server, see xref:../integrators-guide/chap-resource-conf.adoc#net-connector-install["Installing the .NET Connector Server"] in the __Integrator's Guide__. + +. Configure OpenIDM to connect to the .NET connector server. ++ +To do so, copy the remote connector provisioner file from the `openidm\samples\provisioners` directory to your project's `conf\` directory, and edit the file according to your configuration. ++ + +[source, console] +---- +PS C:\ cd \path\to\openidm +PS C:\path\to\openidm cp samples\provisioners\provisioner.openicf.connectorinfoprovider.json conf +---- ++ +For instructions on editing this file, see xref:../integrators-guide/chap-resource-conf.adoc#net-connector-openidm["Configuring OpenIDM to Connect to the .NET Connector Server"] in the __Integrator's Guide__. + +. Download the PowerShell Connector Toolkit archive (`mspowershell-connector-1.4.2.0.zip`) from the link:https://backstage.forgerock.com/[ForgeRock Backstage, window=\_blank] site. ++ +Extract the archive and move the `MsPowerShell.Connector.dll` to the folder in which the connector server application (`connectorserver.exe`) is located. + +. Copy the PowerShell scripts from the `samples\powershell2AD\tools` directory, to the machine on which the connector server is installed. ++ + +[source, console] +---- +PS C:\path\to\openidm>dir samples\powershell2AD\tools +Directory: C:\path\to\openidm\samples\powershell2AD\tools + +Mode LastWriteTime Length Name +---- ------------- ------ ---- + + ----- 11/15/2015 01:55 PM 2813 ADAuthenticate.ps1 + ----- 11/15/2015 01:55 PM 10019 ADCreate.ps1 + ----- 11/15/2015 01:55 PM 2530 ADDelete.ps1 + ----- 11/15/2015 01:55 PM 2617 ADResolveUsername.ps1 + ----- 11/15/2015 01:55 PM 7998 ADSchema.ps1 + ----- 11/15/2015 01:55 PM 3706 ADSearch.ps1 + ----- 11/15/2015 01:55 PM 4827 ADSync.ps1 + ----- 11/15/2015 01:55 PM 2075 ADTest.ps1 + ----- 11/15/2015 01:55 PM 10044 ADUpdate.ps1 + +PS C:\path\to\openidm> +---- + +. Copy the sample connector configuration for the PowerShell connector from the `samples\provisioners` directory to your project's `conf` directory. ++ + +[source, console] +---- +PS C:\ cd \path\to\openidm +PS C:\ cp samples\provisioners\provisioner.openicf-adpowershell.json conf +---- ++ +The following excerpt of the sample connector configuration shows the configuration properties: ++ + +[source] +---- +"configurationProperties" : { + "AuthenticateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADAuthenticate.ps1", + "CreateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADCreate.ps1", + "DeleteScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADDelete.ps1", + "ResolveUsernameScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADResolveUsername.ps1", + "SchemaScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADSchema.ps1", + "SearchScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADSearch.ps1", + "SyncScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADSync.ps1", + "TestScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADTest.ps1", + "UpdateScriptFileName" : "C:/openidm/samples/powershell2AD/tools/ADUpdate.ps1", + "VariablesPrefix" : "Connector", + "QueryFilterType" : "AdPsModule", + "ReloadScriptOnExecution" : true, + "UseInterpretersPool" : true, + "SubstituteUidAndNameInQueryFilter" : true, + "UidAttributeName" : "ObjectGUID", + "NameAttributeName" : "DistinguishedName", + "PsModulesToImport" : [ "ActiveDirectory" ], + "Host" : "", + "Port" : null, + "Login" : "", + "Password" : null + }, +---- ++ +The sample connector configuration assumes that the scripts are located in `C:/openidm/samples/powershell2AD/tools/`. If you copied your scripts to a different location, adjust your connector configuration file accordingly. ++ +Note that the OpenICF framework requires the path to use forward slash characters and not the backslash characters that you would expect in a Windows path. ++ +Add a `CustomProperties` section to your connector configuration file, and set your base context, for example: ++ + +[source, javascript] +---- +"CustomProperties" : [ + "baseContext = CN=Users,DC=example,DC=com" +] +---- ++ +The PowerShell connector parses this string value to create a key=value pair. Because C# does not have built-in JSON support, this custom parser allows you to pass in configuration values beyond what is added to the static configuration. ++ +The host, port, login and password of the machine on which Active Directory runs do not need to be specified here. By default the Active Directory cmdlets pick up the first available Domain Controller. In addition, the scripts are executed using the credentials of the .Net connector server. ++ + +[NOTE] +====== +The `"ReloadScriptOnExecution"` property is set to `true` in this sample configuration. This setting causes script files to be reloaded each time the script is invoked. Having script files reloaded each time is suitable for debugging purposes. However, this property should be set to `false` in production environments, as the script reloading can have a negative performance impact. +====== ++ +In addition, make sure that the value of the `"connectorHostRef"` property in the connector configuration file matches the value that you specified in the remote connector configuration file, in step 2 of this procedure. For example: ++ + +[source, console] +---- +"connectorHostRef" : "dotnet", +---- + +==== + + +[#powershell-ad-test] +==== Testing the PowerShell Active Directory Sample + +Because you have copied all of the required configuration files into the default OpenIDM project, you can start OpenIDM with the default configuration (that is, without the `-p` option). + +[source, console] +---- +PS C:\ cd \path\to\openidm +PS C:\ .\startup.bat +---- +When OpenIDM has started, you can test the sample by using the `curl` command-line utility. The following examples test the scripts that were provided in the `tools` directory. + +==== + +. Test the connector configuration, and whether OpenIDM is able to connect to the .NET connector server with the following request. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request POST ` + "http://localhost:8080/openidm/system?_action=test" +[ + { + "ok": true, + "connectorRef": { + "bundleVersion": "1.4.1.0", + "bundleName": "MsPowerShell.Connector", + "connectorName": "Org.ForgeRock.OpenICF.Connectors.MsPowerShell.MsPowerShellConnector" + }, + "objectTypes": [ + "__ALL__", + "group", + "account" + ], + "config": "config/provisioner.openicf/adpowershell", + "enabled": true, + "name": "adpowershell" + } +] +---- + +. Query the users in your Active Directory with the following request: ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request GET ` + "http://localhost:8080/openidm/system/adpowershell/account?_queryId=query-all-ids" +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 1257, + "result": [ + { + "_id": "7c41496a-9898-4074-a537-bed696b6be92", + "distinguishedName": "CN=Administrator,CN=Users,DC=example,DC=com" + }, + { + "_id": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9", + "distinguishedName": "CN=Guest,CN=Users,DC=example,DC=com" + }, + { + "_id": "99de98a3-c125-48dd-a7c2-e21f1488ab06", + "distinguishedName": "CN=Ben Travis,CN=Users,DC=example,DC=com" + }, + { + "_id": "0f7394cc-c66a-404f-ad6d-38dbb4b6526d", + "distinguishedName": "CN=Barbara Jensen,CN=Users,DC=example,DC=com" + }, + { + "_id": "3e6fa858-ed3a-4b58-9325-1fca144eb7c7", + "distinguishedName": "CN=John Doe,CN=Users,DC=example,DC=com" + }, + { + "_id": "6feef4a0-b121-43dc-be68-a96703a49aba", + "distinguishedName": "CN=Steven Carter,CN=Users,DC=example,DC=com" + }, +... +---- + +. To return the complete record of a specific user, include the ID of the user in the URL. The following request returns the record for Steven Carter. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request GET ` + "http://localhost:8080/openidm/system/adpowershell/account/6feef4a0-b121-43dc-be68-a96703a49aba" +{ + "_id": "6feef4a0-b121-43dc-be68-a96703a49aba", + "postalCode": null, + "passwordNotRequired": false, + "cn": "Steven Carter", + "name": "Steven Carter", + "trustedForDelegation": false, + "uSNChanged": "47219", + "manager": null, + "objectGUID": "6feef4a0-b121-43dc-be68-a96703a49aba", + "modifyTimeStamp": "11/27/2014 3:37:16 PM", + "employeeNumber": null, + "sn": "Carter", + "userAccountControl": 512, + "passwordNeverExpires": false, + "displayName": "Steven Carter", + "initials": null, + "pwdLastSet": "130615726366949784", + "scriptPath": null, + "badPasswordTime": "0", + "employeeID": null, + "badPwdCount": "0", + "accountExpirationDate": null, + "userPrincipalName": "steve.carter@ad0.example.com", + "sAMAccountName": "steve.carter", + "mail": "steven.carter@example.com", + "logonCount": "0", + "cannotChangePassword": false, + "division": null, + "streetAddress": null, + "allowReversiblePasswordEncryption": false, + "description": null, + "whenChanged": "11/27/2014 3:37:16 PM", + "title": null, + "lastLogon": "0", + "company": null, + "homeDirectory": null, + "whenCreated": "6/23/2014 2:50:48 PM", + "givenName": "Steven", + "telephoneNumber": "555-2518", + "homeDrive": null, + "uSNCreated": "20912", + "smartcardLogonRequired": false, + "distinguishedName": "CN=Steven Carter,CN=Users,DC=example,DC=com", + "createTimeStamp": "6/23/2014 2:50:48 PM", + "department": null, + "memberOf": [ + "CN=employees,DC=example,DC=com" + ], + "homePhone": null +} +---- + +. Test whether you can authenticate as one of the users in your Active Directory. The username that you specify here can be either an ObjectGUID, UPN, sAMAccountname or CN. ++ + +[source, console] +---- +$ PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request POST ` + "http://localhost:8080/openidm/system/adpowershell/account?_action=authenticate&username=Steven+Carter&password=Passw0rd" +{ + "_id": "6feef4a0-b121-43dc-be68-a96703a49aba" +} +---- ++ +The request returns the ObjectGUID if the authentication is successful. + +. You can return the complete record for a specific user, using the query filter syntax described in xref:../integrators-guide/chap-data.adoc#constructing-queries["Constructing Queries"] in the __Integrator's Guide__. ++ +The following query returns the record for the guest user. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request GET ` + "http://localhost:8080/openidm/system/adpowershell/account?_queryFilter=cn+eq+guest" +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 1, + "result": [ + { + "_id": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9", + "postalCode": null, + "passwordNotRequired": true, + "cn": "Guest", + "name": "Guest", + "trustedForDelegation": false, + "uSNChanged": "8197", + "manager": null, + "objectGUID": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9", + "modifyTimeStamp": "6/9/2014 12:35:16 PM", + "employeeNumber": null, + "userAccountControl": 66082, + "whenChanged": "6/9/2014 12:35:16 PM", + "initials": null, + "pwdLastSet": "0", + "scriptPath": null, + "badPasswordTime": "0", + "employeeID": null, + "badPwdCount": "0", + "accountExpirationDate": null, + "sAMAccountName": "Guest", + "logonCount": "0", + "cannotChangePassword": true, + "division": null, + "streetAddress": null, + "allowReversiblePasswordEncryption": false, + "description": "Built-in account for guest access to the computer/domain", + "userPrincipalName": null, + "title": null, + "lastLogon": "0", + "company": null, + "homeDirectory": null, + "whenCreated": "6/9/2014 12:35:16 PM", + "givenName": null, + "homeDrive": null, + "uSNCreated": "8197", + "smartcardLogonRequired": false, + "distinguishedName": "CN=Guest,CN=Users,DC=example,DC=com", + "createTimeStamp": "6/9/2014 12:35:16 PM", + "department": null, + "memberOf": [ + "CN=Guests,CN=Builtin,DC=example,DC=com" + ], + "homePhone": null, + "displayName": null, + "passwordNeverExpires": true + } + ] +} +---- + +. Test whether you are able to create a user on the Active Directory server by sending a POST request with the `create` action. ++ +The following request creates the user `Jane Doe` on the Active Directory server. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --header "Content-Type: application/json" ` + --request POST ` + --data "{ + \"distinguishedName\" : \"CN=Jane Doe,CN=Users,DC=example,DC=com\", + \"sn\" : \"Doe\", + \"cn\" : \"Jane Doe\", + \"sAMAccountName\" : \"sample\", + \"userPrincipalName\" : \"janedoe@example.com\", + \"__ENABLE__\" : true, + \"__PASSWORD__\" : \"Passw0rd\", + \"telephoneNumber\" : \"0052-611-091\" + }" ` + "http://localhost:8080/openidm/system/adpowershell/account?_action=create" +{ + "_id": "42725210-8dce-4fdf-b0e0-393cf0377fdf", + "title": null, + "uSNCreated": "47244", + "pwdLastSet": "130615892934093041", + "cannotChangePassword": false, + "telephoneNumber": "0052-611-091", + "smartcardLogonRequired": false, + "badPwdCount": "0", + "department": null, + "distinguishedName": "CN=Jane Doe,CN=Users,DC=example,DC=com", + "badPasswordTime": "0", + "employeeID": null, + "cn": "Jane Doe", + "division": null, + "description": null, + "userPrincipalName": "janedoe@example.com", + "passwordNeverExpires": false, + "company": null, + "memberOf": [], + "givenName": null, + "streetAddress": null, + "sn": "Doe", + "initials": null, + "logonCount": "0", + "homeDirectory": null, + "employeeNumber": null, + "objectGUID": "42725210-8dce-4fdf-b0e0-393cf0377fdf", + "manager": null, + "lastLogon": "0", + "trustedForDelegation": false, + "scriptPath": null, + "allowReversiblePasswordEncryption": false, + "modifyTimeStamp": "11/27/2014 8:14:53 PM", + "whenCreated": "11/27/2014 8:14:52 PM", + "whenChanged": "11/27/2014 8:14:53 PM", + "accountExpirationDate": null, + "name": "Jane Doe", + "displayName": null, + "homeDrive": null, + "passwordNotRequired": false, + "createTimeStamp": "11/27/2014 8:14:52 PM", + "uSNChanged": "47248", + "sAMAccountName": "sample", + "userAccountControl": 512, + "homePhone": null, + "postalCode": null +} +---- + +. Test whether you are able to update a user object on the Active Directory server by sending a PUT request with the complete object, and including the user ID in the URL. ++ +The following request updates user `Jane Doe`'s entry, including her ID in the request. The update sends the same information that was sent in the `create` request, but adds an `employeeNumber`. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --header "Content-Type: application/json" ` + --header "If-Match: *" ` + --request PUT ` + --data "{ + \"distinguishedName\" : \"CN=Jane Doe,CN=Users,DC=example,DC=com\", + \"sn\" : \"Doe\", + \"cn\" : \"Jane Doe\", + \"sAMAccountName\" : \"sample\", + \"userPrincipalName\" : \"janedoe@example.com\", + \"__ENABLE__\" : true, + \"__PASSWORD__\" : \"Passw0rd\", + \"telephoneNumber\" : \"0052-611-091\", + \"employeeNumber\": \"567893\" + }" ` + "http://localhost:8080/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf" +{ + "_id": "42725210-8dce-4fdf-b0e0-393cf0377fdf", + "title": null, + "uSNCreated": "47244", + "pwdLastSet": "130615906375709689", + "cannotChangePassword": false, + "telephoneNumber": "0052-611-091", + "smartcardLogonRequired": false, + "badPwdCount": "0", + "department": null, + "distinguishedName": "CN=Jane Doe,CN=Users,DC=example,DC=com", + "badPasswordTime": "0", + "employeeID": null, + "cn": "Jane Doe", + "division": null, + "description": null, + "userPrincipalName": "janedoe@example.com", + "passwordNeverExpires": false, + "company": null, + "memberOf": [], + "givenName": null, + "streetAddress": null, + "sn": "Doe", + "initials": null, + "logonCount": "0", + "homeDirectory": null, + "employeeNumber": "567893", + "objectGUID": "42725210-8dce-4fdf-b0e0-393cf0377fdf", + "manager": null, + "lastLogon": "0", + "trustedForDelegation": false, + "scriptPath": null, + "allowReversiblePasswordEncryption": false, + "modifyTimeStamp": "11/27/2014 8:37:17 PM", + "whenCreated": "11/27/2014 8:14:52 PM", + "whenChanged": "11/27/2014 8:37:17 PM", + "accountExpirationDate": null, + "name": "Jane Doe", + "displayName": null, + "homeDrive": null, + "passwordNotRequired": false, + "createTimeStamp": "11/27/2014 8:14:52 PM", + "uSNChanged": "47253", + "sAMAccountName": "sample", + "userAccountControl": 512, + "homePhone": null, + "postalCode": null +} +---- + +. Test whether you are able to delete a user object on the Active Directory server by sending a DELETE request with the user ID in the URL. ++ +The following request deletes user `Jane Doe`'s entry. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request DELETE ` + "http://localhost:8080/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf" +---- ++ +The response includes the complete user object that was deleted. ++ +You can you attempt to query the user object to confirm that it has been deleted. ++ + +[source, console] +---- +PS C:\ curl ` + --header "X-OpenIDM-Username: openidm-admin" ` + --header "X-OpenIDM-Password: openidm-admin" ` + --request GET ` + "http://localhost:8080/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf" +{ + "message": "", + "reason": "Not Found", + "code": 404 +} +---- + +==== + + + +[#powershell-azure] +=== Connect to Azure AD + +This sample uses the Microsoft Azure Active Directory (Azure AD) PowerShell module. For more information about this module, see link:https://msdn.microsoft.com/en-us/library/jj151815.aspx[https://msdn.microsoft.com/en-us/library/jj151815.aspx, window=\_blank]. + +The sample assumes that OpenIDM runs on a local UNIX/Linux machine and that the PowerShell Connector Toolkit (and the OpenICF .NET connector server) run on a remote Windows host with access to an instance of AzureAD. Adjust the command-line examples if your OpenIDM instance runs on Windows. + +This sample demonstrates how you can synchronize managed object data such as users and groups with a Microsoft AzureAD deployment. + +[NOTE] +==== +This sample utilizes a connection between three systems: OpenIDM on UNIX/Linux, an OpenICF .NET connector server on Windows, and Azure AD "in the cloud". Internet connection times vary widely and performance is based on responses to external calls, including potential timeouts, if a command doesn't perform the first time, try again. OpenIDM’s synchronization and reconciliation performance will be fully dependent on the performance of the managed resource. +==== + +[#powershell-azure-before-you-start] +==== Before You Start + +Before you can run this sample, you need to meet several prerequisites. This section describes each of the prerequisites, and how to install or test them. + +* You must have a Microsoft account, which gives you access to Microsoft Azure. ++ +You can set up a Microsoft account at link:https://signup.live.com/[https://signup.live.com/, window=\_blank]. ++ +With a Microsoft account, you can access the Azure portal at link:http://azure.microsoft.com[http://azure.microsoft.com, window=\_blank]. + +* You must have an Azure AD cloud directory. ++ +If you do not have an existing Azure AD cloud, set one up as follows: ++ + +. Navigate to link:https://account.windowsazure.com/signup[https://account.windowsazure.com/signup, window=\_blank]. Once you log in with your Microsoft credentials, fill in the prompts and Microsoft will create an Azure subscription. + +. Navigate to link:http://portal.azure.com[http://portal.azure.com, window=\_blank], log in with your Microsoft account. + +. In the Microsoft Azure screen, select New on the left hand menu. + +. From the New list, select Security + Identity > Active Directory. + +. Complete the Add Directory form with the details of your directory, and select the check mark at the bottom of the form to submit. ++ + +image::images/ps-azure-add-directory.png[] ++ +Your directory should now be created and listed. + + +* Apart from your default Microsoft Azure account, you must have an __administrative user account__ for your Azure AD. ++ +By default your directory will have a single identity, your Microsoft Azure account. You cannot use this account to run the PowerShell Connector scripts that administer the Azure AD. ++ +If your Azure AD does not already include other administrative accounts, create a local administrative identity that is native to your directory as follows: ++ + +. Log in to https://portal.azure.com/ with your Microsoft Azure credentials. + +. From the left-hand menu, select Browse > Active Directory. + +. Select your cloud directory from the left-hand menu and select USERS in the top navigation bar. + +. At the bottom of the page select Add User and enter the details of the new administrative user. ++ + +image::images/ps-azure-add-user.png[] ++ +Select the arrow to continue. + +. On the User Profile screen, enter the details of this administrative user. Make sure that the user's Role is at least User Admin. ++ +Select the arrow to continue. + +. On the final screen, select Create and note the temporary password that is assigned to the user. ++ + +image::images/ps-azure-user-pwd.png[] ++ +Because new administrative users are forced to change their password on first login, you should log in as this user to change the password. ++ +Select the check mark to complete the new user creation process. + +. Select the username at the top right of the screen and select Sign-out to sign out of your Microsoft Azure account, then select SIGN IN > Use Another Account to sign in as your new administrative user. + +. Enter the email address of the new administrative user and select Continue. + +. Enter the temporary password that you received and select Sign In. + +. On the Update Your Password screen, enter a new password, then select Update password and sign in. ++ +You now have a new administrative user account that the PowerShell scripts will use to access your Azure AD. + + +* The Windows Azure AD Module for Windows PowerShell must be installed on the Windows host that connects to Azure. ++ +If needed, install the Azure AD Module as described in the following link:https://msdn.microsoft.com/library/azure/jj151815.aspx[Microsoft article, window=\_blank]. + +* Your Windows host must be able to contact your Azure AD deployment. ++ +Verify the connection as follows: ++ + +. Open a PowerShell window and type `Connect-Msolservice` at the command prompt. + +. On the Enter Credentials screen, enter the credentials of the administrative account that you created for the Azure directory. ++ + +image::images/ps-azure-credentials.png[] ++ +If the PowerShell command returns with no error, you have successfully connected to your remote Azure AD deployment. + + +* The OpenICF .NET connector server must be installed on your Windows host. ++ +If you have not yet installed the .NET connector server, follow the instructions in xref:../integrators-guide/index.adoc["Installing and Configuring a .NET Connector Server"] in the __Integrator's Guide__. The connector server must be running in legacy mode (see xref:../integrators-guide/index.adoc["Running the .NET Connector Server in Legacy Mode"] in the __Integrator's Guide__. + +* The PowerShell Connector Toolkit must be installed on your Windows host. ++ +If you have not yet installed the PowerShell Connector Toolkit, follow the instructions in xref:../connectors-guide/chap-powershell.adoc#chap-powershell["PowerShell Connector Toolkit"] in the __Connectors Guide__. In these instructions, you will use a command with a `/setkey` option to create a password key for your .NET connector server. You will use that key in xref:#powershell-azure-setup-sample["Setting Up the PowerShell Azure AD Sample on OpenIDM"]. ++ + +[IMPORTANT] +==== +Before you continue, check that the OpenICF .NET connector server is still running. If it is not running, restart the connector server and check the logs. In some cases, Windows blocks the PowerShell connector dll. If the connector server fails to start, right-click on `MsPowerShell.Connector.dll` and select Properties > Security. If you see the following text on that tab: + +[source, console] +---- +This file came from another computer and might be blocked to help protect + this computer. +---- +Select the Unblock button to unblock the connector dll. Then restart the connector server. +==== + +When all of the above elements are in place, you can proceed with running the sample, as described in xref:#powershell-azure-setup-sample["Setting Up the PowerShell Azure AD Sample on OpenIDM"]. + + +[#powershell-azure-setup-sample] +==== Setting Up the PowerShell Azure AD Sample on OpenIDM + +This section assumes that OpenIDM is installed on the local UNIX/Linux machine. + +==== + +. On the Windows host, create a directory for the PowerShell scripts. ++ +The sample connector configuration expects the scripts in the directory `C:/openidm/samples/powershell2AzureAD/tools/`. If you put them in a different location, adjust your connector configuration accordingly. ++ + +[source, console] +---- +PS C:\> mkdir -Path openidm\samples\powershell2AzureAD\azureADScripts + + Directory: C:\openidm\samples\powershell2AzureAD + +Mode LastWriteTime Length Name +---- ------------- ------ ---- +d---- 5/4/2016 11:26 AM azureADScripts + +PS C:\> +---- + +. Copy the PowerShell sample scripts from the OpenIDM instance on your UNIX/Linux host to the new directory on the remote Windows server. ++ +One way to do this is to run an `scp` client, such as `pscp` in your Windows terminal. The following command copies the PowerShell scripts from the OpenIDM installation to the Windows machine: ++ + +[source, console] +---- +PS C:\> cd openidm\samples\powershell2AzureAD\tools +PS C:\> pscp -r username@openidm-host:path/to/openidm/samples/powershell2AzureAD/azureADScripts/*.ps . +---- ++ +The following scripts should now be in the `azureADScripts` directory on your Windows system: ++ + +[source, console] +---- +PS C:\openidm\samples\powershell2AzureAD\azureADScripts> ls + + Directory: C:\openidm\samples\powershell2AzureAD\azureADScripts + +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 5/4/2016 11:26 AM 7258 AzureADCreate.ps1 +-a--- 5/4/2016 11:26 AM 3208 AzureADDelete.ps1 +-a--- 5/4/2016 11:26 AM 6952 AzureADSchema.ps1 +-a--- 5/4/2016 11:26 AM 8149 AzureADSearch.ps1 +-a--- 5/4/2016 11:26 AM 2465 AzureADTest.ps1 +-a--- 5/4/2016 11:26 AM 10840 AzureADUpdate.ps1 +---- ++ + +[NOTE] +====== +You need to set the execution policy, as Windows by default does not trust downloaded scripts. For more information, see the following article: link:https://technet.microsoft.com/en-us/library/ee176961.aspx[Using the Set-ExecutionPolicy Cmdlet, window=\_blank] +You can then run the `Unblock-File` cmdlet to allow OpenIDM to run the scripts on your Windows system. For more information, see the following article: link:https://technet.microsoft.com/en-us/library/hh849924.aspx[Unblock-File, window=\_blank]. +====== + +. On the Linux/UNIX machine on which OpenIDM is installed, navigate to the `path/to/openidm/samples/powershell2AzureAD` directory, and open the `provisioner.openicf.connectorinfoprovider.json conf` file. + +. Edit the remote connector server configuration file to match the settings of the remote .NET connector server. ++ +Change the port to `8760`, and the password (`key`) that you configured for the .NET connector server. ++ +The following example assumes that the .NET connector server is running on the host `198.51.100.1`, listening on the default port, and configured with a secret key of `Passw0rd`: ++ + +[source] +---- +{ + "remoteConnectorServers" : + [ + { + "name" : "dotnet", + "host" : "198.51.100.1", + "port" : 8760, + "useSSL" : false, + "timeout" : 0, + "key" : "Passw0rd" + } + ] +} +---- + +. Open the sample Azure AD PowerShell connector configuration file, `provisioner.openicf-azureadpowershell.json`, and edit it to match your deployment. In particular, set the following properties in that file: ++ + +[source] +---- +"Host" : "198.51.100.1", +"Port" : 8760, +"Login" : "admin@example.onmicrosoft.com", +"Password" : "Passw0rd", +---- ++ +-- + +`Host`:: +The hostname or IP address on which the .NET connector server is running. + +`Port`:: +The port on which the .NET connector server is listening (`8760` by default in legacy mode). + +`Login`:: +The username of the administrative account you created for the Azure directory in the previous section. + +`Password`:: +The password of the administrative account you created for the Azure directory in the previous section. + +-- ++ +If you have placed the PowerShell scripts in a directory other than the default (`C:\openidm\samples\powershell2AzureAD\azureADScripts`) you must also update those paths in the PowerShell connector configuration file. + +. Start OpenIDM with the PowerShell AzureAD sample configuration: ++ + +[source, console] +---- +$ cd path/to/openidm +$ ./startup.sh -p samples/powershell2AzureAD +---- + +==== + + +[#powershell-azure-run-sample] +==== Managing Users and Groups with the PowerShell Azure AD Sample + +This section walks you through several REST commands that enable you to test the connector configuration, and perform basic CRUD operations in your Azure AD, through the PowerShell connector. + +==== + +. Test that the connector has been configured correctly and that the Azure AD resource can be reached: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/system/azureadpowershell?_action=test" +{ + "name": "azureadpowershell", + "enabled": true, + "config": "config/provisioner.openicf/azureadpowershell", + "objectTypes": [ + "__ALL__", + "account", + "group" + ], + "connectorRef": { + "bundleName": "MsPowerShell.Connector", + "connectorName": "Org.ForgeRock.OpenICF.Connectors.MsPowerShell.MsPowerShellConnector", + "bundleVersion": "1.4.2.0" + }, + "displayName": "PowerShell Connector ", + "ok": true +} +---- ++ +If you see no response from this connector test, review any changes that you made to the `provisioner-openicf*` files in your project's `conf/` subdirectory. If you've made changes appropriate for your deployment, wait a couple of minutes and try again. + +. Query the IDs of the existing users in your Azure AD deployment: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/azureadpowershell/account?_queryId=query-all-ids" +{ + "result": [ { + "_id": "51560d42-e60e-49a8-855b-42b6eca35ca6", + "UserPrincipalName": "admin@example.onmicrosoft.com" + }, + { + "_id": "5e63b42f-c93a-466f-af86-f0a8d00f2491", + "UserPrincipalName": "scarter@example.onmicrosoft.com" + } ], +... +} +---- + +. Use a query filter to return all details of all existing users in your Azure AD: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/azureadpowershell/account?_queryFilter=true" +{ + "result": [ + { + "_id": "51560d42-e60e-49a8-855b-42b6eca35ca6", + "LiveId": "10033FFF96C5186D", + "FirstName": "Barbara", + "LastName": "Jensen", + "UserPrincipalName": "admin@example.onmicrosoft.com", + "AlternateEmailAddress" : [ "bjensen@example.com" ], + "LastPasswordChangeTimestamp": "3/15/2016 11:02:19 AM", + "DisplayName": "Barbara Jensen", + "PasswordNeverExpires": false, + "MobilePhone" : "+1 3602297105" + }, + { + "_id": "5e63b42f-c93a-466f-af86-f0a8d00f2491", + "LiveId": "1003BFFD96A4CFBA", + "FirstName": "Sam", + "LastName": "Carter", + "UserPrincipalName": "scarter@example.onmicrosoft.com" + "AlternateEmailAddresses": [ "scarter@example.com" ], + "LastPasswordChangeTimestamp": "3/7/2016 1:09:31 PM", + "DisplayName": "Sam Carter", + "PasswordNeverExpires": false, + "MobilePhone" : "+1 3602297105" + } + ], +...} +---- + +. Return details for a specific user account, by its `_id` ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/azureadpowershell/account/51560d42-e60e-49a8-855b-42b6eca35ca6" +---- + +. Create a new user in Azure AD. Substitute the domain for your Azure AD deployment for `example.onmicrosoft.com`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --header "content-type: application/json" \ + --data '{ + "PasswordNeverExpires": false, + "AlternateEmailAddresses": ["John.Bull@example.com"], + "LastName": "Bull", + "PreferredLanguage": "en-GB", + "FirstName": "John", + "UserPrincipalName": "Dev_John.Bull@example.onmicrosoft.com", + "DisplayName": "John Bull" + }' \ + "http://localhost:8080/openidm/system/azureadpowershell/account?_action=create" +{ + "_id" : "d4aac947-2037-4f29-b0f5-d404fd99938c", + "LiveId" : "10037FFE979FB2C1", + "FirstName" : "John", + "LastName" : "Bull", + "UserPrincipalName" : "Dev_John.Bull@example.onmicrosoft.com", + "AlternateEmailAddresses" : [ "John.Bull@example.com" ], + "LastPasswordChangeTimestamp" : "5/5/2016 3:52:43 PM", + "DisplayName" : "John Bull", + "PasswordNeverExpires" : false, + "PreferredLanguage" : "en-GB" +} +---- ++ +Rerun the same command. You should see the following error: ++ + +[source, console] +---- +{ + "code" : 500, + "reason" : "Internal Server Error", + "message" : "Operation CREATE failed with ConnectorException on system object: + Dev_John.Bull@example.onmicrosoft.com" +} +---- + +. Update the user entry that you have just created with a patch request. Include the `_id` of the new user in the URL. Save that `_id` value for a later step. ++ +The following example updates the user's display name: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "if-match: *" \ + --header "content-type: application/json" \ + --request PATCH \ + --data '[ + { + "operation": "replace", + "field": "DisplayName", + "value": "John P. Bull" + } + ]' \ + "http://localhost:8080/openidm/system/azureadpowershell/account/d4aac947-2037-4f29-b0f5-d404fd99938c" +{ + "_id" : "d4aac947-2037-4f29-b0f5-d404fd99938c", + "LiveId" : "10037FFE979FB2C1", + "FirstName" : "John", + "LastName" : "Bull", + "UserPrincipalName" : "Dev_John.Bull@mikejangfr.onmicrosoft.com", + "AlternateEmailAddresses" : [ "John.Bull@example.com" ], + "LastPasswordChangeTimestamp" : "5/5/2016 3:52:43 PM", + "DisplayName" : "John P. Bull", + "PasswordNeverExpires" : false, + "PreferredLanguage" : "en-GB" +} +---- + +. Now create a group: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header 'content-type: application/json' \ +--request POST \ +--data '{ + "DisplayName" : "Dev Testers group", + "Description" : "Description of a Dev Group" +}' \ +'http://localhost:8080/openidm/system/azureadpowershell/group?_action=create' + { + "_id" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd", + "Members" : [ ], + "DisplayName" : "Dev Testers Group", + "GroupType" : "Security", + "Description" : "Description of a Dev Group", + "objectId" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd" +} +---- + +. Add your recently created user to this new group. Use the `_id` of that user, as the `ObjectId`. Use the `_id` of the newly created group in the endpoint: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--header "If-Match: *" \ +--request PUT \ +--data '{ + "Members" : [ + { + "ObjectId" : "d4aac947-2037-4f29-b0f5-d404fd99938c" + } + ] +}' \ +"http://localhost:8080/openidm/system/azureadpowershell/group/9091be74-f37e-408d-9198-2d2b5f4b4cdd" + { + "_id" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd", + "Members" : [ { + "ObjectId" : "d4aac947-2037-4f29-b0f5-d404fd99938c", + "DisplayName" : "John P. Bull", + "GroupMemberType" : "User", + "EmailAddress" : "Dev_John.Bull@example.onmicrosoft.com" + } ], + "DisplayName" : "Testing Devs Group", + "GroupType" : "Security", + "Description" : "Description of a Dev Group", + "objectId" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd" +} +---- + +. Confirm the result, by the `_id` of the group: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"http://localhost:8080/openidm/system/azureadpowershell/group/9091be74-f37e-408d-9198-2d2b5f4b4cdd" +---- + +. Update a label for the group. Use the same group `_id`: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--header "If-Match: *" \ +--request PUT \ +--data '{ + "_id" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd", + "Description" : "Dev Masters Group", + "Members" : [ + { + "ObjectId" : "d4aac947-2037-4f29-b0f5-d404fd99938c", + "DisplayName" : "John P. Bull", + "GroupMemberType" : "User", + "EmailAddress" : "Dev_John.Bull@example.onmicrosoft.com" + } + ], + "DisplayName" : "Testing Devs Group", + "GroupType" : "Security", + "objectId" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd" +}' \ +"http://localhost:8080/openidm/system/azureadpowershell/group/9091be74-f37e-408d-9198-2d2b5f4b4cdd" +---- ++ +You should see the new `Description` in the output. + +. Remove the user from the new group. Use the same group `_id` Note how the `Members` in the `--data` block, and the output, are blank: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--header "Content-Type: application/json" \ +--header "If-Match: *" \ +--request PUT \ +--data '{ + "_id" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd", + "Description" : "Dev Masters Group", + "Members" : [ ], + "DisplayName" : "Testing Devs Group", + "GroupType" : "Security", + "objectId" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd" +}' \ +"http://localhost:8080/openidm/system/azureadpowershell/group/9091be74-f37e-408d-9198-2d2b5f4b4cdd" + { + "_id" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd", + "Members" : [ ], + "DisplayName" : "Testing Devs Group", + "GroupType" : "Security", + "Description" : "Dev Masters Group", + "objectId" : "9091be74-f37e-408d-9198-2d2b5f4b4cdd" +} +---- + +. Delete the user that you created earlier: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request DELETE \ +"http://localhost:8080/openidm/system/azureadpowershell/account/d4aac947-2037-4f29-b0f5-d404fd99938c" +---- ++ +To verify that the user was deleted, run the REST call to `query-all-ids` shown earlier in this section. The ID associated with that user should have been removed. + +==== + + +[#azure-sample-recon] +==== Reconciling Users Between OpenIDM and Azure AD + +In this section, you'll run commands that demonstrate reconciliation mappings between OpenIDM managed users and your remote instance of Azure AD. + +In preparation, create a new user on the Azure AD system: + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +--header "content-type: application/json" \ +--data '{ + "UserPrincipalName": "CEO@example.onmicrosoft.com", + "LastName": "Officer", + "FirstName": "Chief", + "DisplayName": "Chief Executive Officer", + "PasswordNeverExpires": false +}' \ +"http://localhost:8080/openidm/system/azureadpowershell/account?_action=create" +---- + +==== +In the steps that follow, you'll run reconciliations to see what happens to that user in the OpenIDM data store. + +. Review the list of current managed users in the OpenIDM repository, filtered for the `userName` that starts with (`sw`) CEO: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"http://localhost:8080/openidm/managed/user?_queryFilter=userName+sw+'CEO'" +---- ++ +Until you reconcile the Azure AD repository to OpenIDM, the output should be empty: ++ + +[source, console] +---- +{ + "result" : [ ], + "resultCount" : 0, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- + +. Run a reconciliation from Azure AD to OpenIDM: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +"http://localhost:8080/openidm/recon?_action=recon&mapping=systemAzureadpowershellAccount_managedUser&waitForCompletion=true" + + { + "_id" : "71811f1c-2ec0-47ae-ba47-d62c7094201b-1105", + "state" : "SUCCESS" +} +---- + +. Now rerun the command to list of current managed users in the OpenIDM repository, filtered for the `userName` that starts with (`sw`) CEO: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"http://localhost:8080/openidm/managed/user?_queryFilter=userName+sw+'CEO'" + { + "result" : [ { + "_id" : "3a012a60-19c2-4fb4-99cc-0bb82dc4588c", + "_rev" : "1", + "userName" : "CEO@example.onmicrosoft.com", + "mail" : "CEO@example.onmicrosoft.com", + "sn" : "Officer", + "givenName" : "Chief", + "accountStatus" : "active", + "effectiveRoles" : [ ], + "effectiveAssignments" : [ ] + } ], + "resultCount" : 1, + "pagedResultsCookie" : null, + "totalPagedResultsPolicy" : "NONE", + "totalPagedResults" : -1, + "remainingPagedResults" : -1 +} +---- ++ + +. Delete that CEO user from the Azure AD system, by the `_id` shown earlier when you searched that system: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request DELETE \ +"http://localhost:8080/openidm/system/azureadpowershell/account/3a012a60-19c2-4fb4-99cc-0bb82dc4588c" +---- ++ +If successful, you'll see the data for the CEO user one last time. + +. Run a second reconciliation from the remote Azure AD repository to OpenIDM: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request POST \ +"http://localhost:8080/openidm/recon?_action=recon&mapping=systemAzureadpowershellAccount_managedUser&waitForCompletion=true" +---- + +. Rerun the command to search the OpenIDM repository for a `userName` that starts with 'CEO' one more time, to confirm that user has been reconciled out of the OpenIDM repository: ++ + +[source, console] +---- +$ curl \ +--header "X-OpenIDM-Username: openidm-admin" \ +--header "X-OpenIDM-Password: openidm-admin" \ +--request GET \ +"http://localhost:8080/openidm/managed/user?_queryFilter=userName+sw+'CEO'" +---- + +==== + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-roles-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-roles-sample.adoc new file mode 100644 index 000000000..34be354e5 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-roles-sample.adoc @@ -0,0 +1,2099 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-roles-sample] +== Roles Samples - Demonstrating the OpenIDM Roles Implementation + +This chapter illustrates how roles are managed in OpenIDM, and how they can be used to provision to external systems, based on certain criteria. +OpenIDM supports two types of roles: + +* __Provisioning roles__ - used to specify how objects are provisioned to an external system. + +* __Authorization roles__ - used to specify the authorization rights of a managed object internally, within OpenIDM. + +Both provisioning roles and authorization roles use the relationships mechanism to link the role object, and the managed object to which the role applies. For more information about relationships between objects, see xref:../integrators-guide/chap-users-groups-roles.adoc#managing-relationships["Managing Relationships Between Objects"] in the __Integrator's Guide__. +There are three samples in this chapter: + +* The __provisioning__ sample (in `openidm/samples/roles/provrole`) demonstrates the operations that can be performed over the REST interface, or by using the Admin UI, to manage roles in OpenIDM. + +* The __crudops__ sample (in `openidm/samples/roles/crudops`) demonstrates how attributes are provisioned to an external system (an LDAP directory), based on role membership. + +* The __temporalConstraints__ sample (in `openidm/samples/roles/temporalConstraints`) builds on the other two roles samples and demonstrates how to apply time-based restrictions to role grants. + +The separate samples are described in the sections that follow. + +[IMPORTANT] +==== +Most of the commands in this sample can be run by using the command-line but it is generally much easier to use the Admin UI. In some cases, the command-line version makes it easier to explain what is happening in OpenIDM. Therefore, in all steps, the sample first shows the command-line version, and then provides the equivalent method of running the command in the Admin UI. +==== + +[#sample-roles-crudops] +=== CRUDOPS Role Sample - Working With Managed Roles + +This sample demonstrates how to manage roles by using the REST interface, or the Admin UI. The sample covers the tasks discussed in the following sections: + +* xref:#crudops-create-role["Creating a Managed Role"] + +* xref:#crudops-read-role["Reading and Searching Managed Roles"] + +* xref:#crudops-assign-role["Granting a Managed Role to a User"] + +* xref:#crudops-remove-assignment["Removing a Managed User's Roles"] + +* xref:#crudops-delete-role["Deleting a Managed Role"] + + +[#roles-before-you-start] +==== Before You Start + +This sample does not include its own configuration. Before you work through the sample, start OpenIDM with the default configuration, as follows: + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh +Executing ./startup.sh... +Using OPENIDM_HOME: /path/to/openidm +Using PROJECT_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/conf/logging.properties +Using boot properties at /path/to/openidm/conf/boot/boot.properties +-> OpenIDM ready +---- + + +[#crudops-create-role] +==== Creating a Managed Role + +In this section, you will create two managed roles - an __Employee__ role and a __Contractor__ role. The sample shows how to create the first role directly over the REST interface and the second by using the Admin UI. Choose whichever method is easiest for you to create both roles. + +To create the Employee role by using the REST interface: + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name" : "Employee", + "description": "Role granted to workers on the payroll." + }' \ + "http://localhost:8080/openidm/managed/role?_action=create" +{ + "_id": "ad19979e-adbb-4d35-8320-6db50646b432", + "_rev": "1", + "name": "Employee", + "description": "Role granted to workers on the payroll." +} +---- +Take note of the role's identifier (ad19979e-adbb-4d35-8320-6db50646b432 in this example). Although you can create roles with a PUT request and specify your own ID, you should use system-generated IDs in a production environment to avoid conflicts and referential integrity issues. + +==== +To create the Contractor role by using the Admin UI: + +. Point your browser to the Admin UI URL (for example `\https://localhost:8443/admin/`). + +. On the Dashboard, click Manage Role > New Role. + +. Enter a name for the role (Contractor) and a description. + +. Click Save. ++ +The managed role has been created, but currently has no assignments. These will be added in the second roles sample. ++ + +[NOTE] +====== +The Admin UI creates managed objects with an unmutable, system-generated identifier. You will see this in the next section, in which you read or query the role object. +====== + +==== + + +[#crudops-read-role] +==== Reading and Searching Managed Roles + +This section shows how to read, and search managed role objects. +The easiest way to see a list of managed role objects is by using the Admin UI: + +. Navigate to the Admin UI URL. + +. On the Dashboard, click Manage Role. ++ +The list of roles (currently only Contractor and Employee) is displayed. + +If you know the identifier, it is easy to read a managed role object over the REST interface. The following command reads the Employee role, that you created in the previous section: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role/ad19979e-adbb-4d35-8320-6db50646b432" +{ + "_id": "ad19979e-adbb-4d35-8320-6db50646b432", + "_rev": "1", + "name": "Employee", + "description": "Role granted to workers on the payroll." +} +---- +You can also __search__ for the role object, with a filtered query, if you know the role name. For more information about filtered queries, see xref:../integrators-guide/chap-data.adoc#query-filters["Common Filter Expressions"] in the __Integrator's Guide__. + +The following query retrieves all roles with a name equal to "Contractor". The `_prettyPrint=true` parameter displays the result in a format that is easy to read: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role?_queryFilter=/name+eq+'Contractor'&_prettyPrint=true" + +{ + "result": [ + { + "_id": "b02d2531-5066-415e-bc90-31fe57e02322", + "_rev": "1", + "name": "Contractor", + "description": "Role granted to contract workers." + } + ], + ... +} +---- +To retrieve a list of all the managed roles that have been defined, query the `managed/role` endpoint, as follows: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role?_queryFilter=true&_prettyPrint=true" + +{ + "result" : [ { + "_id" : "ad19979e-adbb-4d35-8320-6db50646b432", + "_rev" : "1", + "name" : "Employee", + "description" : "Role granted to workers on the payroll." + }, { + "_id" : "b02d2531-5066-415e-bc90-31fe57e02322", + "_rev" : "1", + "name" : "Contractor", + "description" : "Role granted to contract workers." + } ], + ... +} +---- + + +[#crudops-assign-role] +==== Granting a Managed Role to a User + +For a role to be useful, it must be granted to a managed user. This section creates a new managed user entry, Felicitas Doe, then assigns the Employee role, created in the previous section, to Felicitas's user entry. + +==== + +. Create the user entry over REST, as follows: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "mail":"fdoe@example.com", + "sn":"Doe", + "telephoneNumber":"555-1234", + "userName":"fdoe", + "givenName":"Felicitas", + "description":"Felicitas Doe", + "displayName":"fdoe" + }' \ + "http://localhost:8080/openidm/managed/user?_action=create" +{ + "_id": "837085ae-766e-417c-9b7e-c36eee4352a3", + "_rev": "1", + "mail": "fdoe@example.com", + "sn": "Doe", + "telephoneNumber": "555-1234", + "userName": "fdoe", + "givenName": "Felicitas", + "description": "Felicitas Doe", + "displayName": "fdoe", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- ++ +Note that Felicitas has no `effectiveRoles` or `effectiveAssignments` by default. ++ + +[TIP] +====== +Create the user entry in the Admin UI: + +* Click Manage User > New User from the Dashboard. + +====== + +. Grant the Employee role to Felicitas's entry by sending a PATCH request to update her entry, and providing a pointer to the role ID: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : {"_ref": "managed/role/ad19979e-adbb-4d35-8320-6db50646b432"} + } + ]' \ + "http://localhost:8080/openidm/managed/user/837085ae-766e-417c-9b7e-c36eee4352a3" +{ + "_id": "837085ae-766e-417c-9b7e-c36eee4352a3", + "_rev": "2", + "mail": "fdoe@example.com", + "sn": "Doe", + "telephoneNumber": "555-1234", + "userName": "fdoe", + "givenName": "Felicitas", + "description": "Felicitas Doe", + "displayName": "fdoe", + "accountStatus": "active", + "effectiveRoles": [ + { + "_ref": "managed/role/ad19979e-adbb-4d35-8320-6db50646b432" + } + ], + "effectiveAssignments": [] +} +---- ++ + +[TIP] +====== +Grant the role in the Admin UI: + +.. Select Manage > User and click on fdoe's entry. + +.. Click Provisioning Roles, select the Employee role and click Add Role. + +OR + +.. Select Manage > Role and click on the Employee Role. + +.. Select the Role Members tab, click Add Role Members, browse for fdoe's entry, and click Add. + +====== + +. Now, query Felicitas's entry to return her `roles` and __effectiveRoles__. ++ +The `effectiveRoles` property is a virtual property whose value is calculated based on the roles that have been granted to a user, either manually as in this example, or dynamically, through a script or condition. For more information about dynamically granted roles, see xref:../integrators-guide/chap-users-groups-roles.adoc#granting-roles-dynamically["Granting Roles Dynamically"] in the __Integrator's Guide__. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/givenName+eq+'Felicitas'&_fields=_id,userName,roles,effectiveRoles" +{ + "result" : [ { + "_id" : "837085ae-766e-417c-9b7e-c36eee4352a3", + "_rev" : "2", + "userName" : "fdoe", + "roles" : [ { + "_ref": "managed/role/ad19979e-adbb-4d35-8320-6db50646b432", + "_refProperties": { + "_id": "4a42cd0b-d5d0-47e9-81e7-513aed74f6bc", + "_rev": "1" + } ], + "effectiveRoles" : [ { + "_ref" : "managed/role/ad19979e-adbb-4d35-8320-6db50646b432" + } ] + } ], +... +} +---- ++ +Note that Felicitas's `roles` and `effectiveRoles` attributes both show the reference to the `Employee` role ID. + +==== + + +[#crudops-remove-assignment] +==== Removing a Managed User's Roles + +Imagine that the Employee role was erroneously granted to Felicitas, and must be removed from her user entry. + +To remove a granted role from a managed user, send a DELETE request to the user entry, specifying the ID of the __relationship__ that must be removed. Note that this is not the ID of the role. + +The request to remove the Employee role from Felicitas's entry is as follows: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/user/837085ae-766e-417c-9b7e-c36eee4352a3/roles/4a42cd0b-d5d0-47e9-81e7-513aed74f6bc" +{ + "_ref": "managed/role/ad19979e-adbb-4d35-8320-6db50646b432", + "_refProperties": { + "_id": "4a42cd0b-d5d0-47e9-81e7-513aed74f6bc", + "_rev": "1" + } +} +---- +If you query Felicitas's entry again, you will notice that her `roles` and `effectiveRoles` properties are now empty: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/givenName+eq+'Felicitas'&_fields=_id,userName,roles,effectiveRoles" +{ + "result" : [ { + "_id" : "837085ae-766e-417c-9b7e-c36eee4352a3", + "_rev" : "2", + "userName" : "fdoe", + "roles": [], + "effectiveRoles": [] + } ], +... +} +---- +There are other methods to remove a granted role from a user over the REST interface. These are described in xref:../integrators-guide/chap-users-groups-roles.adoc#delete-role-user["Deleting a User's Roles"] in the __Integrator's Guide__. + +[TIP] +==== +You can also remove the granted role from fdoe's entry in the Admin UI as follows: + +. Select Manage > User and click on fdoe's entry. + +. On the Provisioning Roles tab, click the checkbox next to the Employee role and click Remove Selected Provisioning Roles. + +==== + + +[#crudops-delete-role] +==== Deleting a Managed Role + +The final step in basic role management is to remove an existing role. In this section, we will remove the Contractor role we created previously. + +Note that it is not possible to remove a role that is already granted to a user. To demonstrate this, we temporarily grant the Contractor role to Felicitas. + +==== + +. Assign the Contractor role to Felicitas, either by using the Admin UI, as described previously, or over the REST interface. If you use the REST interface, you will need to remember the system-generated identifier that was output when you queried the roles. ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : {"_ref": "managed/role/b02d2531-5066-415e-bc90-31fe57e02322"} + } + ]' \ + "http://localhost:8080/openidm/managed/user/837085ae-766e-417c-9b7e-c36eee4352a3" +{ + "_id": "837085ae-766e-417c-9b7e-c36eee4352a3", + "_rev": "4", + "mail": "fdoe@example.com", + "sn": "Doe", + "telephoneNumber": "555-1234", + "userName": "fdoe", + "givenName": "Felicitas", + "description": "Felicitas Doe", + "displayName": "fdoe", + "accountStatus": "active", + "effectiveRoles": [ + { + "_ref": "managed/role/b02d2531-5066-415e-bc90-31fe57e02322" + } + ], + "effectiveAssignments": [] +} +---- + +. Now, try to delete the Contractor role: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/role/b02d2531-5066-415e-bc90-31fe57e02322" +{ + "code": 409, + "reason": "Conflict", + "message": "Cannot delete a role that is currently granted" +} +---- ++ +As you can see from the previous output, it is not possible to delete a role that is still granted to a user. + +. Remove the role from Felicitas's entry, either by using the Admin UI (as described previously), or by using a DELETE request, specifying the ID of the __relationship__ that must be removed. Note that this is not the ID of the role. You will need to read Felicitas's `roles` property to obtain the relationship ID: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/givenName+eq+'Felicitas'&_fields=_id,userName,roles,effectiveRoles" +{ + "result" : [ { + "_id" : "837085ae-766e-417c-9b7e-c36eee4352a3", + "_rev" : "3", + "userName" : "fdoe", + "roles" : [ { + "_ref": "managed/role/b02d2531-5066-415e-bc90-31fe57e02322", + "_refProperties": { + "_id": "93703eff-a7ef-4cf3-80b1-f86fa3607978", + "_rev": "1" + } ], + "effectiveRoles" : [ { + "_ref" : "managed/role/b02d2531-5066-415e-bc90-31fe57e02322" + } ] + } ], +... +} +---- ++ +In this example, the relationship ID is 93703eff-a7ef-4cf3-80b1-f86fa3607978. ++ +The following request removes the Contractor role from Felicitas's entry: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/user/837085ae-766e-417c-9b7e-c36eee4352a3/roles/93703eff-a7ef-4cf3-80b1-f86fa3607978" +{ + "_ref": "managed/role/b02d2531-5066-415e-bc90-31fe57e02322", + "_refProperties": { + "_id": "93703eff-a7ef-4cf3-80b1-f86fa3607978", + "_rev": "1" + } +} +---- + +. Now that no users are granted the Contractor role, you can delete the role as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/role/b02d2531-5066-415e-bc90-31fe57e02322" +{ + "_id": "b02d2531-5066-415e-bc90-31fe57e02322", + "_rev": "1", + "name": "Contractor", + "description": "Role granted to contract workers." +} +---- ++ +The DELETE operation returns the complete role entry. ++ + +[TIP] +====== +Delete the Contractor role by using the Admin UI: + +.. Select Manage > Role. + +.. Select the Contractor role and click Delete Selected. + +====== + +. Verify that the Contractor role has been deleted by querying the list of managed role objects: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role?_queryFilter=true&_prettyPrint=true" +{ + "result": [ + { + "_id": "ad19979e-adbb-4d35-8320-6db50646b432", + "_rev": "1", + "name": "Employee", + "description": "Role granted to workers on the payroll." + } + ], + ... +} +---- ++ +Note that only the Employee role remains. ++ + +[TIP] +====== +List the remaining role objects in the Admin UI by clicking Manage > Role. +====== + +==== +This concludes the basic role management operations. In the next section, you will see how to add assignments to roles, and how those assignments are used to provision users to external systems. + + + +[#more-sample-roles-prov] +=== Provisioning Role Sample - Provisioning to an LDAP Server + +The main purpose of OpenIDM roles is to provision a set of attributes, based on a managed user's role membership. + +This sample builds on what you learnt in the previous sample, and you will create the same Employee and Contractor roles that were described in that sample. This sample also builds on Sample 2b (described in xref:chap-ldap-samples.adoc#more-sample-2b["Sample 2b - LDAP Two Way"]), and provisions users from the managed user repository to an OpenDJ directory. + +The sample assumes a company, example.com. As an __Employee__ of example.com, a user should be added to two groups in OpenDJ - the Employees group and the Chat Users group (presumably to access certain internal applications). As a __Contractor__, a user should be added only to the Contractors group in OpenDJ. A user's employee type must also be set correctly in OpenDJ, based on the role that is granted to the user. + +[#external-ldap-config-roles-prov] +==== External LDAP Configuration + +Configure OpenDJ as for sample 2 (see xref:chap-ldap-samples.adoc#external-ldap-config-2["LDAP Server Configuration"]). The LDAP user must have write access to create users from OpenIDM on the LDAP server. When you set up the LDAP server, import the LDIF file for this sample (`openidm/samples/roles/provrole/data/Example.ldif).` + + +[#install-sample-roles-prov] +==== Before You Start + +This section sets up the scenario by performing the following tasks: + +. Start OpenIDM with the configuration for the provisioning roles sample. + +. Create the Employee and Contractor roles that you created in the previous sample. + +. Reconcile the managed user repository with the user entries in the LDAP server. + + +==== + +. Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the Provisioning sample. ++ + +[source, console] +---- +$ cd /path/to/openidm +---- ++ + +[source, console] +---- +$ startup.sh -p samples/roles/provrole +---- + +. Create the Employee and Contractor roles, either by using the Admin UI (as described in the previous sample), or by running the following commands: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name" : "Employee", + "description": "Role granted to workers on the payroll." + }' \ + "http://localhost:8080/openidm/managed/role?_action=create" +{ + "_id" : "2902afd5-155a-49c0-9dd9-7e6bfcf1708a", + "_rev" : "1", + "name" : "Employee", + "description" : "Role granted to workers on the payroll." +} +---- ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name" : "Contractor", + "description": "Role granted to contract workers." + }' \ + "http://localhost:8080/openidm/managed/role?_action=create" +{ + "_id": "e7f649ad-8013-4673-a52a-bdcac7483111", + "_rev": "1", + "name": "Contractor", + "description": "Role granted to contract workers." +} +---- ++ +Note the IDs of these two roles because you will use them in the commands that follow. + +. Reconcile the repository. ++ +The `sync.json` configuration file for this sample includes two mappings: `systemLdapAccounts_managedUser`, which synchronizes users from the source LDAP server with the target OpenIDM repository; and `managedUser_systemLdapAccounts`, which synchronizes changes from the OpenIDM repository with the LDAP server. ++ +Run a reconciliation operation for the first mapping, either by using the Admin UI, or over the REST interface: ++ + +* To use the Admin UI, select Configure > Mapping, click on the first mapping (System/Ldap/Account --> Managed User) and click Reconcile Now. + +* To use the REST interface, run the following command: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemLdapAccounts_managedUser&waitForCompletion=true" +{ + "_id": "b5c535f8-5c1f-44dc-afa3-40d4f9984925-24", + "state": "SUCCESS" +} +---- + + +==== +The sample is now ready to demonstrate provisioning roles. + + +[#run-sample-roles-prov] +==== Run the Sample + +This section assumes that you have reconciled the managed user repository to populate it with the users from the LDAP server, and that you have created the Employee and Contractor roles. +This part of the sample demonstrates the following features of the OpenIDM roles implementation: + +* xref:#provrole-add-assignments["Adding Assignments to a Role Definition"] + +* xref:#provrole-effective-assignments["Granting a Role to a User and Observing that User's Role Assignments"] + +* xref:#provrole-propagate-assignments["Propagating Assignments to an External System"] + +* xref:#provrole-remove-role["Removing a Role Grant From a User and Observing That User's Role Assignments"] + + +[#provrole-add-assignments] +===== Adding Assignments to a Role Definition + +A role __assignment__ is the logic that provisions a managed user to an external system, based on some criteria. The most common use case of a role assignment is the provisioning of specific attributes to an external system, based on the role or roles that the managed user has been granted. Assignments are sometimes called __entitlements__. For more information about assignments, see xref:../integrators-guide/chap-users-groups-roles.adoc#working-with-role-assignments["Working With Role Assignments"] in the __Integrator's Guide__. + +In this section, you will create assignments and add them to the two roles that you created previously. This section assumes the following scenario: + +example.com's policy requires that every employee has the correct value for their `employeeType` in their corporate directory (OpenDJ). + +==== + +. Display the roles that you created in the previous section: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role?_queryFilter=true" +{ + "result" : [ + { + "_id" : "2902afd5-155a-49c0-9dd9-7e6bfcf1708a", + "_rev" : "1", + "name" : "Employee", + "description" : "Role granted to workers on the payroll." + }, + { + "_id" : "e7f649ad-8013-4673-a52a-bdcac7483111", + "_rev" : "1", + "name" : "Contractor", + "description" : "Role granted to contract workers." + } + ], +... +} +---- ++ + +[TIP] +====== +Display the roles in the Admin UI by selecting Manage > Role. +====== + +. Create a new managed assignment named Employee. ++ +The assignment is specifically for the mapping from the managed user repository to the LDAP server. The assignment sets the value of the `employeeType` attribute on the LDAP server to `Employee`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-type: application/json" \ + --request POST \ + --data '{ + "name" : "Employee", + "description": "Assignment for employees.", + "mapping" : "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + } + ] + }' \ + "http://localhost:8080/openidm/managed/assignment?_action=create" +{ + "_id": "f2830b80-6ab8-416b-b219-d3bf2efd0ed3", + "_rev": "1", + "name": "Employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ] +} +---- ++ + +[TIP] +====== +Create the assignment in the Admin UI: + +.. Select Manage > Assignment, and click New Assignment. + +.. Enter a name and description for the assignment, and select the mapping for which the assignment is applied (managedUser_systemLdapAccounts). + +.. Click Add Assignment. + +.. Select the Attributes tab, and click Add an Attribute. + +.. Select employeeType, and enter the value Employee. + +.. Click Save. + +====== + +. Add the assignment to the Employee role that you created previously. ++ +Assignments are implemented as __relationship objects__. This means that you add an assignment to a role by __referencing__ the assignment in the role's `assignments` field: ++ +This command patches the Employee role to update its `assignments` field. ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/assignments/-", + "value" : { "_ref": "managed/assignment/f2830b80-6ab8-416b-b219-d3bf2efd0ed3"} + } + ]' \ + "http://localhost:8080/openidm/managed/role/2902afd5-155a-49c0-9dd9-7e6bfcf1708a" +{ + "_id": "2902afd5-155a-49c0-9dd9-7e6bfcf1708a", + "_rev": "2", + "name": "Employee", + "description": "Role granted to workers on the payroll." +} +---- ++ + +[TIP] +====== +Add the assignment to the role in the Admin UI: + +.. Select Manage > Role, and select the Employee role. + +.. On the Managed Assignments tab, click Add Managed Assignments. + +.. Select the Employee assignment and click Add. + +====== + +==== + + +[#provrole-effective-assignments] +===== Granting a Role to a User and Observing that User's Role Assignments + +When a role is granted to a user (by updating the users `roles` property), any assignments that are referenced by the role are automatically referenced in the user's `assignments` property. + +In this section, we will grant the Employee role we created previously to the user Barbara Jensen, who was created in the managed/user repository during the reconciliation from OpenDJ. + +==== + +. Before you can update Barbara Jensen's entry, determine the identifier of her entry by querying her username, `bjensen`, and requesting only her `_id` field: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/userName+eq+'bjensen'&_fields=_id" + +{ + "result" : [ { + "_id" : "2c7daf46-d3ce-4bc5-9790-b44113bca8e7", + "_rev" : "1" + } ], + ... +} +---- ++ +From the output, you can see that bjensen's `_id` is `2c7daf46-d3ce-4bc5-9790-b44113bca8e7`. (This unique ID will obviously be different in your command output.) + +. Update bjensen's entry by adding a reference to the ID of the Employee role as a value of her `roles` attribute: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-type: application/json" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : { "_ref": "managed/role/2902afd5-155a-49c0-9dd9-7e6bfcf1708a" } + } + ]' \ + "http://localhost:8080/openidm/managed/user/2c7daf46-d3ce-4bc5-9790-b44113bca8e7" +{ + "_id": "2c7daf46-d3ce-4bc5-9790-b44113bca8e7", + "_rev": "4", + "displayName": "Barbara Jensen", + "description": "Created for OpenIDM", + "givenName": "Barbara", + "mail": "bjensen@example.com", + "telephoneNumber": "1-360-229-7105", + "sn": "Jensen", + "userName": "bjensen", + "accountStatus": "active", + "effectiveRoles": [ + { + "_ref": "managed/role/2902afd5-155a-49c0-9dd9-7e6bfcf1708a" + } + ], + "effectiveAssignments": [ + { + "name": "Employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ], + "_id": "f2830b80-6ab8-416b-b219-d3bf2efd0ed3", + "_rev": "1" + } + ] +} +---- ++ + +[TIP] +====== +Assign the role to bjensen by using the Admin UI: + +.. Select Manage > User, and click on bjensen's entry. + +.. On the Provisioning Roles tab, click Add Provisioning Roles. + +.. Select the Employee role and click Add. + +====== + +. Take a closer look at bjensen's entry, specifically at her roles, effective roles and effective assignments: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/userName+eq+'bjensen'&_fields=_id,userName,roles,effectiveRoles,effectiveAssignments" +{ + "result": [ + { + "_id": "2c7daf46-d3ce-4bc5-9790-b44113bca8e7", + "_rev": "4", + "userName": "bjensen", + "roles": [ + { + "_ref": "managed/role/2902afd5-155a-49c0-9dd9-7e6bfcf1708a", + "_refProperties": { + "_id": "b1c29213-e726-466a-9051-e9bb4e593331", + "temporalConstraints": [], + "_grantType": "", + "_rev": "2" + } + } + ], + "effectiveRoles": [ + { + "_ref": "managed/role/2902afd5-155a-49c0-9dd9-7e6bfcf1708a" + } + ], + "effectiveAssignments": [ + { + "name": "Employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ], + "_id": "f2830b80-6ab8-416b-b219-d3bf2efd0ed3", + "_rev": "1" + } + ] + } + ], + ... +} +---- ++ +Note that bjensen now has the calculated property `effectiveAssignments`, which includes the set of assignments that pertains to any user with the Employee role. Currently the assignment lists the `employeeType` attribute. ++ +In the next section, you will see how the assignment is used to set the value of the `employeeType` attribute in the LDAP server. + +==== + + +[#provrole-propagate-assignments] +===== Propagating Assignments to an External System + +This section provides a number of steps that show how effective assignments are propagated out to the external systems associated with their mappings. + +==== + +. Verify that bjensen's `employeeType` has been set correctly in the OpenDJ. ++ +Because implicit synchronization is enabled by default in OpenIDM, any changes made to a managed user object are pushed out to all the external systems for which mappings are configured. ++ +So, because bjensen has an effective assignment that sets an attribute in her LDAP entry, you should immediately see the resulting change in her LDAP entry. ++ +To verify that her entry has changed, run an `ldapsearch` on her entry and check the value of her `employeeType` attribute. ++ + +[NOTE] +====== +This command assumes that you are using the `ldapsearch` provided with OpenDJ (`opendj/bin/ldapsearch`). +====== ++ + +[source, console] +---- +$ ldapsearch \ + --port 1389 \ + --hostname localhost \ + --baseDN "dc=example,dc=com" \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --searchScope sub \ + "(uid=bjensen)" dn uid employeeType +dn: uid=bjensen,ou=People,dc=example,dc=com +uid: bjensen +employeeType: Employee +---- ++ +Note that bjensen's `employeeType` attribute is correctly set to `Employee`. ++ + +[TIP] +====== +You can also check bjensen's LDAP entry by using the OpenDJ Control Panel (`opendj/bin/control-panel`): + +image::images/dj-control-panel.png[] +====== + +. To observe how a managed user's roles can be used to provision group membership in an external directory, we add the groups that an Employee and a Contractor should have in the corporate directory (OpenDJ) as assignment attributes of the respective roles. ++ +First, look at the current `assignments` of the Employee role again: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role/2902afd5-155a-49c0-9dd9-7e6bfcf1708a?_fields=assignments,name" +{ + "_id": "2902afd5-155a-49c0-9dd9-7e6bfcf1708a", + "_rev": "2", + "assignments": [ + { + "_ref": "managed/assignment/f2830b80-6ab8-416b-b219-d3bf2efd0ed3", + "_refProperties": { + "_id": "c0005ecb-9dda-4db1-8660-a723b8237f16", + "temporalConstraints": [], + "_grantType": "", + "_rev": "1" + } + } + ], + "name": "Employee" +} +---- ++ +To update the `groups` attribute in bjensen's LDAP entry, you do not need to create a __new__ assignment. You simply need to add the attribute for LDAP groups to the assignment with ID `f2830b80-6ab8-416b-b219-d3bf2efd0ed3`: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/attributes/-", + "value" : { + "name": "ldapGroups", + "value": [ + "cn=Employees,ou=Groups,dc=example,dc=com", + "cn=Chat Users,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + } + } + ]' \ + "http://localhost:8080/openidm/managed/assignment/f2830b80-6ab8-416b-b219-d3bf2efd0ed3" + +{ + "_id": "f2830b80-6ab8-416b-b219-d3bf2efd0ed3", + "_rev": "2", + "name": "Employee", + "description": "Assignment for employees.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "employeeType", + "value": "Employee", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + }, + { + "name": "ldapGroups", + "value": [ + "cn=Employees,ou=Groups,dc=example,dc=com", + "cn=Chat Users,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ] +} +---- ++ +So, the Employee assignment now sets two attributes on the LDAP system - the `employeeType` attribute, and the `ldapGroups` attribute. ++ + +[TIP] +====== +To add more attributes to the Employee assignment in the Admin UI: + +.. Select Manage > Assignment, and click on the Employee assignment. + +.. On the Attributes tab, select Add an attribute and select the ldapGroups attribute. + +.. Enter the values ++ + +[source] +---- +cn=Employees,ou=Groups,dc=example,dc=com +---- ++ +and ++ + +[source] +---- +cn=Chat Users,ou=Groups,dc=example,dc=com +---- ++ +and click Save. + +====== + +. With the implicit synchronization between the managed user repository and OpenDJ, bjensen should now be a member of the `cn=Employees` and `cn=Chat Users` groups in LDAP. ++ +You can verify this with the following `ldapsearch` command. This command returns bjensen's group membership, in her `isMemberOf` attribute. ++ + +[source, console] +---- +$ ldapsearch \ + --port 1389 \ + --hostname localhost \ + --baseDN "dc=example,dc=com" \ + --bindDN "cn=Directory Manager" \ + --bindPassword password \ + --searchScope sub \ + "(uid=bjensen)" dn uid employeeType isMemberOf + +dn: uid=bjensen,ou=People,dc=example,dc=com +uid: bjensen +employeeType: Employee +isMemberOf: cn=openidm2,ou=Groups,dc=example,dc=com +isMemberOf: cn=Chat Users,ou=Groups,dc=example,dc=com +isMemberOf: cn=Employees,ou=Groups,dc=example,dc=com +---- ++ +You can also check bjensen's group membership by using the OpenDJ Control Panel (as shown previously), or by querying her object in the LDAP system, over the REST interface: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryFilter=/uid+sw+'bjensen'&_fields=dn,uid,employeeType,ldapGroups" +{ + "result" : [ { + "_id" : "uid=bjensen,ou=People,dc=example,dc=com", + "dn" : "uid=bjensen,ou=People,dc=example,dc=com", + "uid" : "bjensen", + "employeeType" : "Employee", + "ldapGroups" : [ + "cn=openidm2,ou=Groups,dc=example,dc=com", + "cn=Employees,ou=Groups,dc=example,dc=com", + "cn=Chat Users,ou=Groups,dc=example,dc=com" + ] + } ], + ... +} +---- ++ +In the original LDIF file, bjensen was already a member of the openidm2 group. You can ignore this group for the purposes of this sample. ++ + +[TIP] +====== +Use the Admin UI to see bjensen's LDAP groups as follows: + +.. Select Manage > User, and select bjensen's entry. + +.. On the Linked Systems tab, scroll down to the ldapGroups item. + +====== + +. Now, create a new assignment that will apply to Contract employees, and add that assignment to the Contractor role. ++ +Create the Contractor assignment with the following command. This assignment sets the value of the `employeeType` attribute to `Contractor`, and updates the user's `ldapGroups` attribute to include the `cn=Contractors` group: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + --data '{ + "name" : "Contractor", + "description": "Contractor assignment for contract workers.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "ldapGroups", + "value": [ + "cn=Contractors,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + }, + { + "name": "employeeType", + "value": "Contractor", + "assignmentOperation" : "mergeWithTarget", + "unassignmentOperation" : "removeFromTarget" + } + ] + }' \ + "http://localhost:8080/openidm/managed/assignment?_action=create" +{ + "_id": "7536e234-1268-482d-8459-24c8ef832def", + "_rev": "1", + "name": "Contractor", + "description": "Contractor assignment for contract workers.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "ldapGroups", + "value": [ + "cn=Contractors,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + }, + { + "name": "employeeType", + "value": "Contractor", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ] +} +---- ++ +Note the ID of the Contractor assignment (7536e234-1268-482d-8459-24c8ef832def in this example). ++ + +[TIP] +====== +Create the assignment by using the Admin UI, as described in xref:#provrole-add-assignments["Adding Assignments to a Role Definition"]). +====== + +. Now, add the Contractor assignment to the Contractor role (ID e7f649ad-8013-4673-a52a-bdcac7483111 in this example): ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/assignments/-", + "value" : { + "_ref" : "managed/assignment/7536e234-1268-482d-8459-24c8ef832def" + } + } + ]' \ + "http://localhost:8080/openidm/managed/role/e7f649ad-8013-4673-a52a-bdcac7483111" +{ + "_id": "e7f649ad-8013-4673-a52a-bdcac7483111", + "_rev": "2", + "name": "Contractor", + "description": "Role granted to contract workers." +} +---- ++ + +[TIP] +====== +Add the Contractor assignment to the Contractor role in the Admin UI, as follows: + +.. Select Manage > Role, and select the Contractor role. + +.. On the Managed Assignments tab, click Add Managed Assignment. + +.. Select the Contractor assignment from the dropdown list, and click Add. + +====== + +. Next, we need to grant the Contractor role to user jdoe. Before we can patch jdoe's entry, we need to know his system-generated ID. To obtain the ID, query jdoe's entry as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/userName+eq+'jdoe'&_fields=_id" + +{ + "result": [ + { + "_id": "92680be0-82f9-4297-9e00-c35c7cf700d2", + "_rev": "2" + } + ], + ... +} +---- ++ +From the output, you can see that jdoe's `_id` is `92680be0-82f9-4297-9e00-c35c7cf700d2`. (This unique ID will obviously be different in your command output.) + +. Update jdoe's entry by adding a reference to the ID of the Contractor role (e7f649ad-8013-4673-a52a-bdcac7483111) as a value of his `roles` attribute: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : { + "_ref": "managed/role/e7f649ad-8013-4673-a52a-bdcac7483111" + } + } + ]' \ + "http://localhost:8080/openidm/managed/user/92680be0-82f9-4297-9e00-c35c7cf700d2" +{ + "_id": "92680be0-82f9-4297-9e00-c35c7cf700d2", + "_rev": "4", + "displayName": "John Doe", + "description": "Created for OpenIDM", + "givenName": "John", + "mail": "jdoe@example.com", + "telephoneNumber": "1-415-599-1100", + "sn": "Doe", + "userName": "jdoe", + "accountStatus": "active", + "effectiveRoles": [ + { + "_ref": "managed/role/e7f649ad-8013-4673-a52a-bdcac7483111" + } + ], + "effectiveAssignments": [ + { + "name": "Contractor", + "description": "Contractor assignment for contract workers.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "ldapGroups", + "value": [ + "cn=Contractors,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + }, + { + "name": "employeeType", + "value": "Contractor", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ], + "_id": "7536e234-1268-482d-8459-24c8ef832def", + "_rev": "1" + } + ] +} +---- ++ + +[TIP] +====== +Grant the Contractor role to jdoe by using the Admin UI, as follows: + +.. Select Manage > User, and click on jdoe's entry. + +.. On the Provisioning Roles tab, click Add Provisioning Roles. + +.. Select the Contractor role and click Add. + +.. Click Save. + +====== + +. Check jdoe's entry on the LDAP system. ++ +With the implicit synchronization between the managed user repository and OpenDJ, jdoe should now be a member of the `cn=Contractors` group in LDAP. In addition, his `employeeType` should have been set to `Contractor`. ++ +You can verify this with the following REST query. This command returns jdoes's group membership, in his `isMemberOf` attribute, and his `employeeType`: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryFilter=/uid+sw+'jdoe'&_fields=dn,uid,employeeType,ldapGroups" +{ + "result": [ + { + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "givenName": "John", + "ldapGroups": [ + "cn=openidm,ou=Groups,dc=example,dc=com", + "cn=Contractors,ou=Groups,dc=example,dc=com" + ], + "mail": "jdoe@example.com", + "employeeType": "Contractor", + "uid": "jdoe", + "telephoneNumber": "1-415-599-1100", + "sn": "Doe", + "disabled": null, + "cn": "John Doe", + "description": "Created for OpenIDM", + "dn": "uid=jdoe,ou=People,dc=example,dc=com" + } + ], + ... +} +---- ++ + +[TIP] +====== +Use the Admin UI to see jdoe's LDAP groups as follows: + +.. Select Manage > User, and select jdoe's entry. + +.. On the Linked Systems tab, scroll down to the ldapGroups item. + +====== + +==== + + +[#provrole-remove-role] +===== Removing a Role Grant From a User and Observing That User's Role Assignments + +In this section, you will remove the Contractor role from jdoe's managed user entry and observe the subsequent change to jdoe's managed assignments, and to the corresponding attributes in OpenDJ. + +==== + +. Before you change jdoe's roles, view his entry again to examine his current roles. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/userName+eq+'jdoe'&_fields=_id,roles" +{ + "result": [ + { + "_id": "92680be0-82f9-4297-9e00-c35c7cf700d2", + "_rev": "4", + "roles": [ + { + "_ref": "managed/role/e7f649ad-8013-4673-a52a-bdcac7483111", + "_refProperties": { + "_id": "093fc34b-0694-478e-952e-98d0a828b1ac", + "_rev": "2" + } + } + ] + } + ], + ... +} +---- ++ +Note that jdoe's ID is 92680be0-82f9-4297-9e00-c35c7cf700d2 and the ID of the __relationship__ that expresses the role grant is 093fc34b-0694-478e-952e-98d0a828b1ac. You will need these IDs in the next step. ++ + +[TIP] +====== +View jdoe's current roles in the Admin UI: + +.. Select Manage > User, and select jdoe's entry. + +.. The Provisioning Roles tab lists the current roles. + +====== + +. Remove the Contractor role from jdoe's entry by sending a DELETE request to the user entry, specifying the relationship ID: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/user/92680be0-82f9-4297-9e00-c35c7cf700d2/roles/093fc34b-0694-478e-952e-98d0a828b1ac" +{ + "_ref": "managed/role/e7f649ad-8013-4673-a52a-bdcac7483111", + "_refProperties": { + "_id": "093fc34b-0694-478e-952e-98d0a828b1ac", + "_rev": "2" + } +} +---- ++ + +[TIP] +====== +Use the Admin UI to remove the Contractor role from jdoe's entry as follows: + +.. Select Manage > User, and select jdoe's entry. + +.. On the Provisioning Roles tab, check the box next to the Contractor role and click Remove Selected Provisioning Roles. + +====== + +. Verify jdoe's `employeeType` and `ldapGroups`. ++ +The removal of the Contractor role causes a synchronization operation to be run on jdoe's entry. His `employeeType` and `ldapGroups` attributes in OpenDJ should be reset to what they were before he was granted the Contractor role. ++ +You can check jdoe's attributes by querying his object in the LDAP directory, over the REST interface: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryFilter=/uid+sw+'jdoe'&_fields=dn,uid,employeeType,ldapGroups" +{ + "result" : [ { + "sn" : "Doe", + "telephoneNumber" : "1-415-599-1100", + "employeeType" : null, + "dn" : "uid=jdoe,ou=People,dc=example,dc=com", + "cn" : "John Doe", + "uid" : "jdoe", + "ldapGroups" : [ "cn=openidm,ou=Groups,dc=example,dc=com" ], + "givenName" : "John", + "mail" : "jdoe@example.com", + "description" : "Created for OpenIDM", + "_id" : "uid=jdoe,ou=People,dc=example,dc=com" + } ], + ... +} +---- ++ + +[TIP] +====== +Use the Admin UI to see jdoe's LDAP groups as follows: + +.. Select Manage > User, and select jdoe's entry. + +.. On the Linked Systems tab, scroll down to the ldapGroups item. + +====== + +==== +This concludes the provisioning with roles sample. For more information about roles, assignments, and how to manipulate them, see xref:../integrators-guide/chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"] in the __Integrator's Guide__. + + + + +[#sample-roles-temporal] +=== Temporal Constraints Sample - Applying Time-Based Constraints to Roles + +To restrict the period during which a role is effective, you can set a __temporal constraint__ on the role itself, or on the role grant. A temporal constraint that is set on a role definition applies to all grants of that role. A temporal constraint that is set on a role grant enables you to specify the period that the role is valid per user. For more information about temporal constraints, see xref:../integrators-guide/chap-users-groups-roles.adoc#roles-temporal-constraints["Using Temporal Constraints to Restrict Effective Roles"] in the __Integrator's Guide__. + +This sample builds on what you learned in the previous two roles samples. The sample demonstrates how to add a temporal constraint to the Contractor role to restrict the period that it is effective. The previous sample ended with jdoe's contract ending because he finished up the work that was required for the company. The company now has additional work that requires jdoe's expertise and has rehired him as a contractor. This time however, the company requires that jdoe's contractor role be terminated as soon as his contract expires. + +[#temporal-prerequisites] +==== Before You Start + +This sample assumes that you have already run through the previous two roles samples, and have installed and configured OpenDJ as for Sample 2 (see xref:chap-ldap-samples.adoc#external-ldap-config-2["LDAP Server Configuration"]). The LDAP user must have write access to create users from OpenIDM on the LDAP server. When you set up the LDAP server, import the LDIF file for this sample (`openidm/samples/roles/temporalConstraints/data/Example.ldif).` + +==== + +. Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the temporal constraints sample. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ startup.sh -p samples/roles/temporalConstraints +---- + +. Create the Employee and Contractor roles, and their assignments, either by using the Admin UI, or by using the REST interface, as described in the previous sample ++ +Note the IDs of the roles and assignments because you will use them in the commands that follow. + +. Reconcile the repository, as described in the previous sample. + +==== +The sample is now ready to demonstrate temporal constraints on roles. + + +[#run-sample-roles-temporal] +==== Run the Sample + +This section assumes that you have reconciled the managed user repository to populate it with the users from the LDAP server, and that you have created the Employee and Contractor roles and their assignments. +This part of the sample demonstrates the following features of the OpenIDM roles implementation: + +* xref:#add-temporal-constraint["Adding a Temporal Constraint to a Role Definition"] + +* xref:#grant-temporal-role["Granting a Role With a Temporal Constraint to a User"] + +* xref:#observe-temporal-role-effects["Observing the Effects of a Temporal Role"] + +* xref:#remove-temporal-role["Removing the Role Grant From the User"] + + +[#add-temporal-constraint] +===== Adding a Temporal Constraint to a Role Definition + +In this section, you will update the definition of the Contractor role to add a temporal constraint. + +==== + +. If you are running the sample directly over the REST interface, obtain the ID of the Contractor role: ++ +You can skip this step if you are using the Admin UI. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/role?_queryFilter=true&_prettyPrint=true" +{ + "result" : [ { + "_id" : "1a4c9047-1ce3-4f39-8901-e4f60176330e", + "_rev" : "1", + "name" : "Employee", + "description" : "Role granted to workers on the payroll." + }, { + "_id" : "06128fc1-b89b-4fe8-b9b3-4de30fd17b9e", + "_rev" : "1", + "name" : "Contractor", + "description" : "Role granted to contract workers." + } ], + ... +} +---- ++ +In this example, the ID of the Contractor role is 06128fc1-b89b-4fe8-b9b3-4de30fd17b9e. + +. Patch the contractor role with a temporal constraint by adding a start and end date to the role definition. ++ +For the start date, use the current time plus 5 minutes. This will demonstrate that you can add a future temporal constraint on a user before their contract even starts. For the end date, use 10 minutes from the current time. ++ +In this example, the current time is 15:30:00. Adding 5 minutes results in a start time of 15:35:00. Adding 10 minutes results in an end time of 15:40:00. Adjust these values for the current time that you run the sample. ++ +To add the temporal constraint over REST: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/temporalConstraints", + "value" : [{"duration": "2016-05-05T15:35:00.000Z/2016-05-05T15:40:00.000Z"}] + } + ]' \ + "http://localhost:8080/openidm/managed/role/06128fc1-b89b-4fe8-b9b3-4de30fd17b9e" +{ + "_id": "06128fc1-b89b-4fe8-b9b3-4de30fd17b9e", + "_rev": "2", + "name": "Contractor", + "description": "Role granted to contract workers.", + "temporalConstraints": [ + { + "duration": "2016-05-05T15:35:00.000Z\/2016-05-05T15:40:00.000Z" + } + ] +} +---- ++ +To add the temporal constraint by using the Admin UI: + +.. Select Manage > Role and click the Contractor role. + +.. Select Temporal Constraint, then select a time zone, start date, and end date. + +.. Click Save. + + +==== + + +[#grant-temporal-role] +===== Granting a Role With a Temporal Constraint to a User + +In this section, you will grant the Contractor role to jdoe. + +==== + +. If you are running this sample over REST, determine the identifier of jdoe's entry querying the existing managed users. ++ +If you are using the Admin UI you can skip this step. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=true" +{ + "result" : [ { + ... + }, { + "_id" : "0c470c71-bb4e-4cf1-bc9d-77d7b35b180b", + "_rev" : "2", + "displayName" : "John Doe", + "description" : "Created for OpenIDM", + "givenName" : "John", + "mail" : "jdoe@example.com", + "telephoneNumber" : "1-415-599-1100", + "sn" : "Doe", + "userName" : "jdoe", + "accountStatus" : "active", + "effectiveRoles" : [ ], + "effectiveAssignments" : [ ] + } ], + ... +} +---- ++ +Note that jdoe's ID is 0c470c71-bb4e-4cf1-bc9d-77d7b35b180b. + +. Update jdoe's entry to grant him the Contractor role: ++ + +[source, console] +---- +$ curl \ + --header "Content-type: application/json" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request PATCH \ + --data '[ + { + "operation" : "add", + "field" : "/roles/-", + "value" : { + "_ref": "managed/role/06128fc1-b89b-4fe8-b9b3-4de30fd17b9e" + } + } + ]' \ + "http://localhost:8080/openidm/managed/user/0c470c71-bb4e-4cf1-bc9d-77d7b35b180b" +{ + "_id": "0c470c71-bb4e-4cf1-bc9d-77d7b35b180b", + "_rev": "4", + "displayName": "John Doe", + "description": "Created for OpenIDM", + "givenName": "John", + "mail": "jdoe@example.com", + "telephoneNumber": "1-415-599-1100", + "sn": "Doe", + "userName": "jdoe", + "accountStatus": "active", + "effectiveRoles": [ + + ], + "effectiveAssignments": [ + + ] +} +---- ++ +Note that, at this stage, jdoe does not have any effective roles or assignments because his contract has not yet started. Check jdoe's entry again between the start and end date of the temporal constraint to observe the assignment. ++ +To grant the Contractor role to jdoe by using the Admin UI: + +.. Select Manage > User and click jdoe's entry. + +.. On the Provisioning Roles tab, click Add Provisioning Roles. + +.. Select the Contractor role and click Add. + + +==== + + +[#observe-temporal-role-effects] +===== Observing the Effects of a Temporal Role + +During the contract period, query jdoe again to see when the Contractor role and assignments are present in his list of effective roles and effective assignments. + +==== + +. When the start time that you set has been reached, query jdoe's entry as follows: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/userName+sw+"jdoe"&_fields=roles,effectiveRoles,effectiveAssignments" +{ + "result": [ + { + "_id": "0c470c71-bb4e-4cf1-bc9d-77d7b35b180b", + "_rev": "6", + "roles": [ + { + "_ref": "managed/role/06128fc1-b89b-4fe8-b9b3-4de30fd17b9e", + "_refProperties": { + "_id": "6774696d-999e-4483-87a9-02f501752b29", + "_rev": "4" + } + } + ], + "effectiveRoles": [ + { + "_ref": "managed/role/06128fc1-b89b-4fe8-b9b3-4de30fd17b9e" + } + ], + "effectiveAssignments": [ + { + "name": "Contractor", + "description": "Contractor assignment for contract workers.", + "mapping": "managedUser_systemLdapAccounts", + "attributes": [ + { + "name": "ldapGroups", + "value": [ + "cn=Contractors,ou=Groups,dc=example,dc=com" + ], + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + }, + { + "name": "employeeType", + "value": "Contractor", + "assignmentOperation": "mergeWithTarget", + "unassignmentOperation": "removeFromTarget" + } + ], + "_id": "3e61a1db-202d-4761-aeb7-b617d3376a79", + "_rev": "1" + } + ] + } + ], + ... +} +---- ++ +There is not really a comparable way to perform this step in the UI. + +. Between the start and end times, look at jdoe's entry in the LDAP server. At this point, the Contractor group should be effective for jdoe's entry, so his LDAP groups should include the Contractors group: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryFilter=/uid+sw+"jdoe"&_fields=dn,uid,employeeType,ldapGroups" +{ + "result": [ + { + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "dn": "uid=jdoe,ou=People,dc=example,dc=com", + "uid": "jdoe", + "employeeType": "Contractor", + "ldapGroups": [ + "cn=openidm,ou=Groups,dc=example,dc=com", + "cn=Contractors,ou=Groups,dc=example,dc=com" + ] + } + ] +} +---- ++ +To look at jdoe's LDAP groups in the Admin UI: + +.. Select Manage > User, and select jdoe's entry. + +.. On the Linked Systems tab, scroll down to the ldapGroups item. + + +. Jdoe's contract expires when the current date is greater than the end date of the temporal constraint. ++ +Query jdoe's entry at that point to see that he no longer has the Contractor roles and assignments in his effective roles and assignments lists (although the role is still there in his roles list, it is no longer effective). ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=/userName+sw+"jdoe"&_fields=roles,effectiveRoles,effectiveAssignments" +{ + "result": [ + { + "_id": "0c470c71-bb4e-4cf1-bc9d-77d7b35b180b", + "_rev": "8", + "roles": [ + { + "_ref": "managed/role/06128fc1-b89b-4fe8-b9b3-4de30fd17b9e", + "_refProperties": { + "_id": "6774696d-999e-4483-87a9-02f501752b29", + "_rev": "6" + } + } + ], + "effectiveRoles": [ + + ], + "effectiveAssignments": [ + + ] + } + ], + ... +} +---- ++ +There is not really comparable way to view this information in the Admin UI. Take note of the ID of the __relationship__ that expresses the role grant (6774696d-999e-4483-87a9-02f501752b29 in this example). You will need this ID in the next step. + +. Now verify that jdoe is no longer part of the Contractors group in OpenDJ: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/system/ldap/account?_queryFilter=/uid+sw+"jdoe"&_fields=dn,uid,employeeType,ldapGroups" +{ + "result": [ + { + "_id": "uid=jdoe,ou=People,dc=example,dc=com", + "dn": "uid=jdoe,ou=People,dc=example,dc=com", + "uid": "jdoe", + "employeeType": null, + "ldapGroups": [ + "cn=openidm,ou=Groups,dc=example,dc=com" + ] + } + ], + ... +} +---- ++ +In the Admin UI: + +.. Select Manage > User, and select jdoe's entry. + +.. On the Linked Systems tab, scroll down to the ldapGroups item. + + +==== + + +[#remove-temporal-role] +===== Removing the Role Grant From the User + +To finish up this sample, remove the Contractor role from jdoe by sending a DELETE request to his managed user entry, specifying the relationship ID (6774696d-999e-4483-87a9-02f501752b29 in this example): + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request DELETE \ + "http://localhost:8080/openidm/managed/user/0c470c71-bb4e-4cf1-bc9d-77d7b35b180b/roles/6774696d-999e-4483-87a9-02f501752b29" +{ + "_ref": "managed/role/06128fc1-b89b-4fe8-b9b3-4de30fd17b9e", + "_refProperties": { + "_id": "6774696d-999e-4483-87a9-02f501752b29", + "_rev": "6" + } +} +---- +Alternatively, use the Admin UI to remove the Contractor role from jdoe's entry as follows: + +. Select Manage > User, and select jdoe's entry. + +. On the Provisioning Roles tab, check the box next to the Contractor role and click Remove Selected Provisioning Roles. + + + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-salesforce-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-salesforce-sample.adoc new file mode 100644 index 000000000..d5a170be1 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-salesforce-sample.adoc @@ -0,0 +1,508 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-salesforce-sample] +== Salesforce Sample - Salesforce With the Salesforce Connector + +OpenIDM 4.5 provides a Salesforce Connector that enables provisioning, reconciliation, and synchronization with a Salesforce organization. The Salesforce Connector is not an OpenICF connector, but a separate OpenIDM module, based on the ForgeRock Common Resource API. + +[NOTE] +==== +The Salesforce Connector, and this corresponding sample, are provided only with the OpenIDM Enterprise build, available on the link:https://backstage.forgerock.com[ForgeRock Backstage, window=\_blank] site. +==== +This sample demonstrates the creation and update of users from OpenIDM to Salesforce, and from Salesforce to OpenIDM. You can use either the Admin UI, or the command line to run this sample. Both methods are outlined in the sections that follow. + +[#salesforce-setup] +=== Before you Start + +This sample requires that you have a Salesforce account, and a Connected App with OAuth enabled. For more information about Connected Apps, see the link:http://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_overview.htm[Connected Apps Overview, window=\_top] in the Salesforce documentation. + +==== +To set up a Connected App for OpenIDM, follow these steps: + +. Log in to link:http://salesforce.com[salesforce.com, window=\_top] with your Salesforce credentials. + +. Click __Setup__ in the top right corner. + +. In the left hand menu, under __Build__, expand the __Create__ item and click __Apps__. + +. On the right hand panel, scroll down to __Connected Apps__ and click __New__. + +. In the __New Connected App__ panel, enter the following Basic Information: ++ + +* __Connected App Name__. Enter a name that you will recognize as the OpenIDM App, for example, `OpenIDM`. + +* __API Name__. This field defaults to the Connected App Name, but you can change it. Note that the Application API Name can only contain underscores and alphanumeric characters. The name must be unique, begin with a letter, not include spaces, not end with an underscore, and not contain two consecutive underscores. + +* __Contact Email__. Enter the email address of the person responsible for this Connected App within your organization. + + +. Select __Enable OAuth Settings__ and enter the following information: ++ + +* __Callback URL__. Enter the OpenIDM URL, to which the requested OAuth token will be sent, for example `\https://localhost:8443/admin/oauth.html`. + +* __Selected OAuth Scopes__. Click the __Add__ button to add the following __Available Auth Scopes__ to the __Selected OAuth Scopes__ column: ++ + +** Access and manage your data + +** Access your basic information + +** Perform requests on your behalf at any time + ++ + +[#new-app-data] +image::images/new-app-data.png[] ++ +You can leave the remaining fields blank. + + +. Click __Save__ to create the new Connected App. + +. The next window displays your new Connected App. ++ +Under the __API (Enable OAuth Settings)__ item, the Consumer Key and a link to the Consumer Secret are displayed. ++ +Click the link to reveal the Consumer Secret. ++ + +[#consumer-key] +image::images/consumer-key.png[] ++ +OpenIDM requires the Consumer Key and Secret to obtain an access token and a refresh token for access to salesforce.com. ++ +Copy and paste both the key and the secret into a file for use when you set up the sample. + +. To demonstrate the reconciliation of users from Salesforce to OpenIDM, your Salesforce organization should contain at least a few users. Add these users now if your Salesforce organization is empty. + +==== + + +[#install-sample-salesforce] +=== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for the Salesforce sample. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/salesforce-connector +---- + + +[#salesforce-sample-ui] +=== Running the Sample by Using the Admin UI + +The Admin UI is the recommended way to test this sample. + +==== + +. Log in to the Admin UI at the URL `\https://localhost:8443/admin` as the default administrative user (`openidm-admin`) with password `openidm-admin`. ++ + +[WARNING] +====== +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +====== ++ +The Resources tab shows the Salesforce connector, which is currently disabled. ++ + +image::images/salesforce-connector.png[] + +. Enable the Salesforce connector by completing the authentication details as follows. You will need the Consumer Key and Consumer Secret that you obtained in the previous section. ++ + +* Click on the Salesforce connector and click Edit to open the Edit Connector dialog. + +* Select True from the Enabled list. + +* Select Salesforce Connector from the Connector Type list. + +* Under Basic Connector Details select the Sandbox URL (for the purposes of testing the sample) and enter the Connector Key and Secret that you obtained in the previous section. + +* You can leave the default LiveSync details for now. Click Update to update the connector configuration. ++ + +image::images/edit-sf-connector.png[] + +* On the permission request screen click Allow, to enable OpenIDM to access your Salesforce Connected App. ++ + +[NOTE] +====== +In the current OpenIDM release, an issue is occasionally seen where the system appears to time out while retrieving the refresh token from Salesforce, at this stage. If this happens, refresh your browser and attempt the connector setup again. +====== ++ +On the Resources tab, your Salesforce Connector should now be Active. ++ + +image::images/active-sf-connector.png[] + + +. To test the reconciliation process, select the Mappings tab. ++ +This tab shows two configured mappings, one from Salesforce to the OpenIDM repository (`managed/user`) and one from the OpenIDM repository to Salesforce. ++ + +image::images/salesforce-mappings.png[] + +. Click anywhere on the first mapping and click Reconcile Now. ++ + +image::images/salesforce-reconcile.png[] ++ +The reconciliation operation creates the users that were present in your Salesforce organization in the OpenIDM repository. + +. Retrieve the users in the repository. In the upper-right of the screen, click the `openidm-admin` link. In the pop-up menu that appears, click the Data Management View link. ++ +This link opens the Self-Service UI. If you did not change your password in the first step, you are prompted to change your password again. You can bypass this by clicking X to close the password prompt window. + +. Select the Users tab. ++ + +image::images/salesforce-users.png[] ++ +The users from the Salesforce organization have been reconciled to the OpenIDM repository. If the reconciliation was successful, the list of users displayed here should reflect what was in your Salesforce organization. + +. To retrieve the details of a specific user, click that username on the Users tab. ++ +The following image shows the details of user `bjensen`. Scroll down. Note the Linked Systems panel that shows the corresponding user record in Salesforce. ++ + +image::images/salesforce-bjensen.png[] + +. To test the second mapping (from OpenIDM to Salesforce), update any user in the OpenIDM repository. For example, update Babs Jensen's username. + +. By default, __implicit synchronization__ is enabled for mappings __from__ the `managed/user` repository __to__ any external resource. This means that when you update a managed object, any mappings defined in the `sync.json` file that have the managed object as the source are automatically executed to update the target system. For more information, see xref:../integrators-guide/chap-synchronization.adoc#synchronization-mappings-file["Mapping Source Objects to Target Objects"] in the __Integrator's Guide__. ++ +To test that the implicit synchronization has been successful, look at Babs Jensen's record in the Self-Service UI. At the bottom of the user profile, the Linked Systems panel indicates Babs Jensen's record in the Salesforce data store. Note the changed Username. ++ +Alternatively, check the updated user record in Salesforce. + +==== + + +[#salesforce-sample-cli] +=== Running the Sample by Using the Command Line + +Running the sample by using the command line is a little more complex. This section breaks the sample into two tasks - configuring the connector, and then testing the configuration by running reconciliation operations between the two systems. + +[#d5830e11404] +.To Set Up the Salesforce Connector +==== +Before you start, you will need the Consumer Key and Consumer Secret that you obtained in the previous section. + +. Obtain the refresh token from salesforce.com by pointing your browser to the following URL. Substitute your Consumer Key for `CLIENT_ID`. If OpenIDM is not running on the localhost, substitute the appropriate hostname and port number in the value of the `redirect_uri` parameter. ++ +link:https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https://localhost:8443/admin/oauth.html&scope=id+api+refresh_token[https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https://localhost:8443/admin/oauth.html&scope=id+api+refresh_token, window=\_blank] + +. You are redirected to Salesforce, and prompted to give this application access to your Salesforce account. When you have given consent, you should receive a response URL that looks similar to the following: ++ + +[source, console] +---- +https://localhost:8443/admin/index.html#connectors/edit//&code=aPrxJZTK7Rs03PU634VK8Jn9o_U3ZY1ERxM7IiklF... +---- ++ +The `&code` part of this URL is an authorization code, that you need for the following step. ++ + +[CAUTION] +====== +Note that this authorization code expires after 10 minutes. If you do not complete the OAuth flow within that time, you will need to start this process again. +====== + +. Copy the authorization code from the response URL and use it as the value of the `"code"` parameter in the following REST call. You will also need to supply your Consumer Key and Consumer Secret in this call. ++ + +[source, console] +---- +$ curl \ + --verbose \ + --data "grant_type=authorization_code" \ + --data "client_id=consumer-key" \ + --data "client_secret=consumer-secret" \ + --data "redirect_uri=https://localhost:8443/admin/oauth.html" \ + --data "code=access-token-code" \ + "https://login.salesforce.com/services/oauth2/token" +{ + "access_token": "00DS0000003K4fU!AQMAQOzEU.8tCjg8Wk79yKPKCtrtaszX5jrHtoT4NBpJ8x2NFZGjg3PNuc0TWq0EgiGS_mVkfg5f4pVN5...", + "signature": "2uREX1lseXdg3Vng/2+Hrlo/KHOWYoim+poj74wKFtw=", + "refresh_token": "5Aep861KIwKdekr90I4iHdtDgWwRoG7O_6uHrgJ.yVtMS0UaGxRqE6WFM77W7wCV4muVMgdqKjuWI2i5S6sjN2X", + "token_type": "Bearer", + "instance_url": "https://example-com.cs1.my.salesforce.com", + "scope": "id api refresh_token", + "issued_at": "1417182949781", + "id": "https://login.salesforce.com/id/00DS0000003K4fUMAS/00530000009hWLcAAM" +} +---- ++ +The output includes an `access_token` and a `refresh_token`. You will need the `refresh_token` in the following step. + +. Edit the `configurationProperties` in your Salesforce connector configuration file (`openidm/samples/salesforce-connector/conf/provisioner.salesforce-salesforce.json`) to include your Consumer Key (`clientID`), Consumer Secret (`clientSecret`), and refresh token. ++ +In addition, set the `"instanceUrl"` to the value returned in the previous step, and set the `"enabled"` property to `true`. ++ +The relevant excerpts of the `provisioner.salesforce-salesforce.json` file are as follows: ++ + +[source, javascript] +---- +{ + "name" : "salesforce", + "enabled" : true, + "connectorRef" : { +... + "configurationProperties" : { + "connectTimeout" : 120000, + "loginUrl" : null, + "idleCheckInterval" : 10000, + "refreshToken" : "5Aep861KIwKdekr90I4iHdtDgWwRoG7O_6uHrgJ.yVtMS0UaGxRqE6WFM77W7wCV4muVMgdqKjuWI2i5S6sjN2X", + "clientSecret" : "4850xxxxxxxxxxxxx425", + "clientId" : "3MVG98dostKihXN7Is8Q0g5q1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPdB5f5ATwmaMuWxl", + "instanceUrl" : "https://example-com.cs1.my.salesforce.com", + "version" : 29 + } +... +---- + +. Check that your connector configuration is correct by testing the status of the connector, over REST. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/system?_action=test" +[ + { + "ok": true, + "connectorRef": { + "bundleVersion": "2.0.29.2", + "systemType": "provisioner.salesforce", + "displayName": "Salesforce Connector", + "bundleName": "org.forgerock.openidm.salesforce", + "connectorName": "org.forgerock.openidm.salesforce.Salesforce" + }, + "objectTypes": [ + "User", + "PermissionSet", + "PermissionSetAssignment", + "Profile", + "PermissionSetLicenseAssign", + "Organization", + "PermissionSetLicense", + "Group", + "GroupMember" + ], + "config": "config/provisioner.salesforce/salesforce", + "enabled": true, + "name": "salesforce" + } +] +---- + +==== + +[#d5830e11519] +.Run Reconciliation by Using the Command Line +==== +The mapping configuration file (`sync.json`) for this sample includes two mappings, `sourceSalesforceUser_managedUser`, which synchronizes users from the Salesforce with the OpenIDM repository, and `managedUser_sourceSalesforceUser`, which synchronizes changes from the OpenIDM repository to Salesforce. + +. Reconcile the repository over the REST interface by running the following command: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=sourceSalesforceUser_managedUser&waitForCompletion=true" +{ + "state": "SUCCESS", + "_id": "8a6281ef-6faf-43dd-af5c-3a842b38c468" +} +---- ++ +The reconciliation operation returns a reconciliation run ID and the status of the operation. Reconciliation creates user objects from LDAP in the OpenIDM repository, assigning the new objects random unique IDs. + +. View the recon entry over REST for an indication of the actions that were taken on the OpenIDM repository. ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "https://localhost:8443/openidm/recon/8a6281ef-6faf-43dd-af5c-3a842b38c468" +{ + "duration": 6447, + "ended": "2014-11-28T15:01:38.399Z", + "started": "2014-11-28T15:01:31.952Z", + "parameters": { + "null": false, + "boolean": false, + "number": false, + "list": false, + "object": { + "targetQuery": { + "_queryId": "query-all-ids", + "resourceName": "managed/user" + }, + "sourceQuery": { + "_queryId": "query-all-ids", + "resourceName": "system/salesforce/User" + } + }, + "pointer": { + "empty": true + }, + "transformers": [], + "set": false, + "map": true, + "string": false, + "collection": false, + "wrappedObject": { + "targetQuery": { + "resourceName": "managed/user", + "_queryId": "query-all-ids" + }, + "sourceQuery": { + "_queryId": "query-all-ids", + "resourceName": "system/salesforce/User" + } + } + }, + "_id": "8a6281ef-6faf-43dd-af5c-3a842b38c468", + "mapping": "sourceSalesforceUser_managedUser", + "state": "SUCCESS", + "stage": "COMPLETED_SUCCESS", + "stageDescription": "reconciliation completed.", + "progress": { + "links": { + "created": 8, + "existing": { + "total": "0", + "processed": 0 + } + }, + "target": { + "created": 8, + "existing": { + "total": "0", + "processed": 0 + } + }, + "source": { + "existing": { + "total": "9", + "processed": 9 + } + } + }, + "situationSummary": { + "FOUND_ALREADY_LINKED": 0, + "UNASSIGNED": 0, + "TARGET_IGNORED": 0, + "SOURCE_IGNORED": 0, + "MISSING": 0, + "FOUND": 0, + "AMBIGUOUS": 0, + "UNQUALIFIED": 0, + "CONFIRMED": 0, + "SOURCE_MISSING": 0, + "ABSENT": 9 + }, + "statusSummary": { + "SUCCESS": 8, + "FAILURE": 1 + } +} +---- ++ +The output shows that eight entries were created on the target (`managed/user`). + +. You can display those users by querying the IDs in the managed/user repository. ++ + +[source, console] +---- +$ 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" +{ + "remainingPagedResults": -1, + "pagedResultsCookie": null, + "resultCount": 8, + "result": [ + { + "_rev": "0", + "_id": "f15322f2-5873-4e5f-a4e5-2d4bc03dd190" + }, + { + "_rev": "0", + "_id": "85879c60-afa1-4425-8c7a-5cccbbaff587" + }, + { + "_rev": "0", + "_id": "ed3fe655-29a6-4016-b6bc-4b2356911fd1" + }, + { + "_rev": "0", + "_id": "34678464-c080-41b1-8da6-d5fde9d35aeb" + }, + { + "_rev": "0", + "_id": "02d5da29-8349-4f35-affc-5f6c331307ef" + }, + { + "_rev": "0", + "_id": "f91d6fce-bf27-4379-9411-fd626f8a9528" + }, + { + "_rev": "0", + "_id": "6ace9220-59e7-4d97-8683-e03362a9150c" + }, + { + "_rev": "0", + "_id": "56863eea-35d7-4aeb-a017-74ef28fd3116" + } + ] +---- + +==== + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-trustedfilter-sample.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-trustedfilter-sample.adoc new file mode 100644 index 000000000..7ffc88dcf --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-trustedfilter-sample.adoc @@ -0,0 +1,204 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-trustedfilter-sample] +== The Trusted Servlet Filter Sample + +This sample demonstrates how to use a custom servlet filter and the "Trusted Request Attribute Authentication Module" in OpenIDM. Once configured, OpenIDM can use the servlet filter to authenticate through another service. + +If you want to set up authentication through OpenAM, refer to xref:chap-fullstack-sample.adoc#chap-fullstack-sample["Full Stack Sample - Using OpenIDM in the ForgeRock Identity Platform"]. + +[#trustedfilter-before-you-start] +=== Before You Start + +Before you start this sample, complete the following steps: + +* Prepare a fresh installation of OpenIDM. (See xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"]). + +* Download and install the link:https://maven.apache.org/install.html[Apache Maven, window=\_blank] build tool. + +* Build the custom servlet filter bundle file: ++ + +[source, console] +---- +$ cd /path/to/openidm/samples/trustedservletfilter/filter +$ mvn clean install +---- + +* Copy the newly built servlet bundle file to the `openidm/bundle` directory: ++ + +[source, console] +---- +$ cp target/sample-trusted-servletfilter-1.0.jar /path/to/openidm/bundle +---- + + + +[#trustedservlet-bundle] +=== The Sample Servlet Filter + +You just built a bundle file from a Java file in the following `trustedservletfilter/filter` subdirectory: `src/main/java/org/forgerock/openidm/sample/trustedservletfilter`. The file is named `SampleTrustedServletFilter.java`. + +The following line looks for the `X-Special-Trusted-User` header, to identify a specific User ID as a "trusted" user. + +[source, java] +---- +final String specialHeader = ((HttpServletRequest) servletRequest).getHeader("X-Special-Trusted-User"); +---- +The next line sets the special Servlet attribute `X-ForgeRock-AuthenticationId` to this trusted User ID. + +[source, java] +---- +servletRequest.setAttribute("X-ForgeRock-AuthenticationId", specialHeader); +---- +The rest of the servlet filter chain continues request processing: + +[source, java] +---- +filterChain.doFilter(servletRequest, servletResponse); +---- +This sample includes a `servletfilter-trust.json` file that calls the compiled OpenIDM trusted servlet `filterClass`: + +[source, javascript] +---- +{ + "classPathURLs" : [ ], + "systemProperties" : { }, + "requestAttributes" : { }, + "scriptExtensions" : { }, + "initParams" : { }, + "urlPatterns" : [ + "/*" + ], + "filterClass" : "org.forgerock.openidm.sample.trustedservletfilter.SampleTrustedServletFilter" +} +---- + + +[#run-trustedfilter] +=== Run the Sample + +Start OpenIDM with the configuration for the trusted filter sample. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/trustedservletfilter +Executing ./startup.sh... +Using OPENIDM_HOME: /path/to/openidm +Using PROJECT_HOME: /path/to/openidm +Using OPENIDM_OPTS: -Xmx1024m -Xms1024m +Using LOGGING_CONFIG: -Djava.util.logging.config.file=/path/to/openidm/samples/trustedservletfilter/conf/logging.properties +Using boot properties at /path/to/openidm/samples/trustedservletfilter/conf/boot/boot.properties +-> OpenIDM ready +---- + + +[#trusted-create-user] +=== Create a Trusted User + +In this section, you will create a user, and then apply the special request header `X-Special-Trusted-User` to authenticate that user. + +Create user Barbara Jensen in OpenIDM, with `userName` `bjensen`: + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "Content-Type: application/json" \ + --request PUT \ + --data ' + { + "userName": "bjensen", + "telephoneNumber": "6669876987", + "givenName": "Barbara", + "sn": "Jensen", + "description": "Example User", + "mail": "bjensen@example.com", + "authzRoles" : [ + { + "_ref" : "repo/internal/role/openidm-authorized" + } + ] + }' \ + "https://localhost:8443/openidm/managed/user/bjensen" +---- +Now you can demonstrate the servlet filter by configuring it with the noted special header. Normally, a servlet filter used for authentication does not allow a client to masquerade as any user. This sample demonstrates a basic use of a servlet filter by establishing the authentication ID. + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-Special-Trusted-User: bjensen" \ + --request GET \ + "https://localhost:8443/openidm/info/login?_fields=authenticationId,authorization" +---- +The output should include a JSON structure with the user's authentication and authorization details. In this case, user `bjensen` is authenticated with the "openidm-authorized" role. + +[source, console] +---- +{ + "_id" : "", + "authenticationId" : "bjensen", + "authorization" : { + "id" : "bjensen", + "component" : "managed/user", + "roles" : [ "openidm-authorized" ] + } +} +---- + + +[#external-trustedfilter-servlet] +=== Customizing the Sample for an External System + +To customize this sample for an external authentication/authorization system, you need a servlet filter which authenticates against that external system. You may use a third-party supplied filter, or develop your own filter, using the one in this sample as a model. + +The filter you use should have at least the following capabilities: + +* Perform REST calls to another system. + +* Search through databases. + +* Inspect headers related to authentication and authorization requests. + +This servlet filter must set the username of the authenticated user in a special request attribute. You need to configure that same attribute name in the `TRUSTED_ATTRIBUTE` authentication module, specifically the value of `authenticationIdAttribute`. + +It is helpful if you have a filter that returns an object with the `userRoles` property. If your filter does not support queries using the following parameter: + +[source, console] +---- +queryOnResource + "/" + authenticationId +---- +You will need to provide a security context augmentation script that populates the following authorization properties in the "security" object: + +* `security.authorization.component` + +* `security.authorization.roles` + +The value for the `security.authorization.component` is automatically set to the value specified in any exisitng `queryOnResource` property. + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-workflow-samples.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-workflow-samples.adoc new file mode 100644 index 000000000..4ebd36754 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-workflow-samples.adoc @@ -0,0 +1,954 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-workflow-samples] +== Workflow Samples + +This chapter walks you through the workflow provisioning sample (in the `openidm/samples/workflow` directory) and the workflow use cases (in the `openidm/samples/usecase` directories). For a complete list of the samples provided with OpenIDM, and an overview of each sample, see xref:chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"]. + +[#example-provisioning-workflow] +=== Sample Workflow - Provisioning User Accounts + +This sample, provided in `openidm/samples/workflow`, demonstrates a typical use case of a workflow — provisioning new users. + +The sample demonstrates the use of the Admin UI, to configure user self-service and the Self-Service UI that enables users to complete their registration process. +This sample simulates the following scenario: + +* An existing employee requests that an outside contractor be granted access to an organization's system. + +* The __system__ in this case, is OpenIDM's managed user repository and a remote datasource, represented by an XML file. + +* User roles are stored separately, in a CSV file. + +The sample has three mappings — two for the bidirectional synchronization of the managed user repository and the XML data store, and one for the synchronization of the roles data (stored in the CSV file) to the managed repository. + +[#provisioning-sample-prepare] +==== Prepare OpenIDM For the Provisioning Sample + +In this section, you start OpenIDM, configure the outbound email service, and reconcile user and role data. The reconciliation operations create two managed users, `user1` and `manager1`, and two managed roles, `employee` (assigned to `user1`) and `manager` (assigned to `manager1`). + +==== + +. Start OpenIDM with the configuration for the provisioning sample. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/workflow +---- + +. Log in to the Admin UI (`\https://localhost:8443/admin`) with the default username (`openidm-admin` and password (`openidm-admin`). + +. Configure the outbound email service. ++ +An email configuration is required to send a password-reset email to the user, at the end of the sample. ++ +Select Configure > System Preferences > Email and provide connection information for your preferred email host. ++ +Gmail is configured by default but you must still supply credentials to use Gmail. + +. Reconcile the role data, and the user data. ++ + +.. Select Configure > Mappings. + +.. Click the first mapping (`systemRolesFileRole_managedRole`) and click Reconcile Now. ++ +This reconciliation operation creates two roles in the managed repository (`employee` and `manager`). You can check the result of the reconciliation by selecting Manage > Role. + +.. Go back to the Mappings page (Configure > Mappings), select the second mapping (`systemXmlfileAccounts_managedUser`) and click Reconcile Now. ++ +This reconciliation operation creates the top-level managers (users who do not have their own `manager` property) in the managed user repository. In this sample, there is only one top-level manager (`manager1`). + +.. Click Reconcile Now a second time. ++ +This reconciliation operation creates the employees of the managers that were created by the previous reconciliation. In this sample, there is only one employee (`employee1`). ++ +Check that the manager and employee entries were created correctly by selecting Manage > User. You should have two users — `manager1` and `user1`. + + +. Verify the relationships between your new user and role objects: ++ + +.. Click on `user1`. Note that the `Manager` field shows `manager1` for this user. + +.. On the Authorization Roles tab, note that user1 has two roles — `employee` and `openidm-authorized`. + +.. Click Back to User List then click on `manager1`. Note that the `Manager` field is empty for this user. + +.. On the Authorization Roles tab, note that manager1 has two roles — `manager` and `openidm-authorized`. + + +. Verify the available workflows: ++ + +.. Select Manage > Processes > Definitions. + +.. Click the Contractor onboarding process and look at the sequence diagram for this workflow. + + +. Log out of the Admin UI by selecting Log Out from the top right dropdown menu. + +==== +OpenIDM is now prepared for the Provisioning sample. In the next section you will walk through the workflow whose sequence diagram you saw in the previous step. + + +[#provisioning-sample-running] +==== Running the Provisioning Sample + +As part of provisioning, employees are required to initiate a __Contractor Onboarding__ process. This process is a request to add a contractor to the managed user repository, with an option to include the contractor in the original data source (the XML file). + +When the employee has completed the required form, the request is sent to the manager for approval. Any user with the role `manager` can claim the approval task. If the request is approved, an email is sent to the address provided in the initial form, with a request for the contractor to reset their password. When the password reset has been completed, the contractor is created in the managed user repository. If a request was made to add the contractor to the original data source (the XML file) this is done when the manager approves the request. + +==== + +. Log in to the Self-Service UI (`\https://localhost:8443/`) as the user you created in the previous section (`user1`), with password `Welcome1`. ++ +The user Dashboard is displayed: ++ + +image::images/provisioning-dashboard.png[] + +. Initiate the provisioning workflow as user1: ++ + +.. Under Processes, click the Details link next to the Contractor onboarding process and complete the form for the sample user you will be creating. ++ +Use an email address that you have access to because you will need the email that is sent to this address to complete the workflow. ++ +In the Provision to XML field, select Yes. ++ +This selection enables implicit synchronization from the managed user repository to the XML file. ++ + +image::images/provisioning-add-user.png[] ++ +Note that user1 does not provide the password for this user. This is to ensure that only the actual contractor can log in with this account + +.. Click Start to initiate the process. + +.. Log out of the Self-Service UI. + + +. Approve the workflow task as manager1: ++ + +.. Log in to the Self-Service UI as `manager1`, with password `Welcome1`. + +.. Under My Group's Tasks, locate the Approve Contractor task and select Assign to Me. + +.. Approve Contractor is now listed under My Tasks. ++ +Click the Details link. + +.. Check the form content. (It is the same content that you provided as `user1`, along with a Decision field.) ++ +Select Accept and click Complete to finish the task. + +.. Log out of the Self-Service UI. + + +. Verify that the contractor has been created in the XML file: ++ +Open `openidm/samples/workflow/data/xmlConnectorData.xml` and note the addition of the new contractor entry. Note that there is no value for ``. Note that `user1` is the contractor's manager. ++ +The following excerpt of the `xmlConnectorData.xml` shows a new user entry for bjensen, after the implicit synchronization to the XML file: ++ + +[source, xml] +---- + +... + openidm-authorized + Barbara + user1 + a02536cf-84b2-4d5c-a3ae-480f5e0899e9 + bjensen + bjensen@example.com + + Jensen + +---- + +. Complete the password reset process: ++ + +.. Check the inbox for the email address that you provided when you completed the initial form. ++ +You should have received an email with the subject "Reset your password". + +.. Open the password reset email and click Password reset link. ++ +The link takes you to the Self-Service UI, with the option to Reset Your Password. + +.. Enter a new password and confirmation password, submit the form. ++ +The password that you enter here must comply with the default password policy for managed users, described in xref:../integrators-guide/chap-passwords.adoc#enforce-password-policy["Enforcing Password Policy"] in the __Integrator's Guide__. + +.. Click Return to Login Page and log in with the username that you provided when you completed the initial form, and the new password you have just set. ++ +Notice the Welcome message under Notifications. + + +. Verify that the password reset has been propagated to the XML file: ++ +Open `openidm/samples/workflow/data/xmlConnectorData.xml` and note that the password for the contractor has been added to their entry. + +==== +If you declined the approval request, the user is not created in either data source. + + + +[#workflow-use-cases] +=== Workflow Use Cases + +This section describes a number of sample workflows, that demonstrate typical use cases for OpenIDM. The use cases, provided in `/path/to/openidm/samples/usecase`, work together to describe a complete business story, with the same set of sample data. Each of the use cases is integrated with the Self-Service UI. + +These use cases use OrientDB as a repository by default. Alternative repository configuration files are provided in `/path/to/openidm/samples/usecase/db`. If you want to use one of these alternative repositories, remove the `repo.orientdb.json` file from the `conf/` directory of the use case you are testing (for example, `samples/usecase/usecase1/conf/repo.orientdb.json`) and copy the appropriate JDBC repository configuration files (`datasource.jdbc-default.json` and `repo.jdbc.json`) into that `conf/` directory. For more information on using an alternative repository, see xref:../install-guide/chap-repository.adoc#chap-repository["Installing a Repository For Production"] in the __Installation Guide__. + +Each use case builds on the previous one. You __must__ run the use cases in order, from use case 1 through 3, before you try the remaining use cases. Use cases 2 onwards depend on the `hr_data.ldif` file that you import and reconcile when you run use case 1. + +The use cases assume an initial data set of twenty __ordinary__ managed users in OpenIDM (user.0 - user.19). The users are divided as follows: + +[cols="25%,25%,12%,25%,13%"] +|=== +|Users |Department |Manager |Employees |Contractors +|=== +In addition, the following __special__ users are defined: + +* `hradmin` - represents the human interaction of the HR department + +* `systemadmin` - represents the human interaction of the populated systems (Business and Project) + +* `superadmin` - represents the manager of the managers + + +[NOTE] +==== +Note that the `curl` commands in this section use the secure port for OpenIDM (8443) and assume a self-signed certificate named `self-signed.crt`, located in the directory from which the command is launched. For instructions on using the self-signed certificate that is generated when OpenIDM first starts up, see xref:../integrators-guide/chap-security.adoc#rest-over-https["Restrict REST Access to the HTTPS Port"] in the __Integrator's Guide__. +==== + +[#use-case-1] +==== Use Case 1 - Initial Reconciliation + +This use case assumes an OpenDJ server and populates the managed user repository with users from OpenDJ. +To set up the sample, install and configure OpenDJ, as follows: + +. Download and install OpenDJ, as described in link:../../../opendj/3.5/install-guide/#gui-install[To Install OpenDJ Directory Server With the GUI, window=\_blank]. ++ +This sample assumes that OpenDJ is listening on port 1389, the standard LDAP port for users who cannot use privileged ports. + +. The use case assumes a user with DN `cn=Directory Manager` and password `password` who will bind to the directory server. + +. During the install, import the user data from the LDIF file `/path/to/openidm/samples/usecase/data/hr_data.ldif`. ++ +The OpenDJ server now contains the users required for all the workflow use cases. + + +[#running-use-case1] +.Running Use Case 1 +==== + +. Start OpenIDM with the configuration for use case 1. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/usecase/usecase1 +---- + +. Run reconciliation to populate the managed user repository with the users from the OpenDJ server. ++ +The validation rules in this workflow require a managed user's __manager__ entry to exist before that user can be created. You must therefore run three consecutive reconciliation operations: ++ + +* The first reconciliation creates the `superadmin` user. This user has no manager entry, so is unaffected by the validation rules. Creation fails for the remaining users, because their manager does not yet exist. + +* The second reconciliation creates the 12 users who have the `superadmin` user as their manager. Creation fails for the remaining users, because they require the 12 manager users to exist before they can be created. + +* The third reconciliation creates the remaining 10 users, bringing the total number of users to 23. + ++ +The easiest way to run reconciliation is from the Admin UI: ++ + +.. Log in to the Admin UI (`\https://localhost:8443/admin`) as the administrative user (`openidm-admin`) with password `openidm-admin`. + +.. Select Configure > Mappings. + +.. Click on the only configured mapping (`systemHRAccounts_managedUser`) and click Reconcile Now. + ++ +To run reconciliation from the command line, use the following command: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=systemHRAccounts_managedUser" +{ + "_id": "376b3290-24f0-47a9-8a9e-bba025536c39", + "state": "ACTIVE" + } +---- + +. Run reconciliation twice more to create all the users in the repository. + +. Query the managed users that were created by the three reconciliation operations. ++ +In the Admin UI, select Manage > User and note the new entries in the User List. ++ +Alternatively, run the following command to view the managed user entries over REST: ++ + +[source, console] +---- +$ 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" +{ + "result": [ + { + "_id": "user.7", + "_rev": "1" + }, + { + "_id": "user.3", + "_rev": "1" + }, + { + "_id": "user.4", + "_rev": "1" + }, + ... + { + "_id": "systemadmin", + "_rev": "1" + }, + { + "_id": "hradmin", + "_rev": "1" + }, + { + "_id": "superadmin", + "_rev": "1" + } + ], + "resultCount": 23, + ... +} +---- ++ +Your managed user repository should now contain 23 users. The default password of all the newly created users is `Passw0rd`. + +. Shut down OpenIDM before you proceed with the next use case. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./shutdown.sh +---- + +==== + + +[#use-case-2] +==== Use Case 2 - New User Onboarding + +This use case demonstrates a new user onboarding process. The process can be initiated by any of the users created in the previous reconciliation process. In this example, we use `user.1` to initiate the process. `user.1` captures the details of a new user, and then submits the new user entry for approval by the prospective manager of that new user. + +The use case includes three separate workflows - onboarding (creation of the new user), sunrise (new user start date) and sunset (user end date). + +The use case also demonstrates email notification with the configuration of an external email service. You must configure the external email service, as described in xref:#configure-email-notification["Configuring Email Notification"], __before you start the workflow__. + +The use case demonstrates the OpenIDM Self-Service UI. If you deploy OpenIDM on the local system, you can access the UI at the following URL: `\https://localhost:8443`. + +[#configure-email-notification] +.Configuring Email Notification +==== + +. Start OpenIDM with the configuration for use case 2. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/usecase/usecase2 +---- + +. Configure the outbound email service. ++ +Log in to the Admin UI (`\https://localhost:8443/admin/`) as the default administrative user (`openidm-admin` with password `openidm-admin`). ++ +Select Configure > System Preferences > Email and provide connection information for your preferred email host. ++ +Gmail is configured by default but you must still supply credentials to use Gmail. + +. Log out of the Admin UI. + +. Change the notification email parameters in the workflow definition file. To edit the workflow definition file: ++ + +.. Copy the workflow archive (`.bar`) file (`newUserCreate.bar`) to a temporary location, such as a `/tmp` directory: ++ + +[source, console] +---- +$ cd /path/to/openidm/samples/usecase/usecase2/workflow +$ cp newUserCreate.bar /tmp/ +---- + +.. Unzip the temporary workflow `.bar` file. ++ +This step extracts the workflow definition file (`newUserCreate.bpmn20.xml`) and two xhtml templates required by the workflow: ++ + +[source, console] +---- +$ unzip /tmp/newUserCreate.bar +Archive: newUserCreate.bar + inflating: nUCDecideApprovalForm.xhtml + inflating: nUCStartForm.xhtml + inflating: newUserCreate.bpmn20.xml +---- + +.. Edit the extracted workflow definition file (`newUserCreate.bpmn20.xml`). The email parameters are towards the end of this file: ++ + +[source, console] +---- +$ cd /tmp +$ grep emailParams newUserCreate.bpmn20.xml +emailParams = [from : 'usecasetest@forgerock.com', to : 'notification@example.com', +... +---- ++ +Change the `from` and `to` parameters to reflect valid email addresses. + +.. Zip up the amended workflow definition file, and the xhtml templates into a workflow `.bar` file. ++ + +[source, console] +---- +$ zip newUserCreate.bar newUserCreate.bpmn20.xml nUCDecideApprovalForm.xhtml nUCStartForm.xhtml +updating: nUCDecideApprovalForm.xhtml (deflated 82%) +updating: nUCStartForm.xhtml (deflated 82%) +updating: newUserCreate.bpmn20.xml (deflated 85%) +---- + +.. Copy the new `.bar` file to the workflow directory, overwriting the existing `.bar` file. ++ + +[source, console] +---- +$ cp /tmp/newUserCreate.bar /path/to/openidm/samples/usecase/usecase2/workflow +---- + + +==== + +[#initiate-onboarding] +.Initiating the Onboarding Workflow +==== + +. Start OpenIDM with the configuration for use case 2 (if you have not already done so when you configured the email service). ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/usecase/usecase2 +---- + +. Log in to the Self-Service UI (`\https://localhost:8443`) as `user.1` with password `Passw0rd`. ++ +In this sample, any user who logs into the Self-Service UI can view the new User Onboarding Process workflow. + +. Click Details next to User Onboarding Process link and complete the fields for a sample new user. ++ +__Department__. Specifies one of four departments that the new user will belong to (Human Resources, Production Planning, Sales & Distribution, or Treasury & Payments). The value you select here determines the __manager__ of the new user, to which the request will be sent for approval. (See the previous table of users for a list of the managers of each department.) ++ +__User Type__. Governs user access to specific accounts. If the User Type is Employee, the new user will have access to an account named Business. This access is represented as an attribute of the managed user entry in the OpenIDM repository, as follows: `accounts : ["Business"]`. If the User Type is Contractor, the new user will have no accounts associated with its managed user entry in OpenIDM. ++ +__Send Email Notification__. Indicates whether an email should be sent to alert the manager of the new required approval. The email details used here are defined when you configure email notification, as described in xref:#configure-email-notification["Configuring Email Notification"]. If you select not to send an email notification, the notification is simply added to the OpenIDM repository, and appears when the manager logs into the UI. + +. Click Start to initiate the onboarding workflow. ++ +This action sends the new user request to the corresponding __management__ users (the department manager, as well as the `superadmin` user, who is an overall manager). + +. Log out of the UI, and log back in as the management user of the department that you selected when you completed the new user form. For example, if you selected Human Resources, log in as `user.0`, which simulates the management user for the HR department. All users have the password `Passw0rd`. ++ +Notice that the management user now has an Onboarding Approval task in the queue of tasks assigned to that user's group. ++ + +image::images/approval-task.png[] + +. Select Assign to Me from list next to the Onboarding Approval task. ++ +This action __claims__ the task for `user.0`, removes it from the group queue, and places it in the list of pending tasks for `user.0`. + +. Select Details next to the Onboarding Approval task under My Tasks. ++ +The complete new user request is displayed for the manager's approval. As the manager, you can add any information that was missing from the original request. ++ +In addition, you can specify the following information for the new user. ++ + +* __Start Date__. Completing this field results in the user being created, with a `startDate` added to that user's managed user entry. The status of the user is `inactive`. This field is optional, and is used by the task scanner to trigger the Sunrise workflow. + +* __End Date__. Completing this field results in the user being created, with an `endDate` added to that user's managed user entry. The field is optional, and is used by the task scanner to trigger the Sunset workflow. + +* __Decision__. Selecting Reject here terminates the workflow and sends a notification to the user who initiated the workflow. Selecting Accept creates the managed user entry in OpenIDM. The password of the new user is `Passw0rd`. ++ +Two notifications are created when the request is accepted - one for the user who initiated the workflow, and one for the newly created user. The notifications are visible in the UI after login. If you selected email notification, one email is sent to the user that you defined when you configured email notification, as described in xref:#configure-email-notification["Configuring Email Notification"]. + + +. At the bottom of the form, there is an option either to Requeue the request or to Complete it. Click Complete. ++ +If you click Requeue here, the task is removed from the list of that user's tasks, and returned to the list of tasks pending for that group. The task can then be claimed by any member of that group. ++ +When the new user request has been approved, the user is created in the OpenIDM repository. If you did not include a Start Date in the manager approval, you should now be able to log into the UI with the details of the new user. If you included a Start Date, you need to complete the sunrise workflow before the user account is active (which will enable you to log in as this user). + +==== + +[#initiate-sunrise] +.Initiating the Sunrise Workflow +==== +If a sunrise date is specified for the new user, the user is created in the repository, with an `inactive` account status. + +* To trigger the sunrise workflow (which activates the account), enable the sunrise task scanning schedule. The schedule is disabled by default. ++ +Modify the schedule configuration file (`schedule-taskscan_sunrise.json`), setting the `enabled` property to `true`. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ grep "enabled" samples/usecase/usecase2/conf/schedule-taskscan_sunrise.json +"enabled" : true, +---- ++ +The scan runs every minute, and checks the repository for users that have a sunrise date that is anything up to one day after the current date. When the scan is triggered, it locates the newly created user and starts the sunrise workflow on this user. The workflow takes the following actions: ++ + +** Changes the account status of the user to `active`. ++ +You can check that this part of the workflow has completed by looking at the user's account in the Admin UI: ++ + +. Log in to the Admin UI as `openidm-admin` with password `openidm-admin`, then select Manage > User. + +. Click on the account of the new user that was created in the previous section and note their Status. ++ +The following image shows the status for the new user jdoe, who was created by the previous workflow: ++ + +image::images/new-user.png[] + ++ + +** Generates a notification for the new user, which is visible when the user logs into the Self-Service UI. + ++ + +image::images/workflow-notifications.png[] + +==== + +[#initiate-sunset] +.Initiating the Sunset Workflow +==== +If a sunset date is set for the new user, you can trigger the sunset workflow to deactivate the user account when the end of his work period is reached. + +. To trigger the sunset workflow, enable the sunset task scanning schedule. The schedule is disabled by default. ++ +Modify the schedule configuration file (`schedule-taskscan_sunset.json`), setting the `enabled` property to `true`. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ grep "enabled" samples/usecase/usecase2/conf/schedule-taskscan_sunset.json + +"enabled" : true, +---- ++ +The scan runs every minute, and checks the repository for users that have a sunset date that is anything up to one day after the current date. When the scan is triggered, it locates users whose contracts are about to end, and starts the sunset workflow on these users. When the workflow is initiated, it assigns a task to the manager of the affected user. In our example, the task is assigned to `user.0`. + +. When the sunset schedule has been enabled, log in to the Self-Service UI as `user.0` (with password `Passw0rd`). If the user's sunset date is within one day of the current date, a Contract Termination task becomes available under the manager's My Group's Tasks section. ++ +Select the contract termination task and click Details. + +. In the Decision field, select either Accept termination or Modify date, then click Complete. ++ +When you accept the termination, the user's account status is set to `inactive` and the HR administrative user receives a notification to that effect, the next time that user logs into the UI. The deactivated user is no longer able to log into the UI. ++ +If you select to modify the date, the sunset date of that user is changed to the value that you specify in the End Date field on that form. The management user receives a UI notification that the employee's contract has been extended. + +. Shut down OpenIDM before you proceed with the next use case. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./shutdown.sh +---- + +==== + + +[#use-case-3] +==== Use Case 3 - User Access Request + +This use case simulates a user access request, with two levels of approval for the request. + +If you want to use email notification with this workflow, start OpenIDM with the configuration for Use Case 3, then follow the instructions in xref:#configure-email-notification["Configuring Email Notification"], substituting and `usecase3/workflow/accessRequest.bpmn20.xml` for the file described in that procedure. + +==== + +. Start OpenIDM with the configuration for use case 3, if you have not already started the server to configure email notification. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/usecase/usecase3 +---- + +. Log into the Self-Service UI as `user.1` with password `Passw0rd`. ++ +`user.1` belongs to the HR department and, in this workflow, is requesting access to a Project system. + +. Click Details next to the Access request process and click Start to start the workflow. ++ +A User Access Request appears in the list of tasks for `user.1`. + +. Click Details next to the User Access Request task. ++ +The resulting form indicates the various systems to which the user may request access. ++ +__Access to Business system__. This field reflects the current value of the `accounts` property for that user in the repository. If the value includes `Business` this field is True. ++ +__Access to Project system__. Set this field to True to request Project access for `user.1`. ++ +__Send Email Notification__. Set to True to send an email to alert the manager of the new access request. The email details used here are defined when you configure email notification, as described in xref:#configure-email-notification["Configuring Email Notification"]. If you select not to send an email notification, the notification appears when the manager logs into the UI. ++ +Select either Cancel, to terminate the process, or Request, to start a user task, assigned to the manager of the user who is requesting access (`user.0` in this example), and select Complete. + +. Log out of the Self-Service UI and log back in as the manager (`user.0` with password `Passw0rd`). + +. Under My Group's Tasks, locate the User Access Request Approval task and select Assign to me. ++ +Note that the User Access Request Approval task has moved under My Tasks. + +. Click Details next to the User Access Request Approval task. + +. The details of the access request are displayed. The manager is able to modify the access rights. Select Accept or Reject to approve or deny the request. ++ +Rejecting the request results in a notification being sent to the user who made the request. If you have enabled email notification, a single email is sent to the account that you specified when you configured email notification. ++ +Accepting the request initiates a second approval task, assigned to the `systemadmin` user. ++ + +image::images/ui-access-request.png[] ++ +Click Complete to complete the task. + +. Log out of the UI and log in as the `systemadmin` user (with password `Passw0rd`). ++ +This user now has one User Access Request Approval task in his queue. + +. Select the task and click Details. ++ +This task interface is similar to that of the task that was assigned to the manager. ++ +Rejecting the request results in a notification being sent to the user who made the request. ++ +Accepting the request updates the managed/user record in OpenIDM, to reflect the approved access changes. ++ +If you have enabled email notification, a single email is sent to the account defined when you configured the external email service (xref:#configure-email-notification["Configuring Email Notification"]), indicating whether the request has been accepted or rejected. + +==== +Note that this sample includes an __escalation__ step that is attached to the manager approval task. If the manager does not complete assessment of the user task within ten minutes of its initiation, a new user task is created and assigned to the `superadmin` user. This task has the same interface and functionality as the task assigned to the manager. Accordingly, when the `superadmin` user completes the task, the execution is passed to the `systemadmin` user for approval. + +Shut down OpenIDM before you proceed with the next use case. + +[source, console] +---- +$ cd /path/to/openidm +$ ./shutdown.sh +---- + + +[#use-case-4] +==== Use Case 4 - Orphan Account Detection + +This use case demonstrates two asynchronous tasks, started from a reconciliation process: + +* Detecting orphan accounts on a target object set + +* Handling ambiguous results during correlation + +This use case relies on a customized synchronization configuration (mapping) file, named `syncManagedBusiness.json`, in the `/path/to/openidm/samples/usecase/usecase4/conf` directory. + +This file defines a mapping (`recon_managedUser_systemBusiness`) between a source (managed users) and a target object set. The target object set is defined in the file `samples/usecase/usecase4/data/business.csv`. The `business.csv` file includes all users from the initial reconciliation (described in xref:#use-case-1["Use Case 1 - Initial Reconciliation"]). These users are categorized as `employees`, and therefore include the property `"accounts" : ["Business"]` in their managed user entry (see xref:#use-case-2["Use Case 2 - New User Onboarding"] for an explanation of the User Type). + +The mapping includes the following `"validSource"` field: + +[source] +---- +"validSource" : { + "type" : "text/javascript", + "file" : "script/isSourceValidBusiness.js" +}, +---- +This field references a script which specifies that only those users who are employees are taken into account during the reconciliation. + +In addition, the `business.csv` file includes the following users: + +* `user.50`. This user is defined __only__ in the .csv file, and not in the managed/user repository. When a reconciliation operation is run, this user is detected as an __orphan account__. The orphan account workflow is triggered when an "UNQUALIFIED" or "UNASSIGNED" situation is encountered, as indicated in this section of the mapping: ++ + +[source] +---- +{ + "situation" : "UNQUALIFIED", + "action" : { + "workflowName" : "orphanAccountReport", + "type" : "text/javascript", + "file" : "workflow/triggerWorkflowFromSync.js" + } +}, +{ + "situation" : "UNASSIGNED", + "action" : { + "workflowName" : "orphanAccountReport", + "type" : "text/javascript", + "file" : "workflow/triggerWorkflowFromSync.js" + } +} +---- + +* `user.33`. This user has a `"userName"` attribute of `"user.3"` (which is the same as the `"userName"` attribute of the user, `user.3`). The correlation query of the reconciliation operation is based on the `"userName"` attribute. During the correlation query, two candidate users are therefore correlated with the same managed user (`user.3`), and the result is ambiguous. The manual match workflow is triggered when an "AMBIGUOUS" situation is encountered, as indicated in this section of the mapping: ++ + +[source] +---- +{ + "situation" : "AMBIGUOUS", + "action" : { + "workflowName" : "manualMatch", + "type" : "text/javascript", + "file" : "workflow/triggerWorkflowFromSync.js" + } +} +---- + + +==== + +. Before you start with this use case, rename the mapping file to `sync.json`. ++ + +[source, console] +---- +$ cd /path/to/openidm/samples/usecase/usecase4/conf +$ mv syncManagedBusiness.json sync.json +---- + +. Start OpenIDM with the configuration for use case 4. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/usecase/usecase4 +---- + +. Reconcile the managed user repository with the CSV file, either by using the Admin UI or over the command line. ++ +To use the Admin UI, log in to the Admin UI (`\https://localhost:8443/admin/`) as `openidm-admin` with password `openidm-admin`. Select Configure > Mappings, click on the mapping `recon_managedUser_systemBusiness` and click Reconcile Now. ++ +To use the command line, run the following command: ++ + +[source, console] +---- +$ curl \ + --cacert self-signed.crt \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "https://localhost:8443/openidm/recon?_action=recon&mapping=recon_managedUser_systemBusiness" +---- ++ +When the reconciliation operation finds the ambiguous entry (`user.3`) and the orphan entry (`user.50`) in the CSV file, two asynchronous workflows are launched (`manualMatch` and `orphanAccountReport`), as indicated in the mapping file, described previously. + +. Log in to the Self-Service UI (`\https://localhost:8443`) as the `systemadmin` user, with password `Passw0rd`. + +. Next to the Manual Linking Task in the My Tasks list, click Details. ++ +The __Possible targets__ field presents a list of target entries to which the ambiguous record can be linked. In this example, `user.3 - Atrc, Aaron` and `user.33 - Atrc, Aaron` are the two candidate users found in the target object set by the correlation query. When you select one of these values, the workflow manually links the managed user (`user.3`) to the selected user. ++ +Click complete to finish the manual account linking task. ++ +If you select Ignore, here, no action is taken (no link is created), and the workflow terminates. + +. Next to the Orphan Account Task in the My Tasks list, click Details. ++ +The __Link to__ field enables you to enter an existing managed user ID to which this orphan account should be linked. For the purposes of this example, enter `user.5`. ++ +In the Decision field, select Link to link the orphan account to the ID that you entered in the previous step. Click Complete to complete the task. ++ +Selecting Delete here deletes the user from the target object set (the CSV file in this case) and terminates the workflow. + +. Shut down OpenIDM before you proceed with the next use case. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./shutdown.sh +---- + +==== + +[NOTE] +==== +Use Case 5 has been removed from the sample use cases. +==== + + +[#use-case-6] +==== Use Case 6 - Password Change Reminder + +This use case demonstrates using the task scanner to trigger a password change reminder workflow for managed users. + +In this example, each managed user entry in OpenIDM has a dedicated attribute, `lastPasswordSet`, that stores the date on which the password was last changed. The value of this attribute is updated by an `onStore` script, defined in the managed user configuration file (`conf/managed.json`), as follows: + +[source] +---- +"onStore" : { + "type" : "text/javascript", + "file" : "script/onStoreManagedUser.js" +}, +---- +When a new password is stored for a user, the script sets the date on which this change was made. The task scanner periodically scans the `lastPasswordSet` attribute, and starts the workflow if the password was changed more than an hour ago. This condition is configured in the schedule configuration file (`schedule-taskscan_passwordchange.json`): + +[source, console] +---- +$ cd /path/to/openidm +$ more samples/usecase/usecase6/conf/schedule-taskscan_passwordchange.json + +... +"condition" : { + "before" : "${Time.now - 1h}" +}, +.... +---- +Obviously, in a real deployment, the period between required password changes would be longer, and this value would need to be set accordingly. For the purposes of testing this use case, you might want to set the value to a shorter period, such as `"${Time.now - 1m}"`, which will send the notification one minute after a password change. + +By default, the workflow sends notifications to the user entry, visible when the user logs into the UI. If you want notifications sent by email, configure the external email service, as follows: + +==== + +. Set up outbound email, as described in xref:#configure-email-notification["Configuring Email Notification"]. + +. Enable email notification in the script file that starts the workflow (`samples/usecase/usecase6/script/passwordchange.js`). For example: ++ + +[source, console] +---- +$ cd /path/to/openidm +$ more samples/usecase/usecase6/script/passwordchange.js + +/*global objectID*/ + +(function () { + var params = { + "userId" : objectID, + "emailEnabled" : "true", + "_key": "passwordChangeReminder" +}; +---- + +. Make sure that all managed users have a valid email address as the value of their `mail` attribute. + +==== +The task scanning schedule is disabled by default. To test this use case, follow these steps: + +==== + +. Enable the task scanning schedule by setting `enabled` to `true` in the schedule configuration file (`schedule-taskscan_passwordchange.json`). ++ + +[source, console] +---- +$ cd /path/to/openidm +$ more samples/usecase/usecase6/conf/schedule-taskscan_passwordchange.json +{ + "enabled" : true, +... +---- + +. Start OpenIDM with the configuration for use case 6. ++ + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/usecase/usecase6 +---- ++ + +. Log in to the Self-Service UI as any of the users listed in the introduction to this section (for example, `user.4`, with password `Passw0rd`). ++ +You will see a password expiration notice. ++ +If you ignore the change for five minutes, you will see a second warning, that the password is about to expire. ++ +If you ignore that second warning for another two minutes, the user's account is deactivated. + +. (Optional) To avoid the second notification, or the account deactivation, you can change the user password through the UI, as follows: ++ + +.. Log in to the UI as the user whose password you want to change and click Change Password at the top right of the page. + +.. Enter a new password that conforms to the requirements of the password policy. + +.. Enter the old password (in this case `Passw0rd`). + + +==== + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/chap-xml-samples.adoc b/openidm-doc/src/main/asciidoc/samples-guide/chap-xml-samples.adoc new file mode 100644 index 000000000..e0a62d83e --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/chap-xml-samples.adoc @@ -0,0 +1,823 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[#chap-xml-samples] +== XML Samples - Reconciling Data Between OpenIDM and an XML File + +This chapter walks you through the XML samples (those samples labeled Sample 1, Sample 8, and Sample 9 in the `openidm/samples` directory. For a complete list of the samples provided with OpenIDM, and an overview of each sample, see xref:chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"]. + +[#more-sample-1] +=== First OpenIDM Sample - Reconciling an XML File Resource + +This chapter provides an overview of the first sample and how it is configured. For a complete list of the samples provided with OpenIDM, and an overview of each sample, see xref:chap-overview.adoc#chap-overview["Overview of the OpenIDM Samples"] or the README in the `openidm/samples` directory. + +[#about-the-sample] +==== About the XML Sample + +OpenIDM connects data objects held between resources by mapping one object to another. To connect to external resources, OpenIDM uses link:http://openicf.forgerock.org[OpenICF, window=\_top] connectors, configured for use with each external resource. + +When objects in one external resource change, OpenIDM determines how the changes affect other objects, and can make the changes as necessary. This sample demonstrates how OpenIDM does this by using __reconciliation__. OpenIDM reconciliation compares the objects in one object set to mapped objects in another object set. For a complete explanation of reconciliation and synchronization, see xref:../integrators-guide/chap-synchronization.adoc#sync-types["Types of Synchronization"] in the __Integrator's Guide__. + +This sample connects to an XML file that holds sample user data. The XML file is configured as the authoritative source. In this sample, users are created in the local repository to show you how you can manage local users through the REST APIs as well as through the OpenIDM UI. + +You can also use OpenIDM without storing managed objects for users in the local repository, instead reconciling and synchronizing objects directly through connectors to external resources. + +This sample involves only one external resource. In practice, you can connect as many resources as needed for your deployment. +[#about-the-sample-configuration] +.Sample Configuration Files +-- +You can find configuration files for the sample under the `openidm/samples/sample1/conf` directory. As you review the sample, keep the following in mind: + +* Start OpenIDM with the configuration associated with Sample 1: ++ + +[source, console] +---- +$ ./startup.sh -p samples/sample1 +---- ++ +For more information, see xref:#install-sample1["Install the Sample"]. + +* OpenIDM regularly scans for any scheduler configuration files in the `conf` directory. + +* OpenIDM's reconciliation service reads the mappings and actions for the source and target users from `conf/sync.json`. + +* When you initiate a reconciliation, OpenIDM queries all users in the source, and then creates, deletes, or modifies users in the local OpenIDM repository as mapped in `conf/sync.json`. + +* OpenIDM writes all operations to the audit logs in both the internal database and also the flat files in the `openidm/audit` directory. + +* The default Sample 1 version of the `conf/authentication.json` file includes several authentication modules: `STATIC_USER`, `MANAGED_USER`, `INTERNAL_USER`, and `CLIENT_CERT`. For more information, see xref:../integrators-guide/chap-auth.adoc#supported-auth-modules["Supported Authentication Modules"] in the __Integrator's Guide__. + +When you start OpenIDM with the `-p` project variable (`./startup.sh -p samples/sample1`), the `&{launcher.project.location}` is set to a value of `samples/sample1`. The configuration files use this location, as shown in the following sections. + +The following configuration files play important roles in this sample: + +`samples/sample1/conf/provisioner.openicf-xml.json`:: +This connector configuration file serves as the XML file resource. It is a copy of the file of the same name found in the `samples/provisioners` directory. + ++ +In this sample, the connector instance acts as the authoritative source for users. In the configuration file you can see that the `xmlFilePath` is set to `&{launcher.project.location}/data/xmlConnectorData.xml`. + ++ +The `&{launcher.project.location}`, in this case, is `sample/sample1`. + ++ +For details on the OpenICF connector configuration files, see xref:../integrators-guide/chap-resource-conf.adoc#chap-resource-conf["Connecting to External Resources"] in the __Integrator's Guide__. + +`samples/sample1/conf/schedule-reconcile_systemXmlAccounts_managedUser.json`:: +The sample schedule configuration file defines a reconciliation job that, if enabled by setting `"enabled" : true`, starts a reconciliation each minute for the mapping named `systemXmlAccounts_managedUser`. The mapping is defined in the configuration file, `conf/sync.json`: ++ + +[source, javascript] +---- +{ + "enabled" : false, + "type": "cron", + "schedule": "0 0/1 * * * ?", + "persisted" : true, + "misfirePolicy" : "fireAndProceed", + "invokeService": "sync", + "invokeContext": { + "action": "reconcile", + "mapping": "systemXmlfileAccounts_managedUser" + } +} +---- ++ +For information about the schedule configuration, see xref:../integrators-guide/chap-scheduler-conf.adoc#chap-scheduler-conf["Scheduling Tasks and Events"] in the __Integrator's Guide__. + ++ +Apart from the scheduled reconciliation run, you can also start the reconciliation run through the REST interface. The call to the REST interface is an HTTP POST such as the following: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemXmlfileAccounts_managedUser&waitForCompletion=true" +---- ++ +The `waitForCompletion=true` parameter specifies that the operation should return only when it has completed. + +`samples/sample1/conf/sync.json`:: +This sample configuration file defines the configuration for reconciliation and synchronization. The `systemXmlAccounts_managedUser` is the mapping for the reconciliation. This entry in `conf/sync.json` defines the synchronization mappings between the XML file connector (source) and the local repository (target): ++ + +[source, javascript] +---- +{ + "mappings": [ + { + "name": "systemXmlfileAccounts_managedUser", + "source": "system/xmlfile/account", + "target": "managed/user", + "correlationQuery": { + "type": "text/javascript", + "source": "var query = {'_queryId' : 'for-userName', + 'uid' : source.name};query;" + }, + "properties": [ + { + "source": "email", + "target": "mail" + }, + { + "source": "firstname", + "target": "givenName" + }, + { + "source": "lastname", + "target": "sn" + }, + } + "source": "description", + "target": "description" + }, + { + "source": "_id", + "target": "_id" + }, + { + "source": "name", + "target": "userName" + }, + { + "source": "password", + "target": "password" + }, + { + "source" : "mobileTelephoneNumber", + "target" : "telephoneNumber" + }, + { + "source" : "roles", + "transform" : { + "type" : "text/javascript", + "source" : "var _ = require('lib/lodash'); _.map(source.split(','), + function(role) { return {'_ref': 'repo/internal/role/' + role} });" + }, + "target" : "authzRoles" + } + ], + "policies": [ + { + "situation": "CONFIRMED", + "action": "UPDATE" + }, + { + "situation": "FOUND", + "action": "IGNORE" + }, + { + "situation": "ABSENT", + "action": "CREATE" + }, + { + "situation": "AMBIGUOUS", + "action": "IGNORE" + }, + { + "situation": "MISSING", + "action": "IGNORE" + }, + { + "situation": "SOURCE_MISSING", + "action": "IGNORE" + }, + { + "situation": "UNQUALIFIED", + "action": "IGNORE" + }, + { + "situation": "UNASSIGNED", + "action": "IGNORE" + } + ] + } + ] +} +---- ++ +Source and target paths that start with `managed`, such as `managed/user`, always refer to objects in the local OpenIDM repository. Paths that start with `system`, such as `system/xmlfile/account`, refer to external objects, in this case, objects in the XML file. + ++ +For more information about synchronization, reconciliation, and `sync.json`, see xref:../integrators-guide/chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"] in the __Integrator's Guide__. + ++ +For additional examples related to scripting, see the xref:../integrators-guide/appendix-scripting.adoc#appendix-scripting["Scripting Reference"] in the __Integrator's Guide__. + +-- + + +[#install-sample1] +==== Install the Sample + +Start OpenIDM with the configuration for Sample 1: + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample1 +---- + + +[#sample1-adminui] +==== Review the Sample in the Administrative User Interface + +OpenIDM includes a web-based Administrative User Interface, known as the Admin UI. For details, see xref:../integrators-guide/chap-ui.adoc#ui-admin["Configuring OpenIDM from the Admin UI"] in the __Integrator's Guide__. + +After starting OpenIDM, you can access the Admin UI by navigating to `\https://localhost:8443/admin`. The first time you log in, use the default administrative credentials, (Login: openidm-admin, Password: openidm-admin). + +[WARNING] +==== +To protect your deployment in production, change the default administrative password. To do so, navigate to the Self-Service UI at `\https://localhost:8443/` and click Change Password. +==== +You should now see the Dashboard screen, with quick start cards for common administrative tasks. with the connectors and managed objects associated with that configuration. + + +[#sample-running-reconciliation] +==== Running Reconciliation + +Reconcile the objects in the resources, either by setting `"enabled" : true` in the schedule configuration file (`conf/schedule-reconcile_systemXmlAccounts_managedUser.json`) and then waiting until the scheduled reconciliation happens, or by using the REST interface, as shown in the following example: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemXmlfileAccounts_managedUser&waitForCompletion=true" +---- +Successful reconciliation returns a reconciliation run ID, and the status of the reconciliation operation, as follows: + +[source, console] +---- +{ + "_id":"2d87c817-3d00-4776-a705-7de2c65937d8", + "state":"SUCCESS" +} +---- +Alternatively, you can run the same reconciliation in the Admin UI: + +. Click Configure > Mappings. ++ +For Sample 1, you should see one mapping, `systemXmlfileAccounts_managedUser`. + +. Select Edit to access the configuration options associated with reconciliation. + +. To run the reconciliation, click Reconcile Now. + + +[#d5830e860] +image::images/admin-ui-mappings-sample1.png[] + + +[#sample-viewing-users-logs] +==== Viewing Users and Logs + +After reconciliation, you can use the Admin UI to display user records in both the source and target resources: + +. Navigate to the URL where OpenIDM is installed. ++ +If it is local, navigate to `\https://localhost:8443/admin`. + +. Click Configure > Mappings, then select the only available mapping (`systemXmlfileAccounts_managedUser`) + +. On the Association tab, you should see the result of the reconciliation, from source to target, at the bottom of the screen. + +You can also use the REST interface to display all users in the local repository. Use a REST client to perform an HTTP GET on the following URL: `\http://localhost:8080/openidm/managed/user?_queryId=query-all-ids` with the headers `"X-OpenIDM-Username: openidm-admin"` and `"X-OpenIDM-Password: openidm-admin"`. + +OpenIDM returns JSON data. Depending on the browser, you can use a REST client to display the JSON or download it as a file. Alternatively, you can use the following link:http://curl.haxx.se/[curl, window=\_top] command to get the JSON response: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" + + { + "result": [ + { + "_id": "scarter", + "_rev": "1" + }, + { + "_id": "bjensen", + "_rev": "1" + } + ], +... +} +---- +In addition to querying the users by their ID, you can set up arbitrary queries. For more information about using query expressions in a REST call, see xref:../integrators-guide/chap-data.adoc#queries["Defining and Calling Queries"] in the __Integrator's Guide__. + +Now try a RESTful GET of user `bjensen` by appending the user ID to the managed user URL (`\http://localhost:8080/openidm/managed/user/`): + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user/bjensen" + { + "_id": "bjensen", + "_rev": "1", + "mail": "bjensen@example.com", + "givenName": "Barbara", + "sn": "Jensen", + "description": "Created By XML1", + "userName": "bjensen@example.com", + "telephoneNumber": "1234567", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- +The complete user record is returned. If you need this level of information for all users, substitute `query-all` for `query-all-ids`. + +You can filter the output with the query expressions described in xref:../integrators-guide/chap-data.adoc#queries["Defining and Calling Queries"] in the __Integrator's Guide__. + +As defined in the mapping file `conf/sync.json`, the `sn` and `mail` parameters correspond to surname (sn) and email address, respectively. + +For example, the following RESTful GET filters output by surname (sn): + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=true&_fields=sn" + + { + "result": [ + { + "_id": "scarter", + "_rev": "1", + "sn": "Carter" + }, + { + "_id": "bjensen", + "_rev": "1", + "sn": "Jensen" + } + ], +... +} +---- +Now that you have a list of users, you can add more fields to your query: + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryFilter=true&_fields=sn,mail,description" + + { + "result": [ + { + "_id": "scarter", + "_rev": "1", + "sn": "Carter", + "mail": "scarter@example.com", + "description": "Created By XML1" + }, + { + "_id": "bjensen", + "_rev": "1", + "sn": "Jensen", + "mail": "bjensen@example.com", + "description": "Created By XML1" + } + ], +... +} +---- +This information is also available in the CSV format audit logs located in the `openidm/audit` directory: + +[source, console] +---- +$ ls /path/to/openidm/audit/ +access.csv activity.csv recon.csv +---- +For more information about the contents of each of these files, see xref:../integrators-guide/chap-auditing.adoc#audit-log-topics["Audit Log Event Topics"] in the __Integrator's Guide__. + +You can get a similar level of information for each user. For example, after running reconciliation, follow the instructions in xref:#sample-viewing-users-logs["Viewing Users and Logs"], and review information from the reconciled linked resource. + + +[#sample-adding-users-resource] +==== Adding Users in a Resource + +Add a user to the source connector XML data file to see reconciliation in action. During the next reconciliation, OpenIDM finds the new user in the source connector, and creates the user in the local repository. + +==== + +. To add the user copy the following XML into `openidm/samples/sample1/data/xmlConnectorData.xml`: ++ + +[source, xml] +---- + + tmorris + tmorris@example.com + Toni + Morris + tmorris@example.com + 1234567 + openidm-authorized + Created By XML1 + +---- + +. Run reconciliation again, as described in xref:#sample-running-reconciliation["Running Reconciliation"]. + +. After reconciliation has run, query the local repository to see the new user appear in the list of all managed users: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +{ + "result": [ + { + "_id": "scarter", + "_rev": "2" + }, + { + "_id": "bjensen", + "_rev": "2" + }, + { + "_id": "tmorris", + "_rev": "1" + } + ], +... +} +---- + +==== +To see what happened during the reconciliation operation, look at the reconciliation audit log, `openidm/audit/recon.csv`. This formatted excerpt from the log covers the two reconciliation runs done in this sample: + +[source, csv] +---- +"_id", "action",...,"reconId","situation","sourceObjectId", "targetObjectId","timestamp"; +"7e...","CREATE",...,"486...", "ABSENT", "system/xmlfile/acc.../bjensen","managed/user/bjensen",...; +"1a...","CREATE",...,"486...", "ABSENT", "system/xmlfile/acc.../scarter","managed/user/scarter",...; +"33...","UPDATE",...,"aa9...", "CONFIRMED","system/xmlfile/acc.../bjensen","managed/user/bjensen",...; +"1d...","UPDATE",...,"aa9...", "CONFIRMED","system/xmlfile/acc.../scarter","managed/user/scarter",...; +"0e...","CREATE",...,"aa9...", "ABSENT", "system/xmlfile/acc.../tmorris","managed/user/tmorris",...; +---- +The relevant audit log fields in this example are: action, situation, `sourceObjectId`, and `targetObjectId`. For each object in the source, reconciliation leads to an action on the target. + +In the first reconciliation run (abbreviated `reconID` is shown as `486...`), the source object does not exist in the target, resulting in an ABSENT situation and an action to CREATE the object in the target. The object created earlier in the target does not exist in the source, and so is IGNORED. + +In the second reconciliation run (abbreviated `reconID` is shown as `aa9...`), after you added a user to the source XML, OpenIDM performs an UPDATE on the user objects `bjensen` and `scarter` that already exist in the target. OpenIDM performs a CREATE on the target for the new user (`tmorris`). + +You configure the action that OpenIDM takes based on an object's situation in the configuration file, `conf/sync.json`. For the list of all possible situations and actions, see xref:../integrators-guide/chap-synchronization.adoc#chap-synchronization["Synchronizing Data Between Resources"] in the __Integrator's Guide__. + +For details about auditing, see xref:../integrators-guide/chap-auditing.adoc#chap-auditing["Using Audit Logs"] in the __Integrator's Guide__. + + +[#sample-adding-users-rest] +==== Adding Users Over REST + +You can add users to the local repository over the REST interface. The following example adds a user named James Berg. + +Create `james` (UNIX): + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "_id":"jberg", + "userName":"jberg", + "sn":"Berg", + "givenName":"James", + "mail":"jberg@example.com", + "telephoneNumber":"5556787", + "description":"Created by OpenIDM REST.", + "password":"MyPassw0rd" + }' \ + "http://localhost:8080/openidm/managed/user?_action=create" + { + "_id": "jberg", + "_rev": "1", + "userName": "jberg", + "sn": "Berg", + "givenName": "James", + "mail": "jberg@example.com", + "telephoneNumber": "5556787", + "description": "Created by OpenIDM REST.", + "accountStatus": "active", + "effectiveRoles": [], + "effectiveAssignments": [] +} +---- +Create `james` (Windows): + +[source, console] +---- +C:\> curl ^ + --header "X-OpenIDM-Username: openidm-admin" ^ + --header "X-OpenIDM-Password: openidm-admin" ^ + --header "Content-Type: application/json" ^ + --request POST ^ + --data "{\"_id\":\"jberg\",\"userName\":\"jberg\",\"sn\":\"Berg\",\"givenName\":\"James\",\"email\":\"jberg@example.com\",\"telephoneNumber\":\"5556787\",\"description\":\"Created by OpenIDM REST.\",\"password\":\"MyPassw0rd\"}" ^ + "http://localhost:8080/openidm/managed/user?_action=create" +---- +The output is essentially the same as the UNIX command output. + +OpenIDM creates the new user in the repository. If you configure a mapping to apply changes from the local repository to the XML file connector as a target, OpenIDM then updates the XML file to add the new user. + +You can also add users through the UI, which uses the OpenIDM REST API. When you have logged into the UI as the OpenIDM administrator, click Manage > User > New User. The process is straightforward. + + + +[#more-sample-8] +=== Logging Sample - Using Scripts to Generate Log Messages + +OpenIDM provides a `logger` object with `debug()`, `error()`, `info()`, `trace()`, and `warn()` functions that you can use to log messages to the OSGi console from your scripts. + +[#install-sample8] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for sample 8. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample8 +---- +The `sync.json` file in the `sample8/conf` directory includes brief examples of log messages. + + +[#run-sample8] +==== Running the Sample + +Run reconciliation over the REST interface. + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemXmlfileAccounts_managedUser&waitForCompletion=true" +---- +The reconciliation operation returns a reconciliation run ID, and the status of the operation. + +Note the log messages displayed in the OSGi console. The following example omits timestamps and so forth to show only the message strings. + +[source] +---- +-> +...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=[]},... +---- + + + +[#more-sample-9] +=== Workflow Sample - Demonstrating Asynchronous Reconciling Using a Workflow + +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 `conf/sync.json` file, which specifies that an `ABSENT` condition should launch the `managedUserApproval` workflow: + +[source, javascript] +---- +... + { + "situation" : "ABSENT", + "action" : { + "workflowName" : "managedUserApproval", + "type" : "text/javascript", + "file" : "workflow/triggerWorkflowFromSync.js" + } + }, + ... +---- +When the request is approved by an administrator, the absent users are created by an asynchronous reconciliation process. + +Prepare a fresh installation of OpenIDM before trying this sample. + +[#install-sample9] +==== Install the Sample + +Prepare OpenIDM as described in xref:chap-overview.adoc#preparing-openidm["Preparing OpenIDM"], then start OpenIDM with the configuration for sample 9. + +[source, console] +---- +$ cd /path/to/openidm +$ ./startup.sh -p samples/sample9 +---- + + +[#run-sample9] +==== Running the Sample + + +. Run reconciliation over the REST interface. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request POST \ + "http://localhost:8080/openidm/recon?_action=recon&mapping=systemXmlfileAccounts_managedUser&waitForCompletion=true" +---- ++ +The reconciliation operation returns a reconciliation run ID, and the status of the operation. ++ +Reconciliation starts an approval workflow for each ABSENT user. These approval workflows (named `managedUserApproval`) wait for the request to be approved by an administrator. + +. Query the invoked workflow task instances over REST. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/workflow/taskinstance?_queryId=query-all-ids" +---- ++ +In this case, the request returns two workflow results, each with a process ID (`_id`) as well as a process definition ID. You will use the value of the `_id` shortly. ++ + +[source, console] +---- +{ + "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 +} +---- + +. Approve the requests over REST, by setting the `"requestApproved"` parameter for the specified task instance to `"true"`. Note the use of one of the values of `_id` in the REST call, in this case, `124`. ++ +On UNIX: ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{"requestApproved": "true"}' \ + "http://localhost:8080/openidm/workflow/taskinstance/124?_action=complete" +---- ++ +On Windows: ++ + +[source, console] +---- +$ curl ^ + --header "X-OpenIDM-Username: openidm-admin" ^ + --header "X-OpenIDM-Password: openidm-admin" ^ + --header "Content-Type: application/json" ^ + --request POST ^ + --data "{\"requestApproved\": \"true\"}" ^ + "http://localhost:8080/openidm/workflow/taskinstance/124?_action=complete" +---- ++ +A successful call returns the following: ++ + +[source, console] +---- +{"Task action performed":"complete"} +---- + +. Once the request has been approved, an asynchronous reconciliation operation runs, which creates the users whose accounts were approved in the previous step. ++ +List the users that were created by the asynchronous reconciliation. ++ + +[source, console] +---- +$ curl \ + --header "X-OpenIDM-Username: openidm-admin" \ + --header "X-OpenIDM-Password: openidm-admin" \ + --request GET \ + "http://localhost:8080/openidm/managed/user?_queryId=query-all-ids" +---- ++ +One user is returned. ++ + +[source, console] +---- +{ + "result": [ { + "_rev": "0", + "_id": "1" + } ], +... +} +---- + + + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/index.adoc b/openidm-doc/src/main/asciidoc/samples-guide/index.adoc new file mode 100644 index 000000000..469a0ddaa --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/index.adoc @@ -0,0 +1,48 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + += Samples Guide +:doctype: book +:toc: +:authors: Lana Frost, Mike Jang +:copyright: Copyright 2011-2016 ForgeRock AS. +:copyright: Portions Copyright 2024 3A Systems LLC. + +:imagesdir: ../ +:figure-caption!: +:example-caption!: +:table-caption!: +[abstract] +This guide provides a number of "sample deployments" that walk you through the essential OpenIDM features, as they would be implemented. + +include::./preface.adoc[] +include::./chap-overview.adoc[] +include::./chap-xml-samples.adoc[] +include::./chap-ldap-samples.adoc[] +include::./chap-groovy-samples.adoc[] +include::./chap-powershell-samples.adoc[] +include::./chap-audit-sample.adoc[] +include::./chap-roles-sample.adoc[] +include::./chap-multiaccount-sample.adoc[] +include::./chap-trustedfilter-sample.adoc[] +include::./chap-fullstack-sample.adoc[] +include::./chap-workflow-samples.adoc[] +include::./chap-google-sample.adoc[] +include::./chap-salesforce-sample.adoc[] +include::./chap-kerberos-sample.adoc[] +include::./chap-endpoint-sample.adoc[] +include::./openidm-glossary.adoc[] diff --git a/openidm-doc/src/main/asciidoc/samples-guide/openidm-glossary.adoc b/openidm-doc/src/main/asciidoc/samples-guide/openidm-glossary.adoc new file mode 100644 index 000000000..d8bba1dff --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/openidm-glossary.adoc @@ -0,0 +1,80 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[glossary] +[#openidm-glossary] +== OpenIDM Glossary + + +correlation query:: +A correlation query specifies an expression that matches existing entries in a source repository to one or more entries on a target repository. While a correlation query may be built with a script, it is __not__ a correlation script. + ++ +As noted in xref:../integrators-guide/chap-synchronization.adoc#correlation["Correlating Source Objects With Existing Target Objects"] in the __Integrator's Guide__, you can set up a query definition, such as`_queryId`,`_queryFilter`, or`_queryExpression`, possibly with the help of a`linkQualifier`. + +correlation script:: +A correlation script matches existing entries in a source repository, and returns the IDs of one or more matching entries on a target repository. While it skips the intermediate step associated with a`correlation query`, a correlation script can be relatively complex, based on the operations of the script. + +entitlement:: +An entitlement is a collection of attributes that can be added to a user entry via roles. As such, it is a specialized type of `assignment`. A user or device with an entitlement gets access rights to specified resources. An entitlement is a property of a managed object. + +JSON:: +JavaScript Object Notation, a lightweight data interchange format based on a subset of JavaScript syntax. For more information, see the link:http://www.json.org[JSON, window=\_blank] site. + +JWT:: +JSON Web Token. As noted in the link:http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html[JSON Web Token draft IETF Memo, window=\_blank], "JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties." For OpenIDM, the JWT is associated with the `JWT_SESSION` authentication module. + +managed object:: +An object that represents the identity-related data managed by OpenIDM. Managed objects are configurable, JSON-based data structures that OpenIDM stores in its pluggable repository. The default configuration of a managed object is that of a user, but you can define any kind of managed object, for example, groups or roles. + +mapping:: +A policy that is defined between a source object and a target object during reconciliation or synchronization. A mapping can also define a trigger for validation, customization, filtering, and transformation of source and target objects. + +OSGi:: +A module system and service platform for the Java programming language that implements a complete and dynamic component model. For a good introduction, see the link:https://www.osgi.org//developer/benefits-of-using-osgi[OSGi, window=\_blank] site. While OpenIDM services are designed to run in any OSGi container, currently only link:http://felix.apache.org/[Apache Felix, window=\_blank] is supported. + +reconciliation:: +During reconciliation, comparisons are made between managed objects and objects on source or target systems. Reconciliation can result in one or more specified actions, including, but not limited to, synchronization. + +resource:: +An external system, database, directory server, or other source of identity data to be managed and audited by the identity management system. + +[#gloss-rest] +REST:: +Representational State Transfer. A software architecture style for exposing resources, using the technologies and protocols of the World Wide Web. REST describes how distributed data objects, or resources, can be defined and addressed. + +role:: +OpenIDM includes two different types of provisioning roles and authorization roles. For more information, see xref:../integrators-guide/chap-users-groups-roles.adoc#working-with-managed-roles["Working With Managed Roles"] in the __Integrator's Guide__. + +source object:: +In the context of reconciliation, a source object is a data object on the source system, that OpenIDM scans before attempting to find a corresponding object on the target system. Depending on the defined mapping, OpenIDM then adjusts the object on the target system (target object). + +synchronization:: +The synchronization process creates, updates, or deletes objects on a target system, based on the defined mappings from the source system. Synchronization can be scheduled or on demand. + +system object:: +A pluggable representation of an object on an external system. For example, a user entry that is stored in an external LDAP directory is represented as a system object in OpenIDM for the period during which OpenIDM requires access to that entry.System objects follow the same RESTful resource-based design principles as managed objects. + +target object:: +In the context of reconciliation, a target object is a data object on the target system, that OpenIDM scans after locating its corresponding object on the source system. Depending on the defined mapping, OpenIDM then adjusts the target object to match the corresponding source object. + + diff --git a/openidm-doc/src/main/asciidoc/samples-guide/preface.adoc b/openidm-doc/src/main/asciidoc/samples-guide/preface.adoc new file mode 100644 index 000000000..c534c4115 --- /dev/null +++ b/openidm-doc/src/main/asciidoc/samples-guide/preface.adoc @@ -0,0 +1,44 @@ +//// + The contents of this file are subject to the terms of the Common Development and + Distribution License (the License). You may not use this file except in compliance with the + License. + + You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + specific language governing permission and limitations under the License. + + When distributing Covered Software, include this CDDL Header Notice in each file and include + the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + Header, with the fields enclosed by brackets [] replaced by your own identifying + information: "Portions copyright [year] [name of copyright owner]". + + Copyright 2017 ForgeRock AS. + Portions Copyright 2024 3A Systems LLC. +//// + +:figure-caption!: +:example-caption!: +:table-caption!: + + +[preface] +[#preface] +== Preface + +This guide describes a number of sample deployments that demonstrate the core functionality of OpenIDM. The samples correspond to the configurations provided in the `openidm/samples` directory. + +[#d5830e168] +=== Who Should Use This Guide + +This guide is written for anyone testing OpenIDM to manage identities, and to ensure compliance with identity management regulations. + +The guide covers a number OpenIDM features, often including multiple features in a single sample. + +You do not need to be an OpenIDM wizard to learn something from this guide, although a background in identity management and maintaining web application software can help. You do need some background in managing services on your operating systems and in your application servers. You can nevertheless get started with this guide, and then learn more as you go along. + +include::../partials/sec-formatting-conventions.adoc[] + +include::../partials/sec-accessing-doc-online.adoc[] + +include::../partials/sec-joining-the-community.adoc[] + +include::../partials/sec-support-contact.adoc[]