Filters

Transform and modify values in expressions and format strings

Following filters are supported:

  • hex: Converts a string or a byte value into a hexadecimal string. Outputs lowercase hexadecimal digits without separators. Example: "Hello" becomes "48656c6c6f"
  • string: Converts a non-string or a byte value into a string
  • uppercase: Converts a string value into uppercase
  • lowercase: Converts a string value into lowercase
  • value: Converts to a raw value format
  • printable: Converts byte data to a printable ASCII string
  • normalize_mac: Normalizes MAC address format to 12 lowercase hexadecimal digits without separators, preserving any trailing characters. Example: "00:1A:2B:3C:4D:5E" becomes "001a2b3c4d5e"
  • escape_double_quotes: Escapes double quotes in a string
  • ldap_dn_escape: Escapes special characters in LDAP distinguished names
  • ldap_escape: Escapes special characters in LDAP search strings
  • url_escape: Performs URL encoding on the string
  • xml_escape: Escapes special characters in XML content
  • substring(<start>[, <end>]): Extracts a substring from the value. The start parameter is required and specifies the starting character position (zero-indexed). The optional end parameter specifies the ending position (exclusive). Negative values count from the end of the string. Example: "example" | substring(2, 5) returns "amp"
  • type: Get internal type information of a value
  • default(<default value>): Provides a default value if the value is none
  • recover(<fallback>): If a filter fails, use this to recover from the error using the fallback value
  • json: Parse string value to JSON (standard JSON only)
  • jsonpath(<jsonpath expression>): Extract value from JSON using a JSONPath expression
  • regex: Parse string value to regex
  • join(<separator>): Join array elements into a single string using the specified separator
  • reveal: Reveals masked passwords. Example: radius.request.password | reveal
    • NOTE: Masked password can be compared directly without revealing!

Usage

Filters can be used in any expression. The syntax is:

modify {
    radius.reply.attr.Alc-Subsc-ID-Str = vars.subscriber_id | substring(2, 10) | uppercase;
}

It is also possible to use filters in format strings:

modify {
    radius.reply.attr.Alc-Subsc-ID-Str = "FIBER.%{ vars.subscriber_id | substring(2, 10) | uppercase }";
}

Working with JSON data

The jsonpath filter only works with JSON data. Some backends may return JSON data directly, but if the data is in string format, it needs to be parsed first using the json filter.

modify {
    vars.user_name = vars.user_data | json | jsonpath("$.name");
}

If you need to call jsonpath multiple times on the same JSON data, it's more efficient to first parse the JSON and store it in a variable:

modify {
    vars.user_json = vars.user_data | json;
    vars.user_name = vars.user_json | jsonpath("$.name");
    vars.user_email = vars.user_json | jsonpath("$.email");
}

Error handling

Sometimes a filter can fail. For example, when trying to parse invalid input with the json filter or regexes with the regex filter, they will produce an error value.

You may recover from these errors using the recover filter. For example:

modify {
    vars.user_json = vars.user_data | json | recover("{\"name\": \"unknown\"}" | json);
    vars.user_name = vars.user_json | jsonpath("$.name");
}

Filter errors do not cause execution pipeline errors immediately, but if an error value is attempted to be assigned, for example to a RADIUS reply attribute, it will cause a pipeline error and the authentication will be rejected. However, the error values are allowed to be logged.

The error value is false in all comparison operations and all filters just pass it through.

SQL mappings

SQL backend query mappings support filter expressions to transform column values. This is useful for parsing JSON data stored in text columns.

query "FIND_USER" {
    statement "SELECT username, password, reply_attrs FROM users WHERE username = ?";
    bindings {
        aaa.identity;
    }
    mapping {
        user.username = username;
        user.password = password;
        vars.ip = reply_attrs | json | jsonpath("$.framed_ip_address");
    }
}

For nullable JSON columns, use | default("{}") before | json to handle NULL values gracefully:

mapping {
    vars.data = json_column | default("{}") | json;
}

Note: PostgreSQL and MySQL support native JSON column types which can be read directly without the | json parsing step. The | json filter is primarily needed for databases like SQLite that store JSON as text.