Contents
- Architectural Overview
- Configuration Options
- Configure Server Logs
- Connect to External Resources
- Configuring Synchronization
- Scheduling Synchronization
- Managing Passwords
- Managing Authentication, Authorization & RBAC
- Authentication
- Securing & Hardening OpenIDM
- Integrating Business Processes & Workflows
- Sending Email
- Errors
- References
Architectural Overview
Modular Framework
* OSGi (Felix)
* Servlet (Jetty)
Infrastructure Modules
* Scheduler (Quartz)
* Script engine (JavaScript)
* Audit logging
* Repository
– MySQL for production use
– OrientDB for evaluation use
– Repository API is based on JSON object model and RESTful Web Services
Core Services
Object model
* Java based object model
* Java object model represents of JSON object model
* Also exposes a set of triggers and functions for scripting hookups
Managed objects
* They are identity related data managed by OpenIDM
* Configurable, JSON based data structure living the repository
* Default configuration of a manged object is that of a user
* Can be access via /openidm/managed/ context
curl --header "X-OpenIDM-Username: openidm-admin" --header "X-OpenIDM-Password: openidm-admin" --request GET "http://localhost:8080/openidm/managed/..."
System Objects
* They are pluggable representations of objects on external systems, e.g. a user entry stored in external LDAP.
* Can be access via /openidm/system/ context
curl --header "X-OpenIDM-Username: openidm-admin" --header "X-OpenIDM-Password: openidm-admin" --request GET "http://localhost:8080/openidm/system/..."
Mappings
* Mappings define policies between source and target objects and their attributes during synchronization and reconciliation
* Can also define triggers for validation, customization, filtering, and transformation of source and target objects
Synchronization and Reconciliation
Access Layer
* Provides UI and public APIs (RESTful) for accessing and managing OpenIDM repository and its functions
Configuration Options
Configuration Objects
* Exposed as JSON objects
* Can be either single instance (one per installation) or multiple instances (more than one per installation)
Single Instance Configuration Objects
* audit: specifies how audit events are logged
* authentication: controls REST access
* managed: defines managed objects and their schemas
* repo.repo-type: configures internal repository, e.g. repo.orientdb or repo.jdbc
* router: specifies filters for specific operations
* sync: defines all sync and reconciliation mappings
Multiple Instance Configuration Objects
* Naming: objectname/instancename, e.g. provisioner.openicf/xml
* JSON file views are named: objectname-instancename.json, e.g. provisioner.openicf-xml.json
Configuration Over REST
* Configuration objects are exposed under /openidm/config context
* Single instance objects are under /openidm/config/objectname context
* Multiple instance objects are under /openidm/config/objectname/instancename context
# List all configuration objects: curl --request GET \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" http://localhost:8080/openidm/config # List single instance audit configuration object: curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" http://localhost:8080/openidm/config/audit # List multiple instance configuration object: curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" http://localhost:8080/openidm/config/provisioner.openicf/xml
Property Substitution in Configuration
* Define properties in conf/boot.properties. For example:
PROD.location=production DEV.location=development
* Use Property in configuration files with &{} construct. For example, repo.orientdb.json:
{ "dbUrl" : "local:./db/&{&{environment}.location}-openidm", "user" : "admin", "poolMinSize" : 5, "poolMaxSize" : 20, ... }
* Property “environment” is further defined in environment variables:
# For DEV environment: export OPENIDM_OPTS="-Xmx1024m -Denvironment=PROD" ./startup.sh # For PROD environment: export OPENIDM_OPTS="-Xmx1024m -Denvironment=DEV" ./startup.sh
* Java system properties can also be used. For example:
{ "logTo" : [ { "logType" : "csv", "location" : "&{user.home}/audit", "recordDelimiter" : ";" } ] }
* Also supports nested properties
* Does not support encrypted values at this time
Configure Server Logs
* Server logging can be configured in openidm/conf/logging.properties
* Logging levels
SEVERE WARNING INFO CONFIG FINE FINER FINEST
* Set logging level in individual script:
org.forgerock.openidm.script.javascript.JavaScript.level=level
* Override log level defined in individual script:
org.forgerock.openidm.script.javascript.JavaScript.script-name.level=level
Connect to External Resources
* Resources are
– external systems
– databases
– directory servers
– other sources of identity data
to be managed and audited by an idM
* OpenIDM connects to external resources through OpenICF which can be either standalone or embedded
* Connectors are configured through files named openidm/conf/provisioner.openicf-connectorname
* Sample connectors are located in openidm/samples/provisioners directory (copy them to conf directory to use)
Accessing Remote Connectors
* Configure remote connectors in conf/provisioner.openicf.connectorinfoprovider.json
* See sample file in conf/provisioner directory
cat provisioner.openicf.connectorinfoprovider.jsn { "connectorsLocation" : "connectors", "remoteConnectorServers" : [ { "name" : "dotnet", "host" : "127.0.0.1", "port" : 8759, "useSSL" : false, "timeout" : 0, "key" : "Passw0rd" } ] }
Configure Connectors
* Via OpenICF provisioner service
* Each connector configuration is stored in a file in the conf directory named provisioner.openicf-connectorname
Connector Configuration Examples
Configuring Synchronization
Types of Synchronization
* Synchronization happens when
– OpenIDM receives a change directly: OpenIDM pushes changes immediately to all external resources
– OpenIDM discovers a change on an external resource: through reconciliation and LiveSync
Reconciliation
* Bidirectional synchronization of objects (mainly user objects) between different data stores
* Thorough and heavy weight
* Good for compliance and reports
LiveSync
* Relies on change log on the external resource (e.g. OpenDJ and AD) to determine which object has changed
* Light weight
* Might miss some changes
Flexible Data Model
Basic Data Flow Configuration
* Elements involved:
– Three types of configuration files
– A link table that OpenIDM maintains in its repository
– Scripts to check objects and manipulate attributes
Connector Configuration Files
* Maps external resource objects to OpenIDM objects
* Lives in conf directory
* Naming convention is provisioner.resource-name.json, e.g. provisioner.openicf-xml.json
* Mapping naming conventions:
– nativeName: external attribute name
– nativeType: external attribute type
{ "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" } } } }
Synchronization Mappings File
* Single file: conf/sync.json
* Describes a set of mappings
– Each mapping specifies attribute mapping from source to target objects (i.e. directional from source to target)
– External objects are identified as system/name/object-type
* By default, synchronize all objects matching those defined in the connector config file for the resource
* Can also do
– creating new attribute
– conditional sync
– transformation
{ "mappings": [ { "name": "systemLdapAccounts_managedUser", "source": "system/MyLDAP/account", "target": "managed/user", "properties": [ { "target": "familyName", "source": "lastName" }, { "target": "homePhone", "source": "homePhone" }, /* Creates a new attribute on the target name phoneExtension with default value of 0047 */ { "target": "phoneExtension", "default": "0047" }, /* Sync conditionally (only if source email is not null) */ { "target": "mail", "comment": "Set mail if non-empty.", "source": "email", "condition": { "type": "text/javascript", "source": "(source.email != null)" } }, /* Create a new target attribute named displayName */ { "target": "displayName", "source": ""; "transform": { "type": "text/javascript", "source": "(source.lastName +', ' + source.firstName;)" } } ] } ] }
* Filters to determine if source of target is valid to be mapped
– validSource
{ "validSource": { "type": "text/javascript", "source": "source.ldapPassword != null" } }
– validTarget
{ "validTarget": { "type": "text/javascript", "source": "target.employeeType == 'internal'" } }
Scheduling Synchronization
Managing Passwords
Enforcing Password Policies
* By applying validation rules to attributes of managed user objects
* For example, to exclude the use of password strings: ‘password’,’123456′,’12345678′, ‘qwerty’, ‘abc123’:
– In conf/managed.json file:
{ "objects" : [ { "name" : "user", "properties" : [ { "name" : "password", "encryption" : { "key" : "openidm-sym-default" } } ], "onValidate" : { "type" : "text/javascript", "file" : "script/password-validator.js" } } ] }
– Password validation file (script/password-validator.js)
const dictionary = ['password','123456','12345678', 'qwerty', 'abc123']; function isValidPassword() { var cleartextObject = openidm.decrypt(object); for (var i = 0; i < dictionary.length; i++) { if (cleartextObject.password == dictionary[i]) { throw "Password Policy Violation Exception"; }; }; }; isValidPassword();
Password Synchronization
Managing Authentication, Authorization & RBAC
OpenIDM Users
Internal Users
* anonymous: for self registration. Default password is anonymous
* openidm-admin: super user. Default password is openidm-admin
Managed Users
* Users managed by OpenIDM
Authentication
Default Attributes
* login: email
* password: password
* Default attributes are defined in conf/repo.repotype.json file, e.g. repo.orientdb.json
– credential-internaluser-query
– credential-query
"credential-query" : "SELECT * FROM ${_resource} WHERE userName = '${username}'", "credential-internaluser-query" : "SELECT * FROM internal_user WHERE _openidm_id = ${username}",
* Authentication file conf/authentication.json defines currently active query
cat authentication.json { "queryId" : "credential-query", "queryOnResource" : "managed/user", "propertyMapping" : { "userId" : "_id", "userCredential" : "password", "userRoles" : "roles" }, "defaultUserRoles" : [ ] }
Authentication
* With standard header fields:
curl --user userName:password
* With OpenIDM header fields:
curl --header "X-OpenIDM-Username: openidm-admin" --header "X-OpenIDM-Password: openidm-admin"
Roles
* openidm-reg: anonymous user
* openidm-admin: super user
* openidm-authorized: authenticated users.
– Configured by defaultUserRoles property in the conf/authentication.json file:
cat authentication.json { "queryId" : "credential-query", "queryOnResource" : "managed/user", "propertyMapping" : { "userId" : "_id", "userCredential" : "password", "userRoles" : "roles" }, "defaultUserRoles" : [ ] }
* openidm-cert: authenticated by mutual SSL authentication
Authorization
* Based on REST interface URLs
* Defined in script/router-authz.js file
– Return “Access denied” to deny access:
if (!allow()) { throw "Access denied"; }
Securing & Hardening OpenIDM
TODO
Integrating Business Processes & Workflows
* Two modes of integration
– Local integration: Activiti is embedded in OpenIDM OSGI container
– Remote integration: Standalone Activiti engine
Remote Integration
Checkout Trunk
* Checkout OpenIDM trunk from SVN URL: https://svn.forgerock.org/openidm/trunk
Compile and Package Source Codes
* Install Maven 3 if not already done
* Run Maven command:
mvn package
Install OpenIDM
* Copy openidm-zip/target/openidm-2.1.0-SNAPSHOT to target machine
* Unzip
Install Activiti
* Download and unzip activiti-5.10.zip
* Change Activiti listening port from 8080 to 9090
– Edit setup/build.xml and do a global replacement of 8080 to 9090 with vi command: :%s/8080/9090/g
* Start Activiti demo app:
ant demo.start
* Access Activiti Explorer from URL: http://localhost:9090/activiti-explorer
Deploy OpenIDM Workflow to Activiti Tomcat
* Stop Tomcat
ant tomcat.stop
* Deploy openidm-workflow-remote web app to Tomcat by copying openidm-workflow-remote/target/openidm-workflow-remote-2.1.0-SNAPSHOT.war to Tomcat webapps directory:
$ cd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/webapps $ pwd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/webapps $ ls activiti-explorer activiti-rest docs examples host-manager manager ROOT $ cp /mnt/hgfs/vmshare/src/openidm-workflow-remote-2.1.0-SNAPSHOT.war .
* Deploy openidm workflow activiti jar file to activiti-explorer web app’s WEB-INF/lib directory
$ cd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/webapps/activiti-explorer/WEB-INF/lib $ pwd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/webapps/activiti-explorer/WEB-INF/lib $ cp /mnt/hgfs/vmshare/src/openidm-workflow-activiti-2.1.0-SNAPSHOT-jar-with-dependencies.jar .
* Edit Activiti Explorer config file to be able to use OpenIDM extenstions
$ cd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/webapps/activiti-explorer/WEB-INF $ pwd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/webapps/activiti-explorer/WEB-INF $ ls activiti-ui-context.xml applicationContext.xml classes lib web.xml $ vi applicationContext.xml
– Replace
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="jobExecutorActivate" value="true" /> <property name="customFormTypes"> <list> <ref bean="userFormType"/> </list> </property> </bean>
with
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="mailServerHost" value="mail.my.com" /> <property name="mailServerPort" value="26" /> <property name="mailServerUsername" value="me@my.com" /> <property name="mailServerPassword" value="secret1" /> <property name="mailServerDefaultFrom" value="activiti@my.com" /> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="jobExecutorActivate" value="true" /> <property name="customFormTypes"> <list> <ref bean="userFormType"/> </list> </property> <property name="customSessionFactories"> <list> <bean class="org.forgerock.openidm.workflow.activiti.impl.session.OpenIDMSessionFactory"> <property name="url" value="http://localhost:8080/openidm/"/> <property name="user" value="openidm-admin"/> <property name="password" value="openidm-admin"/> </bean> </list> </property> <property name="resolverFactories"> <list> <bean class="org.forgerock.openidm.workflow.activiti.impl.OpenIDMResolverFactory"></bean> <bean class="org.activiti.engine.impl.scripting.VariableScopeResolverFactory"></bean> <bean class="org.activiti.engine.impl.scripting.BeansResolverFactory"></bean> </list> </property> <property name="expressionManager"> <bean class="org.forgerock.openidm.workflow.activiti.impl.OpenIDMExpressionManager"> </bean> </property> </bean>
* Restart Activiti Demo:
cd $ACTIVITI_HOME/setup ant demo.stop ant demo.start
* Check that Activiti Explorer is accessible.
Configure OpenIDM to use Remote Activiti
* Copy sample workflow.json file to conf directory if none exists:
cd $OPENIDM_HOME cp samples/misc/workflow.json conf/
* Edit workflow.json file to point to remote Activiti instance with correct username and password (i.e. kermit/kermit)
{ "enabled" : true, "location" : "remote", "engine" : { "url" : "http://localhost:9090/openidm-workflow-remote-2.1.0-SNAPSHOT/", "username" : "kermit", "password" : "kermit" },
* Start OpenIDM
cd $OPENIDM_HOME ./startup.sh
* Test integration:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --request GET "http://localhost:8080/openidm/workflow" {"result":[{"name":"Vacation request","processDefinitionId":"vacationRequest:1:21","key":"vacationRequest"},{"name":"Helpdesk process","processDefinitionId":"escalationExample:1:22","key":"escalationExample"},{"name":"Review sales lead","processDefinitionId":"reviewSaledLead:1:23","key":"reviewSaledLead"},{"name":"Fix system failure","processDefinitionId":"fixSystemFailure:1:24","key":"fixSystemFailure"},{"name":"Expense process","processDefinitionId":"adhoc_Expense_process:1:25","key":"adhoc_Expense_process"}]}
Install the sample workflow (example.bpmn20.xml) from the openidm-workflow-activiti project
* Login remote Activiti Explorer as kermit/kermit
* Select Manage > Deployment > Upload New
* Browse to file openidm-workflow-activiti/src/main/resources/OSGI-INF/activiti/example.bpmn20.xml
* Start osgiProcess from REST API:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --request POST "http://localhost:8080/openidm/workflow/processinstance?_action=createProcessInstance" \ --data '{ "key":"osgiProcess" }' {"_id":"1010","processInstanceId":"1010","status":"ended","businessKey":null,"processDefinitionId":"osgiProcess:1:915"}
– Also check Activiti Tomcat catalina.out for osgiProcess related outputs:
$ pwd /opt/openidm/builtFromSrc/activiti-5.10/apps/apache-tomcat-6.0.32/logs $ vi catalina.out script task using expression resolver: [result:[[name:Vacation request, processDefinitionId:vacationRequest:1:21, key:vacationRequest], [name:Helpdesk process, processDefinitionId:escalationExample:1:22, key:escalationExample], [name:Review sales lead, processDefinitionId:reviewSaledLead:1:23, key:reviewSaledLead], [name:Fix system failure, processDefinitionId:fixSystemFailure:1:24, key:fixSystemFailure], [name:Expense process, processDefinitionId:adhoc_Expense_process:1:25, key:adhoc_Expense_process], [name:Osgi process, processDefinitionId:osgiProcess:1:915, key:osgiProcess]]]
* Alternatively, start osgiProcess from Activiti Explorer:
– Check Activiti Tomcat catalina.out for osgiProcess related outputs:
Sep 18, 2012 1:07:26 PM org.restlet.engine.log.LogFilter afterHandle INFO: 2012-09-18 13:07:26 127.0.0.1 kermit 127.0.0.1 9090 GET /openidm-workflow-remote-2.1.0-SNAPSHOT/ - 200 -0 8 http://localhost:9090 Restlet-Framework/2.0.15 - script task using expression resolver: [result:[[name:Vacation request, processDefinitionId:vacationRequest:1:21, key:vacationRequest], [name:Helpdesk process, processDefinitionId:escalationExample:1:22, key:escalationExample], [name:Review sales lead, processDefinitionId:reviewSaledLead:1:23, key:reviewSaledLead], [name:Fix system failure, processDefinitionId:fixSystemFailure:1:24, key:fixSystemFailure], [name:Expense process, processDefinitionId:adhoc_Expense_process:1:25, key:adhoc_Expense_process], [name:Osgi process, processDefinitionId:osgiProcess:1:915, key:osgiProcess]]] Sep 18, 2012 1:07:26 PM org.restlet.engine.log.LogFilter afterHandle INFO: 2012-09-18 13:07:26 127.0.0.1 kermit 127.0.0.1 9090 GET /openidm-workflow-remote-2.1.0-SNAPSHOT/ - 200 -0 15 http://localhost:9090 Restlet-Framework/2.0.15 - script task using resolver: [result:[[name:Vacation request, processDefinitionId:vacationRequest:1:21, key:vacationRequest], [name:Helpdesk process, processDefinitionId:escalationExample:1:22, key:escalationExample], [name:Review sales lead, processDefinitionId:reviewSaledLead:1:23, key:reviewSaledLead], [name:Fix system failure, processDefinitionId:fixSystemFailure:1:24, key:fixSystemFailure], [name:Expense process, processDefinitionId:adhoc_Expense_process:1:25, key:adhoc_Expense_process], [name:Osgi process, processDefinitionId:osgiProcess:1:915, key:osgiProcess]]]
Local Activiti Integration
* Local Activiti integration is included in the standard OpenIDM 2.1 build without Activiti Explorer.
* To access Activiti Explorer with Local Activiti Engine, see this post.
* To use Single User Store for both OpenIDM and Activiti, see this post.
Configure Activiti Engine
* Configured in conf/workflow.json file:
– For local embedded Activiti engine:
cat workflow.json { "enabled" : true }
– For remote standalonoe Activiti instance:
{ "enabled" : true, "location" : "remote", "engine" : { "url" : "http://localhost:9090/openidm-workflow-remote-2.1.0-SNAPSHOT/", "username" : "kermit", "password" : "kermit" },
Define Activiti Workflows
* Define BPMN 2.0 work flow file (with text editor or BPMN 2.0 editor)
* Package as a .bar file
* Copy bar file to workflow directory
* Invoke workflow using a script
* Optionally schedule workflow
Invoking Activiti Workflows
* From any trigger point within OpenIDM
* From script files using openidm.action() function
/* * Calling 'myWorkflow' workflow */ var workflow = { "_action" : "myWorkflow" }; var params = { "foo" : "bar" }; openidm.action("workflow/activiti", workflow, params);
* Directly from REST interface
curl --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --data '{"foo":"bar"}' \ --request POST "http://localhost:8080/openidm/workflow?_action=myWorkflow"
Email Notification Example
* Create EmailNotification.bpmn file
* Package as .bar file
* Copy .bar file to workflow directory
* Invoke directly from REST
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --request POST "http://localhost:8080/openidm/workflow/processinstance?_action=createProcessInstance" \ --data '{"key":"EmailNotification","fromSender" : "noreply@openidm","toEmail" : "jdoe@example.com"}' {"_id":"208","processInstanceId":"208","status":"ended","businessKey":null,"processDefinitionId":"EmailNotification:1:207"}
Example Sunset Workflow Triggered By Reconciliation
* Authoritative data source: CSV file
"firstName","uid","lastName","email","employeeNumber",password,"sunrise","sunset","active" "Darth","DDOE","Doe","doe@example.com","123456","Z29vZA==","2012-06-30T00:00:00Z","2012-12-23T00:00:00Z","TRUE"
* Target data source: XML file
* Scenario:
New user in CSV -> Kicks of reconcile process -> Found new user -> Add to XML
Sending Email
Setup Outbound Email
* Shutdown OpenIDM
* Copy sample email config file to conf directory:
cd $OPENIDM_HOME cp samples/misc/external.email.json conf/
* Modify email config file:
{ "host" : "smtp.example.com", "port" : "25", "username" : "openidm", "password" : "secret12", "mail.smtp.auth" : "true", "mail.smtp.starttls.enable" : "true" }
* Restart OpenIDM
* Check external mail is up and running
-> scr list ... [ 6] [active ] org.forgerock.openidm.external.email ...
Send mail from REST
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --request POST "http://localhost:8080/openidm/external/email?_from=openidm@example.com&_to=user@example.com&_subject=Test&_body=Test" {"status":"OK"}
Sending Mail from Script
* Use external/email context:
var params = new Object(); params._from = "openidm@example.com"; params._to = "admin@example.com"; params._cc = "wally@example.com,dilbert@example.com"; params._subject = "OpenIDM recon report"; params._type = "text/html"; params._body = "<html><body><p>Recon report follows...</p></body></html>"; openidm.action("external/email", params);
Errors
action method not implemented on workflow
* Error message:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --request POST "http://localhost:8080/openidm/workflow?_action=osgiProcess" curl \ > --header "X-OpenIDM-Username: openidm-admin" \ > --header "X-OpenIDM-Password: openidm-admin" \ > --request POST "http://localhost:8080/openidm/workflow?_action=osgiProcess" {"error":400,"reason":"Bad Request","detail":"action method not implemented on workflow"}
* Reason: OpenIDM/Activiti REST interface has changed.
References
* OpenIDM 2.1.0 Integrator’s Guide
* ForgeRock Documentation
* OpenIDM Wiki
* Forum
* Enhanced REST interface for the Activiti integration
One Response to OpenIDM 2.1 Integration Guide