]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add and use fr_pair_legacy_print_nested flag
authorAlan T. DeKok <aland@freeradius.org>
Tue, 8 Aug 2023 19:59:20 +0000 (15:59 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 8 Aug 2023 20:00:33 +0000 (16:00 -0400)
so that we can update the tests to print flat attributes as nested,
without updating all of the tests.

The next step is to set the flag in the tests, so that the tests
will parse flat attributes, but will always print nested ones.

src/bin/unit_test_attribute.c
src/lib/util/pair_legacy.c
src/lib/util/pair_legacy.h
src/lib/util/pair_print.c
src/tests/unit/file_unflatten.txt [new file with mode: 0644]

index edadb9d1ecc20e1a429fb92dc2b709032e01bf01..662a295a6497cdee5fefeeac5f11be7f4105d7d3 100644 (file)
@@ -2385,6 +2385,10 @@ static size_t command_migrate(command_result_t *result, UNUSED command_file_ctx_
                p += sizeof("pair_legacy_nested") - 1;
                out = &fr_pair_legacy_nested;
 
+       } else if (strncmp(p, "pair_legacy_print_nested", sizeof("pair_legacy_print_nested") - 1) == 0) {
+               p += sizeof("pair_legacy_print_nested") - 1;
+               out = &fr_pair_legacy_print_nested;
+
        } else if (strncmp(p, "parse_new_conditions", sizeof("parse_new_conditions") - 1) == 0) {
                p += sizeof("parse_new_conditions") - 1;
                out = &parse_new_conditions;
index f309ca0a783ccd31e3caaadc04b61d1ddd0aebb5..db63beeed5d63e590fd9bfefc4f3dc07c700abd8 100644 (file)
@@ -33,6 +33,7 @@ RCSID("$Id$")
 #include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
 
 bool fr_pair_legacy_nested = false;
+bool fr_pair_legacy_print_nested = false;
 
 static fr_sbuff_term_t const   bareword_terminals =
                                FR_SBUFF_TERMS(
index 065f54e0aa8649c10ab8f57893c2c35b02be5a93..f0354377e91c8d645e3070cde99a81de5513a405 100644 (file)
@@ -34,7 +34,8 @@ RCSIDH(pair_legacy_h, "$Id$")
 extern "C" {
 #endif
 
-extern bool    fr_pair_legacy_nested; /* for migratipon from legacy flat to new nested */
+extern bool    fr_pair_legacy_nested; /* parsing: for migration from legacy flat to new nested */
+extern bool    fr_pair_legacy_print_nested; /* printing: for migration from legacy flat to new nested */
 
 fr_token_t     fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_attr_t const *parent,
                                       char const *buffer, size_t len, fr_pair_list_t *head);
index 5241d2d35a98487f3a272020897abf488af3a65a..22f049818f43f42e40239fbefbe23b4e71622afc 100644 (file)
@@ -22,6 +22,8 @@
  */
 #include <freeradius-devel/util/pair.h>
 #include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/pair_legacy.h>
 
 /** Print the value of an attribute to a string
  *
@@ -46,10 +48,6 @@ ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_toke
         *      For structural types descend down
         */
        case FR_TYPE_STRUCTURAL:
-               /*
-                *      Serialize all child VPs as full quoted
-                *      <pair> = ["]<child>["]
-                */
                our_out = FR_SBUFF(out);
                FR_SBUFF_IN_CHAR_RETURN(&our_out, '{', ' ');
 
@@ -198,10 +196,61 @@ ssize_t fr_pair_print_secure(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_p
        FR_SBUFF_SET_RETURN(out, &our_out);
 }
 
+static ssize_t fr_pair_list_print_unflatten(fr_sbuff_t *out, fr_da_stack_t const *da_stack, unsigned int depth, fr_pair_list_t const *list, fr_pair_t **vp_p)
+{
+       fr_dict_attr_t const *parent;
+       fr_pair_t       *vp = *vp_p;
+       fr_pair_t       *next = fr_pair_list_next(list, vp);
+       fr_sbuff_t      our_out = FR_SBUFF(out);
+
+       fr_assert(depth >= 1);
+
+       /*
+        *      Not yet at the correct depth, print more parents.
+        */
+       if (depth < vp->da->depth) {
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, da_stack->da[depth - 1]->name);
+               FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, " = { ");
+
+               FR_SBUFF_RETURN(fr_pair_list_print_unflatten, &our_out, da_stack, depth + 1, list, vp_p);
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, " }");
+               FR_SBUFF_SET_RETURN(out, &our_out);
+       }
+
+       parent = vp->da->parent;
+
+       /*
+        *      We are at the correct depth.  Print out all of the VPs which match the parent at this depth.
+        */
+       while (vp->da->parent == parent) {
+               next = fr_pair_list_next(list, vp);
+
+               FR_SBUFF_RETURN(fr_pair_print, &our_out, vp->da->parent, vp);
+
+               /*
+                *      Flat structures are listed in order.  Which means as soon as we go from 1..N back to
+                *      1, we have ended the current structure.
+                */
+               if (next && (vp->da->parent->type == FR_TYPE_STRUCT) &&
+                   (next->da->parent == vp->da->parent) &&
+                   (next->da->attr < vp->da->attr)) {
+                       break;
+               }
+
+               vp = next;
+               if (!vp) break;
+
+               FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, ", ");
+       }
+
+       *vp_p = next;
+       FR_SBUFF_SET_RETURN(out, &our_out);
+}
+
 /** Print a pair list
  *
  * @param[in] out      Where to write the string.
- * @param[in] parent   parent da to start from.
+ * @param[in] parent   parent da to start from
  * @param[in] list     pair list
  * @return
  *     - Length of data written to out.
@@ -209,8 +258,8 @@ ssize_t fr_pair_print_secure(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_p
  */
 ssize_t fr_pair_list_print(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pair_list_t const *list)
 {
-       fr_pair_t *vp;
-       fr_sbuff_t our_out = FR_SBUFF(out);
+       fr_pair_t       *vp;
+       fr_sbuff_t      our_out = FR_SBUFF(out);
 
        vp = fr_pair_list_head(list);
        if (!vp) {
@@ -218,10 +267,37 @@ ssize_t fr_pair_list_print(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pai
                return fr_sbuff_used(out);
        }
 
+       /*
+        *      Groups are printed from the root.
+        */
+       if (parent && (parent->type == FR_TYPE_GROUP)) parent = NULL;
+
        while (true) {
-               FR_SBUFF_RETURN(fr_pair_print, &our_out, parent, vp);
+               unsigned int depth;
+               fr_da_stack_t da_stack;
+
+               fr_proto_da_stack_build(&da_stack, vp->da);
+
+               if (!fr_pair_legacy_print_nested ||
+                   (!parent && (vp->da->depth == 1)) ||
+                   (parent && (fr_dict_by_da(parent) != fr_dict_by_da(vp->da))) ||
+                   (parent && (da_stack.da[parent->depth] == parent) && (parent->depth + 1 == vp->da->depth))) {
+                       FR_SBUFF_RETURN(fr_pair_print, &our_out, parent, vp);
+                       vp = fr_pair_list_next(list, vp);
+
+               } else {
+                       /*
+                        *      We have to print a partial tree, starting from the root.
+                        */
+                       if (!parent) {
+                               depth = 1;
+                       } else {
+                               depth = parent->depth + 1;
+                       }
+
+                       FR_SBUFF_RETURN(fr_pair_list_print_unflatten, &our_out, &da_stack, depth, list, &vp);
+               }
 
-               vp = fr_pair_list_next(list, vp);
                if (!vp) break;
 
                FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, ", ");
diff --git a/src/tests/unit/file_unflatten.txt b/src/tests/unit/file_unflatten.txt
new file mode 100644 (file)
index 0000000..8469814
--- /dev/null
@@ -0,0 +1,42 @@
+#
+#  Tests for parsing files ala radclient or "users"
+#
+#  $Id$
+#
+
+proto-dictionary radius
+
+#
+#  The same as "file.txt", but where we set a migration flag which prints flat attributes as nested.
+#
+migrate pair_legacy_print_nested = true
+
+#
+#  Fully specified paths.
+#
+#  @todo - we arguably want to force nesting on these attributes?  Or change the nesting when printed?
+#
+read_file files/cisco_avpair.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+#
+#  Relative attributes, all on the same line.
+#
+read_file files/cisco_relative.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+#
+#  Relative attributes, each on a different line
+#
+read_file files/cisco_multiline_relative.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+
+#
+#  Multiple Cisco AVPAir, all on one line
+#
+read_file files/cisco_single_line.txt
+match User-Name = "bob", User-Password = "hello", Vendor-Specific = { Cisco = { AVPair = "1", AVPair += "2", AVPair += "3", AVPair += "4" } }
+
+count
+match 10