Property value substitution
Property value substitution lets you achieve the following:
-
Define a configuration that is specific to a single 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.
Property value substitution uses configuration expressions to introduce variables into the server configuration. You set configuration expressions as the values of configuration properties. The effective property values can be evaluated in a number of ways. For more information about property evaluation, refer to Expression Resolvers.
Configuration expressions have the following characteristics:
-
To distinguish them from static values, configuration expressions are preceded by an ampersand and enclosed in braces. For example:
&{openidm.port.http}
. The configuration token in the example isopenidm.port.http
. The.
serves as the separator character. -
You can use a default value in a configuration expression by including it after a vertical bar following the token.
For example, the following expression sets the default HTTP port value to 8080:
&{openidm.port.http|8080}
.With this configuration, the server attempts to substitute
openidm.port.http
with a defined configuration token. If no token definition is found, the server uses the default value,8080
. -
A configuration property can include a mix of static values and expressions.
For example, suppose
hostname
is set tods
. Then,&{hostname}.example.com
evaluates tods.example.com
. -
Configuration token evaluation is recursive.
For example, suppose
port
is set to&{port.prefix}389
, andport.prefix
is set to2
. Then&{port}
evaluates to2389
.
You can define nested properties (that is a property definition within another property definition) and you can combine system properties, boot properties, and environment variables.
Property substitution is not available for any configuration not processed by the IDM backend, such as |
Expression resolvers
At server startup, expression resolvers evaluate property values to determine the effective configuration. You must define expression values before you start the IDM server that uses them.
When configuration tokens are resolved, the result is always a string. However, you can coerce the output type of the evaluated token to match the type that is required by the property. Ultimately, the expression must return the appropriate data type for the configuration property. For example, the port
property takes an integer. If you set it using an expression, the result of the evaluated expression must be an integer. If the type is wrong, the server fails to start due to a syntax error. For more information about data type coercion, refer to Transforming Data Types.
Expression resolvers can obtain values from the following sources:
Environment variables
You set an environment variable to hold the property value.
For example: export OPENIDM_PORT_HTTP=8080
.
The environment variable name must be composed of uppercase characters and underscores. The name maps to the expression token as follows:
-
Uppercase characters are converted to lowercase.
-
Underscores (
_
) are replaced with.
characters.
In other words, the value of OPENIDM_PORT_HTTP
replaces &{openidm.port.http}
in the server configuration.
Java system properties
You set a Java system property to hold the value.
Java system property names must match expression tokens exactly. In other words, the value of the openidm.repo.port
system property replaces &{openidm.repo.port}
in the server configuration.
Java system properties can be set in a number of ways. One way of setting system properties for IDM servers is to pass them through the OPENIDM_OPTS
environment variable.
For example: export OPENIDM_OPTS="-Dopenidm.repo.port=3306"
System properties can also be declared in your project’s conf/system.properties
.
This example uses property value substitution with a standard system property. The example modifies the audit configuration, changing the audit.json
file to redirect JSON audit logs to the user’s home directory. The user.home
property is a default Java System property:
"eventHandlers" : [
{
"class" : "org.forgerock.audit.handlers.json.JsonAuditEventHandler",
"config" : {
"name" : "json",
"logDirectory" : "&{user.home}/audit",
...
}
},
...
]
Expression files
You set a key in a .json
or .properties
file to hold the value. To use an expression file, set the IDM_ENVCONFIG_DIRS
environment variable, or the idm.envconfig.dirs
Java system property as described below. By default, IDM sets idm.envconfig.dirs
to &{idm.install.dir}/resolver/
.
The default property resolver file in IDM is resolver/boot.properties
but you can specify additional files that might hold property values.
Keys in .properties
files must match expression tokens exactly. In other words, the value of the openidm.repo.port
key replaces &{openidm.repo.port}
in the server configuration.
The following example expression properties file sets the repository port:
openidm.repo.port=1389
JSON expression files can contain nested objects.
JSON field names map to expression tokens as follows:
-
The JSON path name matches the expression token.
-
The
.
character serves as the JSON path separator character.
The following example JSON expression file uses property value substitution to set the host in the LDAP connector configuration:
{
"openidm" : {
"provisioner" : {
"ldap" : {
"host" : "ds.example.com"
}
}
}
}
}
To substitute this value in the configuration, the LDAP provisioner file would include the following:
{
...
"configurationProperties" : {
"host" : &{openidm.provisioner.ldap.host|localhost},
...
}
}
If the server does not find a configuration token for the host name, it substitutes the default (localhost
).
To use expression files, set the environment variable, IDM_ENVCONFIG_DIRS
, or the Java system property, idm.envconfig.dirs
, to a comma-separated list of the directories containing the expression files.
When reading these files, the server browses the directories in the order specified. It reads all the files with .json
and .properties
extensions, and attempts to use them to evaluate expression tokens.
For example, if you define idm.envconfig.dirs=/directory1,/directory2
and a configuration token is defined in both directory1
and directory2
, the resolved value will be the value defined in directory1
. If the configuration token is defined only in directory2
, the resolved value will be the value defined in directory2
.
Using expression files are subject to the following constraints:
|
Evaluation order of precedence
The following list displays the order of precedence, from greatest to least:
-
Environment variables override system properties, default token settings, and settings in expression files.
-
System properties override default token settings, and any settings in expression files.
-
Default token settings.
-
If
IDM_ENVCONFIG_DIRS
oridm.envconfig.dirs
is set, the server uses the settings found in expression files. -
Framework configuration properties.
-
Hardcoded property values.
-
Properties passed to the startup script with options such as: -P, -w, and -s.
Transforming data types
When configuration tokens are resolved, the result is always a string. However, you can transform or coerce the output type of the evaluated token to match the type that is required by the property.
You transform a property’s data type by setting $type
before the property value.
The following coercion types are supported:
-
array (
$array
) -
boolean (
$bool
) -
decodeBase64 (
$base64:decode
)Transforms a base64-encoded string into a decoded string.
-
encodeBase64 (
$base64:encode
)Transforms a string into a base64-encoded string.
-
integer (
$int
) -
list (
$list
) -
number (
$number
)This type can coerce integers, doubles, longs, and floats.
-
object (
$object
)This type can coerce a JSON object such as an encrypted password.
Type Coercion to Integer
This example JSON expression file sets the value of the port in the LDAP connector configuration:
{
"openidm" : {
"provisioner" : {
"ldap" : {
"port" : 6389
}
}
}
}
When the expression is evaluated, the port is evaluated as a string
value, (which would cause an error). To coerce the port value to an integer, substitute the value in the LDAP provisioner file as follows:
{
...
"configurationProperties" : {
"port" : {
"$int" : "&{openidm.provisioner.ldap.port|1389}",
},
...
}
}
With this configuration, the server evaluates the LDAP port property to the integer 6389
. If the server does not find a configuration token for the port, it substitutes the default (1389
).
Type Coercion to Array
This example JSON expression file sets a value for the failover servers in an LDAP connector configuration:
"openidm" : {
"provisioner" : {
"ldap" : {
"failover" : ["ldap://host1.domain.com:1389", "ldap://host2.domain.com:1389"]
}
}
}
When the expression is evaluated, the URLs would be evaluated as a single string
. To coerce the value to an array, substitute the value in the LDAP provisioner file as follows:
{
...
"configurationProperties" : {
"failover" : {
"$array":"&{openidm.provisioner.ldap.failover}"
},
...
}
}
If you set the failover URLs in a
|
Type Coercion From List to Array
The $list
function is similar to $array
, but lets you specify values in a .properties
file as a list of strings, separated by a comma (,
).
For example, you could list the LDAP failover servers in boot.properties
as follows:
openidm.provisioner.ldap.failover=ldap://host1.domain.com:1389,ldap://host2.domain.com:1389
To coerce the value to an array, your property definition in the LDAP provisioner file would be:
{
...
"configurationProperties" : {
"failover" : {
"$list":"&{openidm.provisioner.ldap.failover}",
},
...
}
}
This configuration would be converted to:
"openidm" : {
"provisioner" : {
"ldap" : {
"failover" : ["ldap://host1.domain.com:1389", "ldap://host2.domain.com:1389"]
}
}
}
Configuration property value storage
The values of configuration properties that are set explicitly (in conf/*.json
files) are stored in the repository. You can manage these configuration objects over REST or by using the JSON files themselves.
Properties that use value substitution are stored in the repository as variables. You store the value of each variable in .properties
files. You can use different .properties
files to change the configuration for multiple nodes in a cluster.
The following table shows how specific configuration properties can be set:
Variable | Description | Environment Variables | System Variables | boot.properties |
---|---|---|---|---|
|
Directory of files from unpacked IDM binary |
YES |
YES |
YES |
|
Working location directory |
YES |
YES |
YES |
|
Project directory with IDM configuration files |
YES |
YES |
YES |
|
Directory with environment files, including |
YES |
YES |
You can access configuration properties in scripts using identityServer.getProperty()
. For more information, refer to The identityServer Variable.
Limitations of property value substitution
Property value substitution is limited in the following areas:
In the admin UI
Support for property value substitution in the admin UI is limited to the following categories:
-
String substitution, where
&{some.property|DefaultValue}
-
Number and integer substitution, including:
-
"$number" : "&{openidm.port|1234}"
-
"$int" : "&{openidm.port|5678}"
-
-
Base64 substitution, such as:
"$base64:decode" : "&{openidm.felix.web.console.password|YWRtaW4=}"
-
Cryptographic substitution, where for passwords and client secrets, IDM substitutes
"********"
for$crypto
In connector configurations
You cannot use property substitution for connector reference (connectorRef
) properties. For example, the following configuration is not valid:
"connectorRef" : {
"connectorName" : "&{connectorName}",
"bundleName" : "org.forgerock.openicf.connectors.ldap-connector",
"bundleVersion" : "&{LDAP.BundleVersion}"
...
}
The connectorName
must be the precise string from the connector configuration. To specify multiple connector version numbers, use a range of versions. For example:
"connectorRef" : {
"connectorName" : "org.identityconnectors.ldap.LdapConnector",
"bundleName" : "org.forgerock.openicf.connectors.ldap-connector",
"bundleVersion" : "[1.5.0.0,2.0.0.0)",
...
}