From: Nick Porter Date: Thu, 18 Mar 2021 16:36:05 +0000 (+0000) Subject: v4: define arguments for %(concat: ) xlat (#3995) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9b9aebbca70173b8887fdec78d3ef0104fa2eadf;p=thirdparty%2Ffreeradius-server.git v4: define arguments for %(concat: ) xlat (#3995) * Define args for concat xlat * Update concat tests for new syntax and add extra tests * Amend other tests using concat xlat to correct syntax * Update docs for concat xlat * Add %(join: ) xlat * Add test for %(join: ) xlat * Add %(concat: ) example that uses %(join: ) --- diff --git a/doc/antora/modules/reference/pages/xlat/builtin.adoc b/doc/antora/modules/reference/pages/xlat/builtin.adoc index 3e2b8391223..adafeeea490 100644 --- a/doc/antora/modules/reference/pages/xlat/builtin.adoc +++ b/doc/antora/modules/reference/pages/xlat/builtin.adoc @@ -311,9 +311,9 @@ Failure in test at line /path/raddb/sites-enaled/default:231 == String manipulation -=== %{concat: <&ref:[idx]>} +=== %(concat:<&ref:[idx]> ) -Used to join two or more attributes, separated by a delimiter. +Used to join two or more attributes, separated by an optional delimiter. .Return: _string_ @@ -328,8 +328,8 @@ update { } update reply { - &Reply-Message += "%{concat:, %{control.Tmp-String-0[*]}}" - &Reply-Message += "%{concat:,%{control.Tmp-String-0[*]}}" + &Reply-Message += "%(concat:%{control.Tmp-String-0[*]} ', ')" + &Reply-Message += "%(concat:%{control.Tmp-String-0[*]} ,)" } ---- @@ -346,7 +346,7 @@ Split an attribute into multiple new attributes based on a delimiter. The original input attribute is removed, and is replaced with multiple attributes of the same name, but which contain the "exploded" values. -This expansion is the opposite of `%{concat: ... }`. +This expansion is the opposite of `%(concat: ... )`. .Return: _the number of total new attributes_. diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index 9cddd687d8b..e312a737bb7 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -1862,15 +1862,22 @@ finish: return XLAT_ACTION_DONE; } +static xlat_arg_parser_t const xlat_func_concat_args[] = { + { .required = true, .type = FR_TYPE_VOID }, + { .concat = true, .type = FR_TYPE_STRING }, + XLAT_ARG_PARSER_TERMINATOR +}; -/** Concatenate values of given attributes using separator +/** Concatenate string representation of values of given attributes using separator * - * First char of xlat is the separator, followed by attributes + * First argument of is the list of attributes to concatenate, followed + * by an optional separator * * Example: @verbatim -"%{concat:, %{User-Name}%{Calling-Station-Id}" == "bob, aa:bb:cc:dd:ee:ff" -"%{concat:, %{request[*]}" == ", , , ..." +"%(concat:%{request[*]} ,)" == ",,,..." +"%(concat:%{Tmp-String-0[*]} '. ')" == ". . . ..." +"%(concat:%(join:%{User-Name} %{Calling-Station-Id}) ', ')" == "bob, aa:bb:cc:dd:ee:ff" @endverbatim * * @ingroup xlat_functions @@ -1880,44 +1887,25 @@ static xlat_action_t xlat_func_concat(TALLOC_CTX *ctx, fr_dcursor_t *out, fr_value_box_list_t *in) { fr_value_box_t *result; - fr_value_box_t *separator; + fr_value_box_t *list = fr_dlist_head(in); + fr_value_box_t *separator = fr_dlist_next(in, list); char *buff; char const *sep; - /* - * If there's no input, there's no output - */ - if (fr_dlist_empty(in)) return XLAT_ACTION_DONE; - - /* - * Separator is first value box - */ - separator = fr_dlist_pop_head(in); - - if (!separator) { - REDEBUG("Missing separator for concat xlat"); - return XLAT_ACTION_FAIL; - } - - sep = separator->vb_strvalue; + sep = (separator) ? separator->vb_strvalue : ""; - result = fr_value_box_alloc_null(ctx); + result = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL, false); if (!result) { error: RPEDEBUG("Failed concatenating input"); return XLAT_ACTION_FAIL; } - buff = fr_value_box_list_aprint(result, in, sep, NULL); + buff = fr_value_box_list_aprint(result, &list->vb_group, sep, NULL); if (!buff) goto error; fr_value_box_bstrdup_buffer_shallow(NULL, result, NULL, buff, fr_value_box_list_tainted(in)); - /* - * Return separator to the start of the vb list - */ - fr_dlist_insert_head(in, separator); - fr_dcursor_append(out, result); return XLAT_ACTION_DONE; @@ -2050,6 +2038,31 @@ static xlat_action_t xlat_func_hmac_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out, return xlat_hmac(ctx, out, request, xlat_inst, xlat_thread_inst, in, digest, SHA1_DIGEST_LENGTH, HMAC_SHA1); } +static xlat_arg_parser_t const xlat_func_join_args[] = { + { .required = true, .variadic = true, .type = FR_TYPE_VOID }, + XLAT_ARG_PARSER_TERMINATOR +}; + +/** Join a series of arguments to form a single list + * + */ +static xlat_action_t xlat_func_join(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, + UNUSED request_t *request, UNUSED void const *xlat_inst, + UNUSED void *xlat_thread_inst, fr_value_box_list_t *in) +{ + fr_value_box_t *arg = NULL, *vb, *p; + + while ((arg = fr_dlist_next(in, arg))) { + fr_assert(arg->type == FR_TYPE_GROUP); + vb = fr_dlist_head(&arg->vb_group); + while (vb) { + p = fr_dlist_remove(&arg->vb_group, vb); + fr_dcursor_append(out, vb); + vb = fr_dlist_next(&arg->vb_group, p); + } + } + return XLAT_ACTION_DONE; +} /** Return the on-the-wire size of the boxes in bytes * @@ -3358,6 +3371,8 @@ do { \ xlat_func_args(xlat, _args); \ } while (0) + XLAT_REGISTER_ARGS("concat", xlat_func_concat, xlat_func_concat_args); + XLAT_REGISTER_ARGS("join", xlat_func_join, xlat_func_join_args); XLAT_REGISTER_ARGS("pairs", xlat_func_pairs, xlat_func_pairs_args); XLAT_REGISTER_ARGS("rpad", xlat_func_rpad, xlat_func_rpad_args); @@ -3370,7 +3385,6 @@ do { \ XLAT_REGISTER_MONO("base64", xlat_func_base64_encode, xlat_func_base64_encode_arg); XLAT_REGISTER_MONO("base64decode", xlat_func_base64_decode, xlat_func_base64_decode_arg); XLAT_REGISTER_MONO("bin", xlat_func_bin, xlat_func_bin_arg); - xlat_register(NULL, "concat", xlat_func_concat, false); XLAT_REGISTER_MONO("hex", xlat_func_hex, xlat_func_hex_arg); xlat_register(NULL, "hmacmd5", xlat_func_hmac_md5, false); xlat_register(NULL, "hmacsha1", xlat_func_hmac_sha1, false); diff --git a/src/tests/keywords/concat b/src/tests/keywords/concat index 695f4dcdcc1..bbe62fba3f5 100644 --- a/src/tests/keywords/concat +++ b/src/tests/keywords/concat @@ -8,8 +8,11 @@ update { } update { - &request.Tmp-String-1 := "%{concat:, %{request[*]}}" - &request.Tmp-String-2 := "%{concat:, %{Tmp-String-0[*]}}" + &request.Tmp-String-1 := "%(concat:%{request[*]} ', ')" + &request.Tmp-String-2 := "%(concat:%{Tmp-String-0[*]} ', ')" + &request.Tmp-String-3 := "%(concat:%{Tmp-String-0[*]})" + &request.Tmp-String-4 := "%(concat:%{Tmp-String-0[*]} ,)" + &request.Tmp-String-5 := "%(concat:%{Tmp-String-0[*]} |-)" } if (&request.Tmp-String-1 != "bob, hello, ab c, de fg, 123") { @@ -20,4 +23,19 @@ if (&request.Tmp-String-2 != "ab c, de fg") { test_fail } +# Empty separator +if (&request.Tmp-String-3 != "ab cde fg") { + test_fail +} + +# Single character separator +if (&request.Tmp-String-4 != "ab c,de fg") { + test_fail +} + +# Multi character separator not delimited +if (&request.Tmp-String-5 !="ab c|-de fg") { + test_fail +} + success diff --git a/src/tests/keywords/join b/src/tests/keywords/join new file mode 100644 index 00000000000..ad5ad3d1d70 --- /dev/null +++ b/src/tests/keywords/join @@ -0,0 +1,25 @@ +# +# PRE: update if concat +# + +update { + &request.Tmp-String-0 := "ab c" + &request.Tmp-String-0 += "de fg" + &request.Tmp-Integer-0 := 123 + &control.Tmp-IP-Address-0 := 192.168.1.254 +} + +update { + &request.Tmp-String-1 := "%(concat:%(join:%{request[*]} %{control.Tmp-IP-Address-0}) '. ')" + &request.Tmp-String-2 := "%(concat:%(join:%{Tmp-String-0[*]} %{Tmp-Integer-0}) ,)" +} + +if (&request.Tmp-String-1 != "bob. hello. ab c. de fg. 123. 192.168.1.254") { + test_fail +} + +if (&request.Tmp-String-2 != "ab c,de fg,123") { + test_fail +} + +success \ No newline at end of file diff --git a/src/tests/keywords/pairs b/src/tests/keywords/pairs index 686795b4219..fffb1535b97 100644 --- a/src/tests/keywords/pairs +++ b/src/tests/keywords/pairs @@ -9,9 +9,9 @@ update { } update { - &request.Tmp-String-1 := "%{concat:, %(pairs:request[*])}" + &request.Tmp-String-1 := "%(concat:%(pairs:request[*]) ', ')" &request.Tmp-String-2 := "%(pairs:Tmp-String-0)" - &request.Tmp-String-3 := "%{concat:, %(pairs:Tmp-String-0[*])}" + &request.Tmp-String-3 := "%(concat:%(pairs:Tmp-String-0[*]) ', ')" &request.Tmp-String-4 := "%(pairs:control.)" &request.Tmp-String-5 := "%(pairs:control.User-Name)" } diff --git a/src/tests/keywords/xlat-attr-index b/src/tests/keywords/xlat-attr-index index 5c454e14283..5d9757acb56 100644 --- a/src/tests/keywords/xlat-attr-index +++ b/src/tests/keywords/xlat-attr-index @@ -24,7 +24,7 @@ if ("%{Tmp-IP-Address-0[*]}" != '192.0.2.1,192.0.2.2') { update request { &Tmp-IP-Address-1 += "%{Tmp-IP-Address-0[1]}" &Tmp-IP-Address-1 += "%{Tmp-IP-Address-0[0]}" - &Tmp-String-0 = "%{concat:,%{Tmp-IP-Address-0[*]}}" + &Tmp-String-0 = "%(concat:%{Tmp-IP-Address-0[*]} ,)" &Tmp-Integer-0 = "%{Tmp-IP-Address-0[#]}" } diff --git a/src/tests/keywords/xlat-list b/src/tests/keywords/xlat-list index b0b2d55a866..84f549f0c50 100644 --- a/src/tests/keywords/xlat-list +++ b/src/tests/keywords/xlat-list @@ -33,7 +33,7 @@ if ("%{control[*]}" != '192.0.2.1,192.0.2.2') { update request { &Tmp-IP-Address-1 += "%{control[1]}" &Tmp-IP-Address-1 += "%{control[0]}" - &Tmp-String-0 = "%{concat:,%{control[*]}}" + &Tmp-String-0 = "%(concat:%{control[*]} ,)" &Tmp-Integer-0 = "%{control[#]}" }