Attribute functions

You can use the configuration API samples and syntax, to author custom functions.

Overview

Use functions to reference, transform, and combine attribute values before they are passed to an application in the form of a single sign-on authentication token or when provisioning accounts. Functions can access the identity source credential that is used to authenticate to IBM® Security Verify, the user object (in SCIM form) that is stored in Cloud Directory and any external API endpoint. For example, an attribute that is called formalDisplayName can be created as a fixed value attribute and a function can be specified that concatenates the user.name.givenName and user.name.familyName in a specified manner.
Note: The function syntax is C- and JavaScript-like. However, it is based on a single-line expression language, Google Common Expression Language.

Accessing domain objects

The term, domain objects, is a catch-all phrase that is used to indicate all possible objects that can be accessed in an attribute's custom function.

Cloud Directory user

For every user that authenticates to Verify a user account is created in Cloud Directory. This account is represented as a SCIM object. In the following examples, the following Cloud Directory user account is used.

The following SCIM object is the user account.
{
  "id": "600000A3DD",
  "userName": "google-oauth2|1033116550041553242@jke.samlfed.com",
  "emails": [
    {
      "type": "work",
      "value": "jessica@jke.com"
    }
  ],
  "meta": {
    "created": "2019-04-26T09:21:35Z",
    "location": "https://jke.cloudidentity.com/v2.0/Users/600000A3DD",
    "lastModified": "2019-04-26T09:21:35Z",
    "resourceType": "User"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User",
    "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
    "urn:ietf:params:scim:schemas:extension:ibm:2.0:User"
  ],
  "name": {
    "formatted": "Jessica Hill",
    "familyName": "Hill",
    "givenName": "Jessica"
  },
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "manager": {
      "value": "6030101TP6"
    }
  },
  "urn:ietf:params:scim:schemas:extension:ibm:2.0:User": {
    "userCategory": "federated",
    "twoFactorAuthentication": false,
    "realm": "jke.samlfed.com",
    "unqualifiedUserName": "google-oauth2|1033116550041553242",
    "customAttributes": [
        { 
          "name": "car",
          "values": [ "Ford Mustang Mach-E", "Maruti Suzuki 800" ]
        },
        {
          "name": "hobbies",
          "values": [ "Reading", "Running", "Gaming", "Star Wars" ]
        }
    ]
  },
  "active": true
}
Note: In the SCIM object, two custom attributes are defined - car and hobbies. These attributes are schema extensions that can be configured through the Admin Console. Values can be added to the user object through the Verify Users API.
Syntax Description Examples
user.$property Access $property. Both . and [".."] can be used.
user.name.familyName + ", " + user.name["givenName"]

Result:

Hill, Jessica
user.$values.filter(x, $condition) $values: A list. filter function extracts values based on $condition.
user.emails.filter(x, x.type == "work")[0].value

Result:

jessica@jke.com
user.GetCustomValues($attrName) Function to get custom attribute values as a list. $attrName: Name of the attribute in the user object Returns null if the attribute does not exist.
userOp.getCustomValues("car")

Result:

["Ford Mustang Mach-E","Maruti Suzuki 800"]
user.GetCustomValue($attrName) Function to get the first custom attribute value in the list. $attrName:Name of the attribute in the user object Returns empty string ("") if the attribute does not exist.
userOp.getCustomValue("hobbies")

Result:

Reading
user.GetManager() Function to get the manager information of current user. The function returns the manager's user account (as a SCIM object). If no manager is specified for the user, it returns an empty JSON object.
userOp.getManager().name.formatted

Result:

Jacob Jones
Identity source credential
When a user logs in to Verify, the identity source credential attributes are added into the login session and can be accessed in a custom function. Consider that the user logs in with a SAML federated identity provider and the SAML assertion contains an attribute statement that is called userRoles and it is set to marketing and helpdesk.
The idsuser attribute is available as a map with a string key and a string array value. For example,
{
  "userRoles": ["marketing", "helpdesk"],
  "displayName": ["Jessica J. Hill"],
  "phone": ["+12324321234"]
}
Syntax Description Examples
idsuser.$property Access $property. The value in idsuser is always an array of strings.
idsuser.userRoles[1]

