Writing Lua scripts

This page explains how to integrate Lua scripts in Airlock IAM with the Input Value Providers and the Expected Output properties of the Scriptable Step plugin. For more information on Lua itself see The Programming Language Lua.

When programming a Lua script, the following configuration properties are available:

Property

Mandatory

Description

Input Value Providers

optional

List of value map providers that provide input values for the Lua script.

Script

mandatory

Source code of the Lua script

Expected Output

optional

List of keys and data types to return values to Airlock IAM.

Input Secrets

optional

Secrets provided to the Lua script.

Namespace

optional

Namespace for output values to segregate output from multiple Lua scripts.

The following screenshot gives an impression of a fully configured scriptable step:

IAM_Scriptable_Step

Output Map

A list of keys must be configured to return output values to Airlock IAM.

  • To integrate a script correctly, the following conditions must be met:
  • The script must return all mandatory keys.
  • The script cannot return undeclared keys.
  • The data types of the output values must match the declaration.
  • If no Output Map is configured, the return statement is optional.

If one of these conditions is not met, an error will result, and the flow will be terminated as failed.

To convert output values into a strongly typed value provider map in Airlock IAM, each key must be configured with its data types and whether this key is optional.

  • IAM currently supports the following data types for output values of scripts:
  • The output value must be a string (UTF-8 format).
  • Boolean: The output value must either be true or false.
    Binary digits are not allowed.
  • Number: The output value must be an integer (64-bit constraints).
    Floating point values are currently not supported.
  • Date-Time must be an integer-type Unix timestamp with millisecond precision.
  • The Date must be a valid date string in the format yyyy-MM-dd (e.g. 2021-06-04)

To use the examples below, the Output Map property must be configured with a Key full_name and a Script Output Declaration plugin with an Output Value Type property set to STRING.

Script

To successfully run a script in the scripting step plugin, at least the following function must be defined in the script property:

function iam_on_step_init ()
    <your code>
    return iam.output_map
end

The iam_on_step_init() function is called by IAM when starting the execution of a script.

Input Map

Input values are configured in the scriptable step plugin through value map providers. For this purpose, Airlock IAM provides a large number of plugins. Refer to the plugin documentation for further information on a particular value map provider.

All the examples in this text configure the Input Map property with a Context Data Map plugin. To read a context data item, the iam.input_map[<key>] table must be used in the script code.

The following example shows how a user's givenname and surname are concatenated into a property full_name. This property is then logged and finally returned to IAM for further use.

function iam_on_step_init ()
    iam.output_map = {}
    iam.output_map["full_name"] = iam.input_map["givenname"] .. " " .. iam.input_map["surname"]
    iam.log:info("User with full name: " .. iam.output_map["full_name"])
    return iam.output_map
end

If multiple value map providers contain a key with the same name, the value from the last value map provider will overwrite previous values.

Optional input values

When configuring value provider maps with optional values, it is the script's responsibility to check whether a particular value is present. In case of a missing value, the iam.input_map[<key>] will return the value nil.

The following code example will check if the key givenname is present or not:

if (iam.input_map["givenname"] ~= nil) then
   <logic if given name is present>
else
   <logic if given name is absent>
end

List all values provided to the script

The following code snippet will iterate over the table of input values provided to the script and output them to the IAM logfile:

for k, v in pairs(iam.input_map) do
    iam.log:info("key : " .. k .. ", value: " .. v)
end 

The following code snippet will achieve the same result, with less formatting:

 iam.log:info("Complete input map of GET request step: " .. iam.cjson.encode(iam.input_map))

Input Secrets

Use this property to provide sensitive values to a Lua script. Sensitive values are transferred to the Lua script as environment variables. In the script, these values can be accessed through the IAM API function local secret = iam.secrets:get(<secretId>).

Once made available within the Lua script, secrets are retrieved as plaintext. The script developer is responsible for ensuring that secrets are not leaked (e.g., not logged).

The secretId can only consist of alphanumeric and underscore characters.

Namespaces

Namespaces are optional. They are used to assign a name to the value map from the output of a script. This may be necessary if multiple scripts in the same flow can return an output value with the same key.

If no Namespace is configured, the default namespace is used. If several steps return the same key into the default namespace, the value of the scripting step executed last will overwrite the previous value.

Lua API functions

The Lua scripting language provides several built-in functions and types.

The ‚Äčprint() function has been modified for Airlock IAM. It now writes messages to the appropriate Airlock IAM logfile with severity DEBUG.

All other built-in functions and types remain as implemented and documented by Lua.

