Expressions
This article will describe how to use expressions with Phenix Server.
exec_if_expr can be replaced with skip_if_expr.
When making changes to the configuration, please make sure to have backup on the file/files.
Item that contains a property
"exec_if_expr": "flow.items().get(0).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.items().isEmpty()"
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"
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 we will start in the uid_pwd_pipe for the RADIUS_AUTHENTICATOR by doing an LDAPSearch and bind. After this we add 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.
"valves" : [ {
"name" : "LDAPSearchValve",
"config" : {
"connection_ref" : "a464079e-020f-460a-96d5-804a63b0ad23",
"base_dn" : "DC=Org,DC=local",
"scope" : "SUB",
"size_limit" : "0",
"filter_template" : "samAccountName={{request.User-Name}}",
"attributes" : "mobile"
}
}, {
"name" : "LDAPBindValve",
"config" : {
"connection_ref" : "a464079e-020f-460a-96d5-804a63b0ad23",
"password_param_name" : "User-Password"
}
}, {
"name" : "GetTokenExistsValve",
"config" : {
"username_attribute" : "User-Name",
"get_value_attribute_key" : "HasToken"
}
}, {
"name" : "OTPGeneratorValve",
"config" : {
"length" : "4",
"alpha_numeric" : "false",
"name" : "generated_otp",
"valid_time_in_seconds" : "120",
"exec_if_expr" : "flow.property('HasToken').equals('false')"
}
}, {
"name" : "OTPBySMSValve",
"config" : {
"gw_username" : "gwuseraccount",
"gw_password" : "{enc}tUQS2IeqHZdR9w1vENQZaJoSxrkTE=",
"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 validate_otp_pipe. No LDAPSearch and bind is needed here (not used because of the line "skip_if_expr" : "true === true"). 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.
"valves" : [ {
"name" : "LDAPSearchValve",
"config" : {
"connection_ref" : "a464079e-020f-460a-96d5-804a63b0ad23",
"base_dn" : "DC=Org,DC=local",
"scope" : "SUB",
"size_limit" : "0",
"filter_template" : "samAccountName={{request.User-Name}}",
"attributes" : "",
"skip_if_expr" : "true === true"
}
}, {
"name" : "SessionLoadValve",
"config" : {
"id" : "{{request.State}}"
}
}, {
"name" : "PINValidationValve",
"config" : {
"stored_pin_param_name" : "",
"provided_otp_attribute_name" : "otp",
"provided_pin_param_name" : "User-Password",
"pin_placement" : "before",
"pin_length" : "",
"skip_if_expr" : "true === true"
}
}, {
"name" : "GetTokenExistsValve",
"config" : {
"username_attribute" : "User-Name",
"get_value_attribute_key" : "HasToken"
}
}, {
"name" : "TokenValidationValve",
"config" : {
"provided_otp_param_name" : "{{request.User-Password}}",
"debug_token_data" : "true",
"otp_length" : "6",
"exec_if_expr" : "flow.property('HasToken').equals('true')"
}
}, {
"name" : "OTPValidationValve",
"config" : {
"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.
So we will start in the uid_pwd_pipe for the RADIUS_AUTHENTICATOR 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.
"valves" : [ {
"name" : "LDAPSearchValve",
"config" : {
"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"
}
}, {
"name" : "LDAPSearchValve",
"config" : {
"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" : ""
}
}, {
"name" : "LDAPBindValve",
"config" : {
"connection_ref" : "7571a019-77c4-4664-87a7-80b7fb1341eb",
"password_param_name" : "User-Password"
}
}, {
"name" : "OTPGeneratorValve",
"config" : {
"length" : "4",
"alpha_numeric" : "false",
"name" : "generated_otp",
"valid_time_in_seconds" : "120",
"exec_if_expr" : "flow.items().get(0).containsProperty('mobile')"
}
}, {
"name" : "OTPBySMSValve",
"config" : {
"gw_username" : "gwuseraccount",
"gw_password" : "{enc}GRmxWFJgpWsJdEFd7vOPnwFnLAnbMLwIeqFf5d8Z5Jc=",
"recipient_param_name" : "mobile",
"generated_otp_name" : "generated_otp",
"use_flash" : "true",
"exec_if_expr" : "flow.items().get(0).containsProperty('mobile')"
}
}
Now to our validate_otp_pipe. 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.
"valves" : [ {
"name" : "LDAPSearchValve",
"config" : {
"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"
}
}, {
"name" : "LDAPSearchValve",
"config" : {
"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" : ""
}
}, {
"name" : "SessionLoadValve",
"config" : {
"id" : "{{request.State}}"
}
}, {
"name" : "PINValidationValve",
"config" : {
"stored_pin_param_name" : "",
"provided_otp_attribute_name" : "otp",
"provided_pin_param_name" : "User-Password",
"pin_placement" : "before",
"pin_length" : "",
"skip_if_expr" : "true === true"
}
}, {
"name" : "TokenValidationValve",
"config" : {
"provided_otp_param_name" : "{{request.User-Password}}",
"debug_token_data" : "true",
"otp_length" : "6",
"skip_if_expr" : "flow.items().get(0).containsProperty('mobile')"
}
}, {
"name" : "OTPValidationValve",
"config" : {
"provided_otp_param_name" : "{{request.User-Password}}",
"generated_otp_param_name" : "generated_otp",
"exec_if_expr" : "flow.items().get(0).containsProperty('mobile')"
}
}