Result:

helpdesk
idsuser.getValue($property) Returns the value of $property as a string. If the value array has multiple entries, the first item is returned. If the $property does not exist, an empty string is returned.
idsuser.getValue('userRoles') 

Result:

"Marketing"
idsuser.getValues($property) Returns all the value of $property as a string array. If the $property does not exist, a nil object is returned.
idsuser.getValues('userRoles') 

Result:

["Marketing", "helpdesk]"
HTTP request context

When a user logs into IBM Security Verify, the incoming HTTP request context can be accessed in a custom function. If the user logs in with an OAuth flow and the client sends client-ip and user-agent information, requestContext can extract the information. It can be used to call out to an external endpoint to determine the risk score for the user.

requestContext is available as a map with a string key and a string array value.

Table 1. HTTP request context
Syntax Description Example
requestContext.$property Access $property. The value in requestContext is always an array of strings.
requestContext.devicePlatform[1]

Result

MACOS
requestContext.getValue($property) Returns the value of $property as a string.If the value array has multiple entries, the first item is returned. If the $property does not exist, an empty string is returned.
requestContext.getValue('x-forwarded-for')

Result

116.15.12.181
requestContext.getValues($property) Returns all the values of $property as a string array. If the $property does not exist, a nil object is returned.
requestContext.getValues('x-forwarded-for')

Result

["116.15.12.181"]
Attribute context
The context object holds key-value pairs of certain properties of the attribute that can be used when writing functions. The values of these properties are valid only in the context of that attribute's lookup. This object can be accessed with the ctx key.
The following properties are available with the context object:
Syntax Description Examples
ctx.currentValue Access the attribute value evaluated before running this function. The data type of this value is specified in the attribute configuration. If the value cannot be casted to the data type, this value is set to null.
ctx.currentValue.toUpper

Standard operators

Operators that are supported in attribute functions include the standard list available in any programming language. For example, +, -, *, /, >, <. The + can be used to concatenate strings.
Operator Description Examples
== Equals comparison
user.name.givenName == "Jessica"
!= Not equals comparison
idsuser.myroles[0] == "marketing"
|| Logical OR comparison
user.name.givenName == "Jessica" || user.name.formatted.startsWith("Hill")
&& Logical AND comparison
1 == 1 && 2 == 2
[ ] Map access
user["name"]["givenName"]
+ Concatenation and addition, depending on the type
"Hello" + " " + "World"
- Subtraction
10 - 5
* Multiplication
5 * 2
/ Division
20 / 4
> Greater than condition
1 > 2
< Less than condition
1 < 2
>= Greater than or equal to
1 >= 1
<= Less than or equal to
1 <= 1
? Ternary if operator
a == b ? true : false

Standard functions

Attribute functions can use standard functions to perform tasks such as manipulating attribute values and accessing elements in a list.
Table 2. String functions
Syntax Description Examples
$string.contains($fragment) Checks if $fragment is found in $string.
"helloworld".contains("hello")

Result:

true
$string.endsWith($fragment) Checks if $string ends with $fragment.
"hello world".endsWith("old")

Result:

false
$string.matches($regex) Checks if the $regex matches the pattern in $string.
"foo".matches("k.*")

Result:

false
$string.toUpper() Converts $string to uppercase.
"hello".toUpper()

Result:

HELLO
$string.toLower() Converts $string to lowercase.
"HEllO".toLower()

Result:

hello
$string.base64Encode() Base64 encodes $string.
"hello".base64Encode()

Result:

aGVsbG8=
$string.base64Decode() Base64 decodes $string.
"aGVsbG8=".base64Decode()

Result:

hello
$string.size() Size of the $string
"hello".size()

Result:

5
$string.substring($begin,$end) Returns the string between the $begin index (including) and $end index (excluding).
"hello".substring(1,4)

Result:

ell
$string.split($delim) Returns the array of strings that are split by the $delim.
"hello".split("e")

Result:

["h","llo"]
$string.replaceAll($old,$new) Replaces all occurrences of $old with $new.
"hello".replaceAll("l","p")

Result:

heppo
$string.matchAndReplaceAll($regex, $newStr) Replaces all matches of $regex with $newStr.
"some12#$text".matchAndReplaceAll("[^a-zA-Z]+", "-")

Result:

some-text
$string.indexOf($str) Returns the index of first occurrence of $str.
"hello".indexOf("l")

Result:

2
$string.lastIndexOf($str) Returns the index of last occurrence of $str.
"hello".lastIndexOf("l")

Result:

3
Table 3. List functions
Syntax Description Examples
$values.size() Size of the list $values
["hello", "world"].size()

Result:

2

$values.filter(x, $condition) Filters $values by $condition.
["hello", "world", "helios"].filter(x,
            x.startsWith("hel"))

Result:

["hello", "helios"]

$values.all(x, $condition) Checks if all $values satisfy $condition.
["hello", "world", "helios"].all(x,
            x.contains("hel"))

Result:

false

$values.exists(x, $condition) Checks if any value satisfies $condition.
["hello", "world", "helios"].exists(x,
            x.contains("hel"))

Result:

true

$values.exists_one(x, $condition) Checks if exactly one value satisfies $condition.
["hello", "world", "helios"].exists(x,
            x.contains("hel"))

Result:

false

$values.map(x, $op) Runs $op on each value.
["hello", "world", "helios"].map(x, x.toUpper())

Result:

["HELLO","WORLD","HELIOS"]

jsonToString($json) Convert the list $json into a string.
jsonToString(["hello","world","helios"])

Result:

"[\"hello\",\"world\",\"helios\"]

joinString($values, $s) Joins the strings in the list $values with the separator $s.
joinStrings(['hello','world','helios'], ' + ')

Result:

"hello + world + helios"

Note: You can also run List functions against Map objects. The function runs against a list of the keys in the Map. For example, if the input contains:
{
idsuser: {
"attr1":"value1",
"attr2":"value2"
}
}
then for function idsuser.exists(x, $condition), x is [ "attr1", "attr2" ].
Table 4. Map and object functions
Syntax Description Examples
has($m.$p) Checks whether the map $m contains the property $p.
has({ "hello": "world" }.hello)

Result:

true

jsonToString($m) Converts the map $m into a string.
jsonToString({"hello":"world"})

Result:

"{\"hello\":\"world\"}"

jsonToFormURLEncoded($m, $doUrlEncode) Converts the map $m into a form. If $doUrlEncode is set to true, the form is URL-encoded.
jsonToFormURLEncoded({'hello':'world', 'key1':'value 1'}, true)

Result:

"hello=world&key1=value+1"

Note: To check for existence of property that has reserved characters in the name , use the exists List function.
idsuser.exists(x, x == "ext:idsource_attr1")
It returns true if the property exists and false otherwise.

HTTP client

Attribute functions can be written to call out to external API endpoints to obtain values.
Note:
  • This feature is not available for trial tenants.
  • The Authorization header token must be generated by the consumer. For example, it can be a long-lived API key that is baked into the function.
Syntax Description Examples
hc.GetAsString($url, $headers) Returns the response as a serialized string. $url: URL of the API endpoint must be a full URL $headers: JSON object in the form {"headerName":"headerVal"}.
hc.GetAsString("https://api.jke.com/resources/" + user.name.givenName, {"Authorization":"Some token"})

Result:

"{\"value\":\"someValue\"}"

hc.GetAsJson($url, $headers) Parses the response as a JSON object. $url: URL of the API endpoint must be a full URL $headers: JSON object in the form {"headerName":"headerVal"}.
hc.GetAsJson("https://api.jke.com/resources/" + user.name.givenName, {"Authorization":"Some token"}).value

Result:

"someValue"

hc.Post($url, $headers, $body) Returns the status code, response headers and response body, The response body is returned as a JSON object if the content type is application/json or as a string for any other content type.

$url - The URL of the API endpoint has to be a full URL.

$headers - JSON object in the form {"headerName":"headerVal"}

$body - Request the body in the form of a string.

hc.Post("https://api.jke.com/resources", {"Authorization": "Some token"}, "{\"key\":\"value\"}")

Result:

{"statusCode": "200", "responseHeaders": {"header": ["value1", "value2"]}, "responseBody": {"message": "success"}}

hc.Opts($options) $options: Eight flags are currently supported:
  1. insecure: Indicates that the SSL connection can be insecure.
  2. certLabel: Label of the signer certificate that is uploaded to the CI tenant.
  3. tlsMinVersion: The minimum TLS version that is supported. The options available are TLSv1.0, TLSv1.1, TLSv1.2. If this flag is not specified, the minimum TLS version is set to TLSv1.2.
  4. followRedirects: Indicates whether the HTTP client follows redirects. If a value is not specified, the setting defaults to false.
  5. cache: Boolean indicating if the response is cached. The setting defaults to true for GET calls and defaults to false for other HTTP methods.
  6. cacheExpiry: The lifetime of the cached response in seconds, maximum of 1 hour (3600). If the lifetime is not specified, the setting defaults to 60 seconds.
  7. mtls: Boolean indicating if mutual TLS should be enabled for the request. If the value is set to true, the values for the following flags are ignored: insecure, certLabel, tlsMinVersion, followRedirects.
  8. mtlsCert: The label for an existing personal certificate to be provided for the MTLS request.
This function responds with an hc instance, so GetAsJSON and GetAsString can be called.
hc.Opts({"certLabel": "jkeCA","insecure":false,
"tlsMinVersion":"TLSv1.2", "followRedirects":true,"cache":true,
"cacheExpiry":"1200"}).GetAsString(...)
HTTP client response caching

HTTP response caching is enabled by default for GET calls (GetAsString and GetAsJSON) with a default cache expiry of 1 minute. It is disabled by default for POST calls. To override the default settings for HTTP client response caching, the flag cache must be included in the hc.Opts with the value of either true or false. The cache lifetime is set to 60 seconds by default. To override the default cache lifetime, the flag cacheExpiry must be included in the hc.Opts with the value in seconds, up to a maximum of 3600 seconds (one hour).

Note: The response body limit is capped at 4 MB per request.

Adaptive risk

Use Adaptive access risk functions to access the current user session risk level and associated authorization data.

An Adaptive access policy must be evaluated at least once in the session before using the custom attribute to ensure that the data is populated, otherwise the value “NOT_AVAILABLE” is returned.

The Adaptive access risk functions provide access to the corresponding Access policy conditions displayed in the Policy Editor as described in Managing Adaptive Access policy rules.

Details of the Risk indicators are described in Risk indications.

The key indicators of the risk data is structured as JSON and can be seen in the following example. This JSON structure can be accessed by using the risk.getAdaptiveSessionData() function.

The full adaptive risk data response that is related to the user session can be accessed by using the risk.getRawAdaptiveSessionData() function.

{
  "riskLevel": "LOW", 
  "isNewDevice": false, 
  "isRiskyDevice": false, 
  "isRiskyConnection": false, 
  "remoteIP": "122.143.222.333", 
  "country": "ISR", 
  "city": "Jerusalem", 
  "isp": "013 Netvision", 
  "isNewLocation": false, 
  "behavioralAnomaly": false,
  "userBehavioralScore":"100"
}
Syntax Description Examples
risk.getAdaptiveSessionLevel() Returns the user session's adaptive risk level.
risk.getAdaptiveSessionLevel()

Result:

"LOW"

risk.getAdaptiveSessionData() Returns a JSON array of the Adaptive risk data that is related to the user session. Properties that have the prefix is return a Boolean value. All others return string.
risk.getAdaptiveSessionData()

Result:

"behavioralAnomaly":false, 
"city":"Bundall", 
"country":"AUS", 
"isNewDevice":false, 
"isNewLocation":false, 
"isRiskyConnection":false, 
"isRiskyDevice":false, 
"isp":"Network Technology (AUST) P/L", 
"remoteIP":"120.29.43.158", 
"riskLevel":"LOW",
"userBehavioralScore":"100"
risk.getAdaptiveSessionData().($p) Returns the individual property $p from the risk.getAdaptiveSessionData().
risk.getAdaptiveSessionData().isRiskyDevice

Result:

true
risk.getAdaptiveSessionData().isp

Result:

"Network Technology (AUST) P/L”

risk.getRawAdaptiveSessionData() Returns the full JSON Adaptive risk data response that is related to the user session. See the Trusteer Pinpoint API documentation for available elements.

The Adaptive Access response is a two handler JSON array. The first element is sourced from the session_info handler and the second element the pinpoint_eval handler.

risk.getRawAdaptiveSessionData()[1].message.pinpoint_assessment.risk.risk_score

Result:

850

Note: Datatype casting may be required when using extracted adaptive attributes in attribute mapping for applications or custom attributes conditions in access policy evaluation, including JSON conversation to string.

For example:

To return a string value of the risk_score for evaluation in an access policy, it must first be cast to a string

 string(risk.getRawAdaptiveSessionData()[1].message.pinpoint_assessment.risk.risk_score)

To perform a mathematical or logic operation or evaluation in an Advanced rule, a JSON number must first be cast to an int

 int(risk.getRawAdaptiveSessionData()[1].message.pinpoint_assessment.risk.risk_score) > 900
or evaluated as a double
 risk.getRawAdaptiveSessionData()[1].message.pinpoint_assessment.risk.risk_score > 900.0

Hash functions

Syntax Description Examples
sha256($value) Computes the sha256 hash value for the specified string.
sha256('hello')

Result:

`2cf24dba...`

sha512($value) Computes the sha512 hash value for the specified string.
sha512('hello')

Result:

`9b71d224...`

hmacSha1($value, $key) Computes the HMAC-SHA1 value with the key $key for the given string.
hmacSha1('hello','key')

Result:b34ceac4516ff23a143e61d79d0fa7a4fbe5f266

Timestamp functions

Syntax Description Examples
now Returns a timestamp object of the current time.
now

Result:

"2021-08-17T08:24:58Z"
$t.getDate() Returns the day of the month from the timestamp $t as an integer, one-based indexing.
timestamp('2021-08-17T08:24:58Z').getDate()

Result:

17
$t.getDayOfMonth() Returns the day of the month from the timestamp $t as an integer, zero-based indexing.
timestamp('2021-08-17T08:24:58Z').getDayOfMonth()

Result:

16
$t.getDayOfWeek() Returns the day of the week from the timestamp $t as an integer, zero-based, zero for Sunday.
timestamp('2021-08-17T08:24:58Z').getDayOfWeek()

Result:

2
$t.getDayOfYear() Returns the day of the year from the timestamp $t as an integer, zero-based indexing.
timestamp('2021-08-17T08:24:58Z').getDayOfYear()

Result:

228
$t.getMonth() Returns the month from the timestamp $t as an integer, zero-based indexing.
timestamp('2021-08-17T08:24:58Z').getMonth()

Result:

7
$t.getFullYear() Returns the year from the timestamp $t as an integer.
timestamp('2021-08-17T08:24:58Z').getFullYear()

Result:

2021
$t.getHours() Returns the hours from the timestamp $t as an integer
timestamp('2021-08-17T08:24:58Z').getHours()

Result:

8
$t.getMinutes() Returns the minutes from the timestamp $t as an integer.
timestamp('2021-08-17T08:24:58Z').getMinutes()

Result:

24
$t.getSeconds() Returns the seconds from the timestamp $t as an integer.
timestamp('2021-08-17T08:24:58Z').getSeconds()

Result:

58
$t.getMilliseconds() Returns the milliseconds from the timestamp $t as an integer.
timestamp('2021-08-17T08:24:58.642Z').getMilliseconds()

Result:

642
int($t) Converts the timestamp to int64 in seconds since UNIX epoch.
int(timestamp('2021-08-17T08:24:58Z'))

Result:

1629188698
duration($d) The duration $d must be given as a string ending with "s", which denotes the duration in seconds.
timestamp('2021-08-17T08:24:58Z') + duration('3600s')

Result:

"2021-08-17T09:24:58Z"
formatTime($t, $s) Returns the timestamp $t in the format $s. The format $s must use the reference time "Mon Jan 2 15:04:05 MST 2006".
formatTime(timestamp('2021-08-17T08:24:58Z'), 'Monday, 02-Jan-06 15:04:05 MST')

Result:

"Tuesday, 17-Aug-21 08:24:58 UTC"