Care should be taken when using the os object or functions like load(), loadfile(), or dofile(). These functions may have security-relevant side effects as they are run with the IAM user's permissions and can access processes and file system objects.

IAM API functions

Airlock IAM provides additional and IAM-specific functions for scripting with Lua:

Function

Description

iam.input_map[<key>]

This table contains all the key-value pairs provided to the script by configuring the Input Value Providers.

Use the key to access the value within the table.

If the value is not present, "nil" is returned.

Date-time values include the precise time of day and are represented as a Unix timestamp with millisecond precision.

Date values exclude the time, and are specified as strings in the format yyyy-MM-dd.

iam.log:debug(message)

iam.log:info(message)

iam.log:warn(message)

iam.log:error(message)

Use these functions to write messages to the Airlock IAM Log4J log files. The severity of the log message is determined by the function used in the script.

All log message output of a Lua script will be prefixed with LuaScriptInterpreter:

The iam.log function will convert values of data types other than a string to the closest representation of a string value. Tables will be converted to JSON. If a conversion is not possible, type and memory address will be logged (e.g. function: 0x55c4f3eeb220).

Logging uses the standard error output stream. If the script or a library used in the script directly writes to standard error, this will be interpreted as an error message with severity ERROR.

Data written to standard output will be ignored.

iam.secrets:get(<secretId>)

This function retrieves a value from secret storage and returns it as plaintext to the script.

The key and value of secrets are defined in the IAM configuration.

If the value is not present, "nil" is returned.

iam.cjson

This is an alias for the bundled lua-cjson library, which transforms lua tables into JSON strings.

It is used implicitly by other IAM API functions.

See Lua CJSON 2.1.0 Manual for more details.

Error handling

The Config Editor does not verify the correctness of the Lua script syntax or semantics during configuration activation.

The following behavior is to be expected:

Syntax error

Syntax errors will be reported before the scriptable step is executed. The step will fail, and an error will be reported in the IAM log.

Runtime error

Runtime errors will be reported if the error occurs during the execution of the scriptable step. The step will fail, and an error will be reported the IAM log.

We recommended testing custom scripts before they are deployed with Airlock IAM.

The scriptable step supports the following error codes:

Error code

Description

20

A syntax error is present in the script source.

21

A runtime error occurred.

This type of error is printed together with a Lua stack trace, showing the cause and location of the error.

22

The value returned by the iam_on_step_init() has an incorrect data type.

iam_on_step_init() always expects a result of data type table.

24

The value returned by the iam_on_step_init() function cannot be written to the IAM value map.

This error occurs when data (e.g., a function reference) inside the expected output cannot be converted to a string.

27

An error occurred when using the IAM API functions (e.g. trying to access a secret using an identifier that isn't a string).

126

This error occurs if the ‚Äčoperating system cannot execute the lua executable or if one of the shared libraries cannot be used.

-

If a script does not terminate within 30 seconds, the script is aborted, and the following message is written with severity ERROR to the logfile:

ScriptableStep: Scriptable Step failed: The process was interrupted due to timeout of the script.

Lua library bundles

Lua can be extended with libraries by placing the necessary files in the correct folder of the instance directory. The following table explains the types of libraries and the expected directory locations:

Library type

File extension

Directory

Libraries written in Lua itself and provided as source code.

.lua

instances/common/scripting/lua/share

or

instances/<instance>/scripting/lua/share

Libraries written in C/C++ and provided as shared object libraries.

.so

instances/common/scripting/lua/lib

or

instances/<instance>/scripting/lua/lib

Once a library is placed in the directory structure outlined above, it can be used in scripts with the require function of Lua.

When adding additional libraries, you must ensure that the libraries are compatible with the installed version of Lua. See the Lua compatibility matrix here: Scriptable step overview - an incubating feature.

Building libraries with Luarocks

The package manager for Lua is Luarocks. The package manager should be installed on a system running the same operating system as the target architecture of Airlock IAM. This is necessary to ensure that shared object libraries will work correctly on the IAM target operating system.

When installed and configured, additional packages can be built and installed locally using the following command:

./luarocks install --tree <install_path> <lib_name> <lib_version>

Parameter

Description

Example

<install_path>

The directory where Luarocks should place the library files.

Luarocks knows the different library bundles types and will automatically add the share or lib path segments.

Luarocks will also add directory levels to manage different Lua versions in parallel.

instances/common/scripting/lua

<lib_name>

The name of the library.

lua-curl

<lib_version>

The version of the library.

0.3.13-1