]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: define arguments for %(concat: ) xlat (#3995)
authorNick Porter <nick@portercomputing.co.uk>
Thu, 18 Mar 2021 16:36:05 +0000 (16:36 +0000)
committerGitHub <noreply@github.com>
Thu, 18 Mar 2021 16:36:05 +0000 (16:36 +0000)
* 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: )

doc/antora/modules/reference/pages/xlat/builtin.adoc
src/lib/unlang/xlat_builtin.c
src/tests/keywords/concat
src/tests/keywords/join [new file with mode: 0644]
src/tests/keywords/pairs
src/tests/keywords/xlat-attr-index
src/tests/keywords/xlat-list

index 3e2b8391223c3293d821d18ac04ae05a672934bb..adafeeea490fd45ac44c0250a0041936d15536d6 100644 (file)
@@ -311,9 +311,9 @@ Failure in test at line /path/raddb/sites-enaled/default:231
 
 == String manipulation
 
-=== %{concat:<delim> <&ref:[idx]>}
+=== %(concat:<&ref:[idx]> <delim>)
 
-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_.
 
index 9cddd687d8b80e585805b0501bd916c2a9346ba2..e312a737bb72a168118fd149edaabb0c219a0ed7 100644 (file)
@@ -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[*]}" == "<attr1value>, <attr2value>, <attr3value>, ..."
+"%(concat:%{request[*]} ,)" == "<attr1value>,<attr2value>,<attr3value>,..."
+"%(concat:%{Tmp-String-0[*]} '. ')" == "<str1value>. <str2value>. <str3value>. ..."
+"%(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);
index 695f4dcdcc15d18cd7f1a1a26d1df8c4387307f4..bbe62fba3f59f73606321ab20db3ac5ea82cd9f1 100644 (file)
@@ -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 (file)
index 0000000..ad5ad3d
--- /dev/null
@@ -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
index 686795b421919485562dce23b0e4a56909f84e74..fffb1535b9768f7ab9a0b70cbcbffe1128a5fd7b 100644 (file)
@@ -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)"
 }
index 5c454e142833e278aa904f4cf0fb4eca4863fdd2..5d9757acb567367a8d39996d8f4d0047e716cfe9 100644 (file)
@@ -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[#]}"
 }
 
index b0b2d55a8669b41b916afc58027399099b9b959d..84f549f0c502cef0d616aef7fc34f8a5d2f19821 100644 (file)
@@ -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[#]}"
 }