Scriptable step examples

The example presented is only useful to demonstrate the capabilities of the scriptable step and how it can interact with Airlock IAM. The example is limited because most useful use cases require either proprietary services or services behind a paywall.

The example implements a self-registration flow that verifies secrets with a REST API, registers the user with another REST API, and summarizes the success in an HTML-formatted message:

IAM_Scriptable_Step_Example_Flow

The template of this flow has been taken from the Demo Configuration bundled with Airlock IAM.

  1. This flow has been changed to show the following, scripting-related properties:
  2. A Scriptable Step implements a REST call to a 3rd party system that requires basic auth and returns success/failure status to Airlock IAM.
  3. An Acknowledge Message Step is displayed depending on the status output of the previous scriptable step. It will set a tag that forces all the following steps to be skipped in case of an error.
  4. A Scriptable Step implements a POST request to a 3rd party system to retrieve data for display.
  5. A Scriptable Step to generate an HTML snippet with the data from the previous step and to return this to Airlock IAM.
  6. An Acknowledge Message Step that displays the HTML snippet from the previous step.
  • To use the example as intended, you can simulate the following two situations:
  • If the user interactively chooses the same password as configured as secret in the first scriptable step, the self-registration flow will continue successfully.
  • If the user chooses a different password, the first scriptable step will fail and all the following steps will be skipped and the self-registration will not be successful.

Prerequisites

  • To run the example scripts on this page, the following prerequisites must be met:
  • Additional libraries need to be installed in the instance directory.
    • lua-curl is a shared library object that can send and receive REST requests from a Lua script.
    • base64 is a Lua library to base64 encode and decode strings.
  • The User Data Registration Step has to be configured with a Password User Item plugin. In the Password User Item the Password Attribute Key must be configured with a value (e.g. PASSWORD). This key will be used to provide the password to the script in the Input Map.
  1. Installing additional libraries:
  2. Use luarocks install --tree $IAM_instance_dir/scripting/lua lua-curl.
  3. Use luarocks install --tree $IAM_instance_dir/scripting/lua base64.

REST request with basic auth

This Scriptable Step plugin will connect to httpbin.org and simulate basic auth authentication.

It will take the username (Context Data Map plugin) and password (User Password Map plugin) to create the authentication URL.

It will take the username (Context Data Map plugin) and secret (Input Secrets) and create a basic auth header.

In this simulation, httpbin.org will compare password and secret. The REST API will return a 200-ok status code if they are identical. If there is a mismatch a 401 unauthorized status code will be returned.

We can use this behavior to force the self-registration flow to either succeed if we provide the secret as user password or to fail, if we provide a different string.

  1. Go to:
    Loginapp >> Self-Registration >> Default Flow
  2. In property Steps create and edit a Scriptable Step plugin.
  3. In property Input Map configure a Context Data Map plugin.
  4. In property Output Map configure 2 Script Output Declaration plugins as follows.
    • A mandatory STRING with Key response_code.
    • An optional STRING with Key server_response.
  5. In section Advanced Settings in property Input Secrets configure and edit a Script Secret plugin.
    • In property Identifier set the value to BASIC_AUTH.
    • In property Secret configure a random value.
  6. In section Advanced Settings in property Namespace configure and edit a Script Namespace plugin.
    • In property Identifier set the value to get-basic-auth.
  7. In property Script add the following Lua code:
  8. Show moreShow lesscopy
    -- Example script that shows how a GET request with Basic Auth can be sent to a remote endpoint.
    local curl = require "cURL"
    local base64 = require "base64"
    
    function prepareGetRequest (responseHandler)
        local targetURL = "https://httpbin.org/basic-auth/" .. iam.input_map["username"] .. "/" .. iam.input_map["PASSWORD"]
        local basic_auth_cred = base64.encode(iam.input_map["username"] .. ":" .. iam.secrets:get("BASIC_AUTH"))
    
        local request = curl.easy{
            url = targetURL,
            httpheader = {
                "Authorization: Basic " .. basic_auth_cred,
                "accept: application/jsonbla"
            },
            writefunction = responseHandler,
            [curl.OPT_TIMEOUT] = 10 -- request timeout
        }
        return request
    end
    
    function performBasicAuthGetRequest ()
        local output_map = {}
        local function handleResponse (response)
            output_map["server_response"] = response
        end
    
        local request = prepareGetRequest(handleResponse)
        request:perform()
    
        output_map["response_code"] = tostring(request:getinfo(curl.INFO_RESPONSE_CODE))
    
        request:close()
        return output_map
    end
    
    function iam_on_step_init ()
        iam.log:info("Complete input map of GET request step: " .. iam.cjson.encode(iam.input_map))
        local output_map = performBasicAuthGetRequest()
        iam.log:info("Complete output map of GET request step: " .. iam.cjson.encode(output_map))
        return output_map
    end
  9. The plugin is ready to exchange data between IAM and script.

