Expressions
This article will describe how to use expressions with PhenixID Server.
exec_if_expr can be replaced with skip_if_expr.
The settings should be made by logging in to the configuration GUI, go to Scenarios, Radius, <your scenarion>, press the tab "Execution flow" and edit the Pipe/Valve in question. Set the parameters and values according to examples below.
When making changes to the configuration, please make sure to have backup of the file/files.
Item that contains a property
"exec_if_expr": "flow.firstItem().containsProperty('organizationId')"
Item that contains a property but without a value
"exec_if_expr" : "flow.property('AttributeID').isEmpty()",
If no items exists
"exec_if_expr": "flow.isEmpty()"
If many items exists
"exec_if_expr": "flow.isMulti()"
If one item exists
"exec_if_expr": "flow.isSingle()"
Include items when a property value equals
"item_include_expr" : "item.getPropertyValue('Approved','')===('0')"
When a property equals a value
"exec_if_expr" : "flow.property('SMSAuth').equals('true')"
Request parameter contains a value
"exec_if_expr": "request['User-Name'].contains('abc123')"
Request parameter with a specific length
"skip_if_expr": "request['User-Password'].length==6||request['User-Password'].length==8"
Request parameter exists
"exec_if_expr": "request['customParam']"
OR expression
"exec_if_expr" : "flow.property('APPAuth').equals('true') || flow.property('DeviceAuth').equals('true')"
Example configuration, fallback to sms if user has not enrolled for Pocket Pass
This is an example configuration, where we will deliver an SMS if the user has not enrolled for Pocket Pass. The Scenario used is Username, Password & OTP delivered by SMS. Changes to the configuration is in red.
So please go to Scenarios, Radius, <your scenario> and the tab "Execution flow". We will start in the Pipe that does LDAPSearch and bind.
Press "+ Add valve", "Type" should be "GetTokenExistsValve", set it to "Enabled" and add the parameters:
username_attribute with value User-Name
get_value_attribute_key with value HasToken
Then press "Add valve".
Should look like this (press JSON in rigt corner):
{
"username_attribute": "User-Name",
"get_value_attribute_key": "HasToken"
}
Drag the valve so it's located before OTPGeneratorValve and then press "Save".
We also need to add expressions to the valves OTPGeneratorValve and OTPBySMSValve. So on these valves press "+ Add" and enter the parameter and value. In the example below we have used both "exec_if_expr" and "skip_if_expr" with the values "flow.property('HasToken').equals('false')" and "flow.property('HasToken').equals('true')".
So with these added configuration we will use GetTokenExistValve to see if the user has enrolled for a token or not. Then we will generate and deliver an SMS depending on the result from GetTokenExistValve.
Configuration should now look like this:
"LDAPSearchValve"
{
"connection_ref": "f56b30ab-5042-4ca0-b9f0-bc7e36a12fde",
"base_dn": "DC=Org,DC=local",
"scope": "SUB",
"size_limit": "0",
"filter_template": "samAccountName={{request.User-Name}}",
"attributes": "mobile"
}
"LDAPBindValve"
{
"connection_ref": "f56b30ab-5042-4ca0-b9f0-bc7e36a12fde",
"password_param_name": "User-Password"
}
"GetTokenExistsValve"
{
"username_attribute" : "User-Name",
"get_value_attribute_key" : "HasToken"
}
"OTPGeneratorValve"
{
"name": "generated_otp",
"length": "4",
"alpha_numeric": "false",
"valid_time_in_seconds": "120",
"exec_if_expr": "flow.property('HasToken').equals('false')"
}
OTPBySMSValve"
{
"message_gateway_settings": "dd5f8934-b955-4829-9bc5-20f4a0e74a58",
"recipient_param_name": "mobile",
"generated_otp_name": "generated_otp",
"use_flash": "true",
"skip_if_expr": "flow.property('HasToken').equals('true')"
}
Now we need to do the same on our pipe for the validation of otp. No LDAPSearch and bind is needed here. But we need to add the the GetTokenExistValve to know what valve to use for the validation. We also need to add a valve for the validation using tokens, and that valve is TokenValidationValve. After this we add the expressions to use the correct validation depending OTP method.
So please add the new valves like we did above. Parameters and values for GetTokenExistValve is the same as before:
username_attribute with value User-Name
get_value_attribute_key with value HasToken
The TokenValidationValve should have the following set:
provided_otp_param_name with value {{request.User-Password}}
debug_token_data with value true
otp_length with value 6
exec_if_expr with value flow.property('HasToken').equals('true')
These two new valves should be placed right after the SessionLoadValve, according to the configuration below.
Last step is to add expression to the OTPValidationValve.
Configuration should now look like this:
"SessionLoadValve"
{
"id": "{{request.State}}"
}
"GetTokenExistsValve",
{
"username_attribute" : "User-Name",
"get_value_attribute_key" : "HasToken"
}
"TokenValidationValve",
{
"provided_otp_param_name" : "{{request.User-Password}}",
"debug_token_data" : "true",
"otp_length" : "6",
"exec_if_expr" : "flow.property('HasToken').equals('true')"
}
"OTPValidationValve",
{
"provided_otp_param_name" : "{{request.User-Password}}",
"generated_otp_param_name" : "generated_otp",
"exec_if_expr" : "flow.property('HasToken').equals('false')"
}
Example configuration, group membership
This is an example configuration, where we will decide OTP method depending on group membership. The Scenario used is Username, Password & OTP delivered by SMS. Changes to the configuration is in red.
Please go to Scenarios, Radius, <your scenario> and the tab "Execution flow". We will start in the Pipe that does LDAPSearch and bind, where we now need two different LDAPSearch, one for each group. On the first search we will retrieve the value for mobile if user is a member of the group SMS, if not we proceed to the next search since "proceed on error" is set to true. If we have the property mobile we will generate and send OTP.
Add the second LDAPSearchValve as well as parameters and values according to the following configuration:
"LDAPSearchValve",
{
"connection_ref" : "7571a019-77c4-4664-87a7-80b7fb1341eb",
"base_dn" : "DC=Org,DC=local",
"scope" : "SUB",
"size_limit" : "0",
"filter_template" : "(&(samAccountName={{request.User-Name}})(memberOf=cn=SMS,ou=Users,ou=PhenixID,dc=org,dc=local))",
"attributes" : "mobile",
"proceed_on_error" : "true"
}
"LDAPSearchValve",
{
"connection_ref" : "7571a019-77c4-4664-87a7-80b7fb1341eb",
"base_dn" : "DC=Org,DC=local",
"scope" : "SUB",
"size_limit" : "0",
"filter_template" : "(&(samAccountName={{request.User-Name}})(memberOf=cn=Token,ou=Users,ou=PhenixID,dc=org,dc=local))",
"attributes" : ""
}
"LDAPBindValve",
{
"connection_ref" : "7571a019-77c4-4664-87a7-80b7fb1341eb",
"password_param_name" : "User-Password"
}
"OTPGeneratorValve",
{
"length" : "4",
"alpha_numeric" : "false",
"name" : "generated_otp",
"valid_time_in_seconds" : "120",
"exec_if_expr" : "flow.firstItem().containsProperty('mobile')"
}
"OTPBySMSValve",
{
"gw_username" : "gwuseraccount",
"gw_password" : "{enc}GRmxWFJgpWsJdEFd7vOPnwFnLAnbMLwIeqFf5d8Z5Jc=",
"recipient_param_name" : "mobile",
"generated_otp_name" : "generated_otp",
"use_flash" : "true",
"exec_if_expr" : "flow.firstItem().containsProperty('mobile')"
}
Now we need to do the same on our pipe for the validation of otp. We need the same two LDAP searches here. As well as the valve for the validation using tokens, TokenValidationValve. After this we add the expressions to use the correct validation depending OTP method.
So please add the two new valves as well as parameters and values according to the following configuration:
"LDAPSearchValve",
{
"connection_ref" : "7571a019-77c4-4664-87a7-80b7fb1341eb",
"base_dn" : "DC=Org,DC=local",
"scope" : "SUB",
"size_limit" : "0",
"filter_template" : "(&(samAccountName={{request.User-Name}})(memberOf=cn=SMS,ou=Users,ou=PhenixID,dc=org,dc=local))",
"attributes" : "mobile",
"proceed_on_error" : "true"
}
"LDAPSearchValve",
{
"connection_ref" : "7571a019-77c4-4664-87a7-80b7fb1341eb",
"base_dn" : "DC=Org,DC=local",
"scope" : "SUB",
"size_limit" : "0",
"filter_template" : "(&(samAccountName={{request.User-Name}})(memberOf=cn=Token,ou=Users,ou=PhenixID,dc=org,dc=local))",
"attributes" : ""
}
"SessionLoadValve",
{
"id" : "{{request.State}}"
}
"TokenValidationValve",
{
"provided_otp_param_name" : "{{request.User-Password}}",
"debug_token_data" : "true",
"otp_length" : "6",
"skip_if_expr" : "flow.firstItem().containsProperty('mobile')"
}
"OTPValidationValve",
"config" : {
"provided_otp_param_name" : "{{request.User-Password}}",
"generated_otp_param_name" : "generated_otp",
"exec_if_expr" : "flow.firstItem().containsProperty('mobile')"
}