.Example
```
-%{db_status:ok}
-%{db_status:fail}
-%{db_status:notfound}
+%db_status(ok)
+%db_status(fail)
+%db_status(notfound)
...
```
.Example
```
-%{db_status:}
+%db_status()
```
-update { ... }:: The list of attributes to cache for a particular key.
+update { ... }:: The attributes to cache for a particular key.
-Each key gets the same set of cached attributes. The attributes
-are dynamically expanded at run time.
+Each key gets the same set of cached attributes.
-The semantics of this construct are identical to an `unlang`
-update block, except the left hand side of the expression
-represents the cache entry. see man unlang for more information
-on update blocks.
+The operation of the `update` section is a little different
+from normal `update` sections. This is because we need to
+both reference the attributes which we want to store in the
+cache, and also to describe where those attributes are
+written to when the cache entry is read.
-NOTE: Only `request`, `reply`, `control` and `session-state` lists
-are available in cache entries. Attempting to store attributes
-in other lists *will raise an error* during config validation.
+The solution (albeit an imperfect one) is that the cache
+does not store attributes, it stores `update` sections.
+The `update` section given below is used as a template
+for the cache entry.
+
+When the cache entry is created, the right-hand side of
+each attribute assignment line is expanded. The left-hand
+side of the attribute assignment is left alone.
+
+Once all of the right-hand side values are expanded, the
+result is an `update` section with left-hand side
+assignments, and right-hand side values. That `update`
+section is then cached, indexed by the `key`
+
+When the cache entry is read, it is looked up by the `key`,
+and the cached `update` section is found. This cache entry
+now has left-hand side assignments, and right-hand side
+values. It is then applied to the current request.
+
+For example, if the `cache` module is configured with the
+block below:
+
+update {
+ &reply.Reply-Message := "Hello %{User-Name}"
+}
+
+When the cache entry is created, the module will expand the
+right side of the entry, using the attributes from the
+packet. In this case, the string could expand to `"Hello bob"`.
+
+Once all of the right-hand values are expanded, the
+resulting cache entry will look like this:
+
+update {
+ &reply.Reply-Message := "Hello bob"
+}
+
+When the cache module is read, this `update` section is
+applied just as if it had been specified in a configuration
+file.
+
+NOTE: Only `request`, `reply`, `control` and
+`session-state` lists are available for the left side of
+cache entries. Attempting to reference other lists *will
+raise an error* during config validation.
<list>.<attribute> <op> <value>::
-Cache all instances of `Reply-Message in the reply list.
+Cache all instances of `link:https://freeradius.org/rfc/rfc2865.html#Reply-Message[Reply-Message]` in the reply list.
Add our own to show when the cache was last updated.
# memcached {
# options = "--SERVER=localhost"
# pool {
-# start = 0
-# min = 0
-# max =
+# start = 0
+# min = 0
+# max =
# spare = 1
# uses = 0
# lifetime = 0
# password = 'supersecret'
# database = 0
# pool {
-# start = 0
-# min = 0
-# max =
+ start = 0
+ min = 0
+# max =
# spare = 1
# uses = 0
# lifetime = 0
# max_entries = 0
update {
&reply.Reply-Message := &reply.Reply-Message
- &reply.Reply-Message := "Cache last updated at %t"
- &reply.Class := "%{randstr:ssssssssssssssssssssssssssssssss}"
+ &reply.Reply-Message += "Cache last updated at %t"
+ &reply.Class := "%randstr(ssssssssssssssssssssssssssssssss)"
}
}
```
start:: Connections to create during module instantiation.
-If the server cannot create specified number of connections
-during instantiation it will exit.
-Set to `0` to allow the server to start without the couchbase
-being available.
+
+If the server cannot create specified number of
+connections during instantiation it will exit.
+Set to `0` to allow the server to start without the
+external service being available.
If these connections are all in use and a new one
is requested, the request will NOT get a connection.
-Setting `max` to LESS than the number of threads means
+Setting `max` to *LESS* than the number of threads means
that some threads may starve, and you will see errors
-like '_No connections available and at max connection limit_'
+like _No connections available and at max connection limit_.
Setting `max` to MORE than the number of threads means
that there are more connections than necessary.
+If `max` is not specified, then it defaults to the number
+of workers configured.
+
spare:: Spare connections to be left idle.
&Acct-Output-Gigawords = 'outputGigawords'
&Event-Timestamp = 'lastUpdated'
}
- user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"
+ user_key = "raduser_%md5(%tolower(%{%{Stripped-User-Name}:-%{User-Name}}))"
# read_clients = no
client {
view = "_design/client/_view/by_id"
### xlat expansions
-The `date` module defines an expansion `%{date:}` When the
+The `date` module defines an expansion `%date()` When the
expansion is not passed an argument, it returns the current date
printed according to the `format` string defined above.
."Attribute" mode:
-If the argument to `%{date:...}` is an attribute of `date` or
+If the argument to `%date(...)` is an attribute of `date` or
`integer` type, the date used will be the time given by the
relevant attribute. If the attributes is of type `string`, the
string will be parsed according to the `format` configuration,
and a Unix date will be returned, as integer seconds since the epoch.
-For example, `%{date:&Event-Timestamp}` will use the date from the
+For example, `%date(&Event-Timestamp)` will use the date from the
`link:https://freeradius.org/rfc/rfc2869.html#Event-Timestamp[Event-Timestamp]` attribute as the source of the date for printing.
."Get time" mode:
string is interpreted as if the string had been given in the
`format` configuration item.
-For example `%{date:'+%A'}` will return `Monday` if today is Monday.
+For example `%date('+%A')` will return `Monday` if today is Monday.
Note that the `%` character is special for xlat expansions, and therefore
either has to be "protected" by string quotation, or the `%` character has
-to be escaped itself, as in `%{date:+%%A}`
+to be escaped itself, as in `%date(+%%A)`
."Integer output"
ca_file = ${cadir}/rsa/ca.pem
# psk_identity = "test"
# psk_hexphrase = "036363823"
-# psk_query = "%{sql:select hex(key) from psk_keys where keyid = '%{TLS-PSK-Identity}'}"
+# psk_query = "%sql(select hex(key) from psk_keys where keyid = '%{TLS-PSK-Identity}')"
dh_file = ${certdir}/dh
# fragment_size = 1024
ca_path = ${cadir}
the `instantiate` section. You can then do dynamic translation of
attributes like:
- Attribute-Name = "%exec(/path/to/program,args, ...)"
+ Attribute-Name = %exec(/path/to/program,args, ...)
+
+The output of `exec` is interpreted as the data type of the attribute.
+
+The output of `exec` can even be pairs, such as `User-Name = foo`.
+Those pairs can be assigned to a list. If the program fails, it
+will output nothing.
+
+Note that when the assignment is to a list, the `exec` call _must_ be
+inside of a double-quoted string.
+
+ &request += "%exec(/path/to/program,args, ...)"
The value of the attribute will be replaced with the output of the
-program which is executed. Due to RADIUS protocol limitations,
-any output over `253` bytes will be ignored.
+program which is executed.
-The RADIUS attributes from the list referenced in the `input_pairs`
+The attributes from the list referenced in the `input_pairs`
configuraton item will be placed into environment variables of the executed
-program, as described in `man unlang` and in `doc/unlang/xlat.adoc`
+program.
Alternatively, by setting the `program` item of the module configuration,
the module can be called as a module rather than as an xlat function.
[NOTE]
====
-The expansion string: `%{idn:example.com}` results in an ASCII
+The expansion string: `%idn(example.com)` results in an ASCII
punycode version of the domain name. That version can then be used
for name comparisons. Using an `i18n` version of the name is *NOT
RECOMMENDED*, as that version is not canonical.
rlm_json provides the below xlat functions to handle the JSON documents.
-### %{json_jpath_validate:...}
+### %json_jpath_validate(...)
Determine if a jpath expression is valid.
[source,unlang]
----
&control.Tmp-String-0 := '$.my.json.payload[1]'
-&reply.Reply-Message := "Validation of %{control.Tmp-String-0} is %{json_jpath_validate:$.my.json.payload[1]}"
+&reply.Reply-Message := "Validation of %{control.Tmp-String-0} is %json_jpath_validate($.my.json.payload[1])"
----
.Output
Validation of $.my.json.payload[1] is 20:$.my.json.payload[1]
```
-### %{json_quote:...}
+### %json_quote(...)
Escapes string for use as a JSON string.
[source,unlang]
----
&control.Tmp-String-0 := "caipirinha/gelada"
-&reply.Reply-Message := "The string %{control.Tmp-String-0} should be %{json_quote:%{control.Tmp-String-0}} to be a valid JSON string."
+&reply.Reply-Message := "The string %{control.Tmp-String-0} should be %json_quote(%{control.Tmp-String-0}) to be a valid JSON string."
----
.Output
The string caipirinha/gelada should be caipirinha\\/gelada to be a valid JSON string.
```
-### %{json.encode:...}
+### %json.encode(...)
Generates a JSON document from a given list of attribute templates. The
format of document generated can be controlled with the 'encode' section in
.Example
```
-%{json.encode:&User-Name &Calling-Station-Id}
+%json.encode(&User-Name, &Calling-Station-Id)
```
The following will include all attributes in the RADIUS request, except for
.Example
```
-%{json.encode:&request[*] !&User-Password}
+%json.encode(&request[*], !&User-Password)
```
In another (contrived) example, all the attributes in the RADIUS request will
.Example
```
-%{json.encode:&request[*] !&reply[*] &control.User-Name}
+%json.encode(&request[*], !&reply[*], &control.User-Name)
```
#### Output format modes
The rlm_ldap provides the below xlat's functions.
-### %{ldap_escape:...}
+### %ldap_escape(...}
Escape a string for use in an LDAP filter or DN.
&Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?(objectClass=radiusprofile)"
}
update reply {
- &Reply-Message := "The LDAP url is %{ldap_escape:%{control.Tmp-String-0}}"
+ &Reply-Message := "The LDAP url is %ldap_escape(%{control.Tmp-String-0}}"
}
----
"The LDAP url is ldap:///ou=profiles,dc=example,dc=com??sub?\28objectClass=radiusprofile\29"
```
-### %{ldap_unescape:...}
+### %ldap_unescape(...)
Unescape a string for use in an LDAP filter or DN.
&Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?\28objectClass=radiusprofile\29"
}
update reply {
- &Reply-Message := "The LDAP url is %{ldap_unescape:%{control.Tmp-String-0}}"
+ &Reply-Message := "The LDAP url is %ldap_unescape(%{control.Tmp-String-0})"
}
----
# attribute_suspended = 'radiusProfileDn'
}
accounting {
- reference = "%{tolower:type.%{Acct-Status-Type}}"
+ reference = "%tolower(type.%{Acct-Status-Type})"
type {
start {
update {
The module also registers a Unix group expansion, where it is possible
to check if the user is a member of a particular Unix group.
- if (%{unix.group: admin}) { ...
+ if (%unix.group(admin)) { ...
The expansion returns `true` if the `link:https://freeradius.org/rfc/rfc2865.html#User-Name[User-Name]` is a membber of the given
group, and `false` otherwise.
group { ... }:: Group membership checking.
-Groups can be checked via the expansion `%{winbind.group:<name>}`
+Groups can be checked via the expansion `%winbind.group(<name>)`
search_username:: AD username to search for group searches.
pool { ... }:: Connection pool parameters.
-start::
+start:: Connections to create during module instantiation.
-Connections to create during module instantiation.
If the server cannot create specified number of
connections during instantiation it will exit.
-
Set to `0` to allow the server to start without the
-winbind daemon being available.
+external service being available.
If these connections are all in use and a new one
is requested, the request will NOT get a connection.
-Setting `max` to LESS than the number of threads means
+Setting `max` to *LESS* than the number of threads means
that some threads may starve, and you will see errors
-like 'No connections available and at max connection limit'
+like _No connections available and at max connection limit_.
Setting `max` to MORE than the number of threads means
that there are more connections than necessary.
+If `max` is not specified, then it defaults to the number
+of workers configured.
uses:: Number of uses before the connection is closed.
The rlm_yubikey provides the below xlat's functions.
-### %{modhextohex:...}
+### %modhextohex(...)
Convert Yubikey modhex to standard hex.
[source,unlang]
----
-"%{modhextohex:vvrbuctetdhc}" == "ffc1e0d3d260"
+"%modhextohex(vvrbuctetdhc)" == "ffc1e0d3d260"
----
.Output
# client_id = 00000
# api_key = '000000000000000000000000'
pool {
- start = 0
- min = 0
-# max =
+ start = 0
+ min = 0
+# max =
uses = 0
retry_delay = 30
lifetime = 86400
# .Example
#
# ```
-# %{db_status:ok}
-# %{db_status:fail}
-# %{db_status:notfound}
+# %db_status(ok)
+# %db_status(fail)
+# %db_status(notfound)
# ...
# ```
#
# .Example
#
# ```
-# %{db_status:}
+# %db_status()
# ```
#
&reply.Reply-Message += "Cache last updated at %t"
# Add your own value for `Class`.
- &reply.Class := "%{randstr:ssssssssssssssssssssssssssssssss}"
+ &reply.Class := "%randstr(ssssssssssssssssssssssssssssssss)"
}
#
#
# user_key:: Couchbase document key for user documents (`unlang` supported).
#
- user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"
+ user_key = "raduser_%md5(%tolower(%{%{Stripped-User-Name}:-%{User-Name}}))"
#
# read_clients:: Set to `yes` to read radius clients from the Couchbase view specified below.
#
# ### xlat expansions
#
-# The `date` module defines an expansion `%{date:}` When the
+# The `date` module defines an expansion `%date()` When the
# expansion is not passed an argument, it returns the current date
# printed according to the `format` string defined above.
#
# ."Attribute" mode:
#
-# If the argument to `%{date:...}` is an attribute of `date` or
+# If the argument to `%date(...)` is an attribute of `date` or
# `integer` type, the date used will be the time given by the
# relevant attribute. If the attributes is of type `string`, the
# string will be parsed according to the `format` configuration,
# and a Unix date will be returned, as integer seconds since the epoch.
#
-# For example, `%{date:&Event-Timestamp}` will use the date from the
+# For example, `%date(&Event-Timestamp)` will use the date from the
# `Event-Timestamp` attribute as the source of the date for printing.
#
# ."Get time" mode:
# string is interpreted as if the string had been given in the
# `format` configuration item.
#
-# For example `%{date:'+%A'}` will return `Monday` if today is Monday.
+# For example `%date('+%A')` will return `Monday` if today is Monday.
#
# Note that the `%` character is special for xlat expansions, and therefore
# either has to be "protected" by string quotation, or the `%` character has
-# to be escaped itself, as in `%{date:+%%A}`
+# to be escaped itself, as in `%date(+%%A)`
#
# ."Integer output"
#
# Note that this query is just an example. You will
# need to customize it for your installation.
#
-# psk_query = "%{sql:select hex(key) from psk_keys where keyid = '%{TLS-PSK-Identity}'}"
+# psk_query = "%sql(select hex(key) from psk_keys where keyid = '%{TLS-PSK-Identity}')"
#
# For DH cipher suites to work, you have to run OpenSSL to
#
# [NOTE]
# ====
-# The expansion string: `%{idn:example.com}` results in an ASCII
+# The expansion string: `%idn(example.com)` results in an ASCII
# punycode version of the domain name. That version can then be used
# for name comparisons. Using an `i18n` version of the name is *NOT
# RECOMMENDED*, as that version is not canonical.
#
# rlm_json provides the below xlat functions to handle the JSON documents.
#
-# ### %{json_jpath_validate:...}
+# ### %json_jpath_validate(...)
#
# Determine if a jpath expression is valid.
#
# [source,unlang]
# ----
# &control.Tmp-String-0 := '$.my.json.payload[1]'
-# &reply.Reply-Message := "Validation of %{control.Tmp-String-0} is %{json_jpath_validate:$.my.json.payload[1]}"
+# &reply.Reply-Message := "Validation of %{control.Tmp-String-0} is %json_jpath_validate($.my.json.payload[1])"
# ----
#
# .Output
# Validation of $.my.json.payload[1] is 20:$.my.json.payload[1]
# ```
#
-# ### %{json_quote:...}
+# ### %json_quote(...)
#
# Escapes string for use as a JSON string.
#
# [source,unlang]
# ----
# &control.Tmp-String-0 := "caipirinha/gelada"
-# &reply.Reply-Message := "The string %{control.Tmp-String-0} should be %{json_quote:%{control.Tmp-String-0}} to be a valid JSON string."
+# &reply.Reply-Message := "The string %{control.Tmp-String-0} should be %json_quote(%{control.Tmp-String-0}) to be a valid JSON string."
# ----
#
# .Output
# The string caipirinha/gelada should be caipirinha\\/gelada to be a valid JSON string.
# ```
#
-# ### %{json.encode:...}
+# ### %json.encode(...)
#
# Generates a JSON document from a given list of attribute templates. The
# format of document generated can be controlled with the 'encode' section in
# .Example
#
# ```
-# %{json.encode:&User-Name &Calling-Station-Id}
+# %json.encode(&User-Name, &Calling-Station-Id)
# ```
#
# The following will include all attributes in the RADIUS request, except for
# .Example
#
# ```
-# %{json.encode:&request[*] !&User-Password}
+# %json.encode(&request[*], !&User-Password)
# ```
#
# In another (contrived) example, all the attributes in the RADIUS request will
# .Example
#
# ```
-# %{json.encode:&request[*] !&reply[*] &control.User-Name}
+# %json.encode(&request[*], !&reply[*], &control.User-Name)
# ```
#
# #### Output format modes
# replaced with a single attribute.
#
accounting {
- reference = "%{tolower:type.%{Acct-Status-Type}}"
+ reference = "%tolower(type.%{Acct-Status-Type})"
type {
start {
# manage_interval = 0.2
#
- # request:: Options specific to requests handled by this connection pool
+ # request:: Options specific to requests handled by this connection pool
#
request {
#
#
# The rlm_ldap provides the below xlat's functions.
#
-# ### %{ldap_escape:...}
+# ### %ldap_escape(...}
#
# Escape a string for use in an LDAP filter or DN.
#
# &Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?(objectClass=radiusprofile)"
# }
# update reply {
-# &Reply-Message := "The LDAP url is %{ldap_escape:%{control.Tmp-String-0}}"
+# &Reply-Message := "The LDAP url is %ldap_escape(%{control.Tmp-String-0}}"
# }
# ----
#
# "The LDAP url is ldap:///ou=profiles,dc=example,dc=com??sub?\28objectClass=radiusprofile\29"
# ```
#
-# ### %{ldap_unescape:...}
+# ### %ldap_unescape(...)
#
# Unescape a string for use in an LDAP filter or DN.
#
# &Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?\28objectClass=radiusprofile\29"
# }
# update reply {
-# &Reply-Message := "The LDAP url is %{ldap_unescape:%{control.Tmp-String-0}}"
+# &Reply-Message := "The LDAP url is %ldap_unescape(%{control.Tmp-String-0})"
# }
# ----
#
# The module also registers a Unix group expansion, where it is possible
# to check if the user is a member of a particular Unix group.
#
-# if (%{unix.group: admin}) { ...
+# if (%unix.group(admin)) { ...
#
# The expansion returns `true` if the `User-Name` is a membber of the given
# group, and `false` otherwise.
#
# group { ... }:: Group membership checking.
#
- # Groups can be checked via the expansion `%{winbind.group:<name>}`
+ # Groups can be checked via the expansion `%winbind.group(<name>)`
#
group {
#
#
# The rlm_yubikey provides the below xlat's functions.
#
-# ### %{modhextohex:...}
+# ### %modhextohex(...)
#
# Convert Yubikey modhex to standard hex.
#
#
# [source,unlang]
# ----
-# "%{modhextohex:vvrbuctetdhc}" == "ffc1e0d3d260"
+# "%modhextohex(vvrbuctetdhc)" == "ffc1e0d3d260"
# ----
#
# .Output