The Namespace property is used in this example because another script also provides response_code and server_response as return values. Using Namespace avoids name conflicts in the self-registration flow.

Conditional error message

The Acknowledge Message Step is used as a conditional error message and should only be shown if the previous scriptable step returned a 401 status code.

  1. In property Steps create and edit an Acknowledge Message Step.
  2. In property Server Message create and edit a Static String Value Provider plugin.
    • In the Value property define a message for the end user (e.g., The REST request was unsuccessful. Self-registration has been aborted).
  3. In property Skip Condition create and edit a String Regex Condition plugin.
    • In the property Value Provider create and edit a String From Map Value Provider plugin.
      • In the property Value Maps create and edit a Script Execution Result Value Map Provider plugin.
        • In the property Namespace Config select the namespace pluign configured in the previous step (REST request with basic auth).
      • In the property Key provide the key from the Output Map (e.g. response_code).
    • In the property Regex Pattern define the pattern (e.g. ^2.*).
  4. In property Tags on Success create and edit a Tag plugin.
    • In the property Name define an identifier (e.g. rest-failed).
  5. The step will be skipped if the key response_code in the namespace get-basic-auth is 2xx. In all other cases, the step will be executed, the message displayed and the tag rest-failed will be set.

Skip condition on all remaining steps

If the first Scriptable Step (REST request with basic auth) returned an error status code, all remaining steps in the flow should be skipped. For this purpose, we use the Tag set in the Acknowledge Message Step (conditional error message).

  1. To be configured on every further step:
  2. In property Skip Condition configure and edit a Has Tag plugin.
  3. In property Tag select the plugin created previously for the rest-failed identifier.

Post request with JSON body

This Scriptable Step plugin will establish a connection to reqres.in and will send a POST request with a JSON body. The reqres.in endpoint will simulate adding a user with the JSON data provided. The user data, an additional user identity, and a creation date are returned in the response.

  1. Go to:
    Loginapp >> Self-Registration >> Default Flow
  2. In property Steps create and edit a Scriptable Step plugin.
  3. In property Input Map configure a Context Data Map plugin.
  4. In property Output Map configure 2 Script Output Declaration plugins.
    • A mandatory STRING with Key response_code.
    • An optional STRING with Key server_response.
  5. In Advanced Settings in property Namespace configure and edit a Script Namespace plugin.
    • In property Identifier set the value to post-request-step.
  6. In property Script add the following Lua code:
  7. Show moreShow lesscopy
    -- Example script that shows how a POST request with JSON body can be sent to a remote endpoint.
    local curl = require "cURL"
    
    function buildRequestJsonBody ()
        local jsonBody = {}
        jsonBody["username"] = iam.input_map["username"]
        jsonBody["email"] = iam.input_map["email"]
        jsonBody["givenname"] = iam.input_map["givenname"]
        jsonBody["surname"] = iam.input_map["surname"]
        jsonBody["town"] = iam.input_map["town"]
        jsonBody["birthdate"] = iam.input_map["birthdate"]
        return iam.cjson.encode(jsonBody)
    end
    
    function prepareJsonPostRequest (responseHandler)
        local targetURL = "https://reqres.in/api/users"
        local request = curl.easy{
            url = targetURL,
            post = true,
            httpheader = {
                "Content-Type: application/json"
            },
            postfields = buildRequestJsonBody(),
            writefunction = responseHandler,
            [curl.OPT_TIMEOUT] = 10 -- request timeout
        }
        return request
    end
    
    function performJSONPostRequest ()
        local output_map = {}
        local function handleResponse (response)
            output_map["server_response"] = response
        end
        local request = prepareJsonPostRequest(handleResponse)
        request:perform()
    
        output_map["response_code"] = tostring(request:getinfo(curl.INFO_RESPONSE_CODE))
    
        request:close()
        return output_map
    end
    
    function iam_on_step_init ()
        iam.log:info("Complete input map on POST request step: " .. iam.cjson.encode(iam.input_map))
        local output_map = performJSONPostRequest()
        iam.log:info("Complete output map of POST request step: " .. iam.cjson.encode(output_map))
        return output_map
    end
    
  8. The plugin is ready to exchange data between IAM and script.
  9. The Namespace property is used in this example because another script also provides response_code and server_response as return values. Using Namespace avoids name conflicts in the self-registration flow.

Message builder

This Scriptable Step will transform the JSON response of the previous step (Post request with JSON body) into human-readable HTML for the next Acknowledge Message Step.

  1. Go to:
    Loginapp >> Self-Registration >> Default Flow
  2. In property Steps create and edit a Scriptable Step plugin.
  3. In property Input Map configure a Context Data Map plugin.
  4. In property Output Map configure 2 Script Output Declaration plugins.
    • An optional STRING with Key message.
    • An mandatory STRING with Key result.
  5. In Advanced Settings in property Namespace configure and edit a Script Namespace plugin.
    • In property Identifier set the value to message_builder.
  6. In property Script add the following Lua code:
  7. Show moreShow lesscopy
    -- Example script that transforms the received JSON response into a human-readable HTML.
    local messageTemplate = [[
    Added a new user with the following properties:
    <ul>
    <li>Username: %s</li>
    <li>E-Mail: %s</li>
    <li>First Name: %s</li>
    <li>Last Name: %s</li>
    <li>Town: %s</li>
    <li>Birth Date: %s</li>
    <li>Identity: %s</li>
    <li>Creation Date: %s</li>
    </ul>
    ]]
    
    function getPropertyOrDefault(jsonTable, key)
        if (jsonTable[key] ~= nil) then
            return tostring(jsonTable[key])
        else
            return "-"
        end
    end
    
    function buildMessage ()
        local output_map = {}
        local success, jsonTable = pcall(iam.cjson.decode, iam.input_map["server_response"])
        if (success == false) then
            output_map["result"] = "failure"
            return output_map
        end
    
        local ackMessage = string.format(messageTemplate,
                getPropertyOrDefault(jsonTable, "username"),
                getPropertyOrDefault(jsonTable, "email"),
                getPropertyOrDefault(jsonTable, "givenname"),
                getPropertyOrDefault(jsonTable, "surname"),
                getPropertyOrDefault(jsonTable, "town"),
                getPropertyOrDefault(jsonTable, "birthdate"),
                getPropertyOrDefault(jsonTable, "id"),
                getPropertyOrDefault(jsonTable, "createdAt"))
    
        output_map["message"] = ackMessage
        output_map["result"] = "success"
        return output_map
    end
    
    function iam_on_step_init ()
        iam.log:info("Complete input map of message builder step: " .. iam.cjson.encode(iam.input_map))
        local output_map = buildMessage()
        iam.log:info("Complete output map of message builder step: " .. iam.cjson.encode(output_map))
        return output_map
    end
    
  8. The plugin is ready to exchange data between IAM and script.
  9. The Namespace property is used in this example for clarity only. No other scripts provide the same return values, and the default namespace would have been sufficient.

REST Response Display

To display the data from the REST response, an Acknowledge Message Step is configured as follows:

  1. In property Server Message create and edit a String From Value Map Provider plugin.
  2. In property Value Maps create and edit a Script Execution Result Value Map plugin.
    • In property Namespace Config select the message builder namespace.
  3. In property Key set the key from the Output Map of the message builder (e.g. message).