]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
parse key variable, create it, and test it
authorAlan T. DeKok <aland@freeradius.org>
Fri, 13 Sep 2024 12:55:07 +0000 (08:55 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 13 Sep 2024 13:39:17 +0000 (09:39 -0400)
string path "foo.bar.baz[0]" for attributes, and a numerical index
for xlat expansions

src/lib/unlang/compile.c
src/lib/unlang/foreach.c
src/tests/keywords/foreach-key

index 562e055800dfa2950d24a1439d126a277b0b7fde..9797df34852843ad562697e4148e1c77fd7da60c 100644 (file)
@@ -3180,6 +3180,9 @@ static unlang_t *compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx,
        fr_type_t               type;
        unlang_t                *c;
 
+       fr_type_t               key_type;
+       char const              *key_name;
+
        unlang_group_t          *g;
        unlang_foreach_t        *gext;
 
@@ -3296,6 +3299,14 @@ static unlang_t *compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx,
         */
        type_name = cf_section_argv(cs, 0); /* AFTER name1, name2 */
 
+       key_name = cf_section_argv(cs, 2);
+       if (key_name) {
+               key_type = fr_table_value_by_str(fr_type_table, key_name, FR_TYPE_VOID);
+       } else {
+               key_type = FR_TYPE_VOID;
+       }
+       key_name = cf_section_argv(cs, 3);
+
        if (tmpl_is_xlat(vpt)) {
                if (!type_name) {
                        /*
@@ -3311,7 +3322,19 @@ static unlang_t *compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx,
                        return NULL;
                }
 
+               if ((key_type != FR_TYPE_VOID) && !fr_type_is_numeric(key_type)) {
+                       cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be numeric", fr_type_to_str(key_type));
+                       return NULL;
+               }
+
                goto get_name;
+       } else {
+               fr_assert(tmpl_is_attr(vpt));
+
+               if ((key_type != FR_TYPE_VOID) && (key_type != FR_TYPE_STRING)) {
+                       cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be 'string'", fr_type_to_str(key_type));
+                       return NULL;
+               }
        }
 
        if (type_name) {
@@ -3368,6 +3391,16 @@ static unlang_t *compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx,
                 */
                gext->value = fr_dict_attr_by_name(NULL, var->root, variable_name);
                fr_assert(gext->value != NULL);
+
+               /*
+                *      Define the local key variable.  Note that we don't copy any children.
+                */
+               if (key_type != FR_TYPE_VOID) {
+                       if (define_local_variable(cf_section_to_item(cs), var, &t_rules, key_type, key_name, NULL) < 0) goto fail;
+
+                       gext->key = fr_dict_attr_by_name(NULL, var->root, key_name);
+                       fr_assert(gext->key != NULL);
+               }
        }
 
        if (!compile_children(g, &unlang_ctx2, true)) return NULL;
index ce6fd45d2599f64ed5591ecc3fc99d6c04cbe80b..8783c5fc6e03e67afafb0ff1bfe5126b2fb10a39 100644 (file)
@@ -45,6 +45,8 @@ static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
 
 static int xlat_foreach_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };     /* up to 10 for foreach */
 
+#define BUFFER_SIZE (256)
+
 /** State of a foreach loop
  *
  */
@@ -55,6 +57,9 @@ typedef struct {
        fr_pair_t               *value;                         //!< local variable which contains the value
        tmpl_t const            *vpt;                           //!< pointer to the vpt
 
+       uint32_t                index;                          //!< for xlat results
+       char                    *buffer;                        //!< for key values
+
        bool                    success;                        //!< for xlat expansion
        fr_value_box_list_t     list;                           //!< value box list for looping over xlats
 
@@ -302,6 +307,27 @@ static unlang_action_t unlang_foreach_xlat_init(rlm_rcode_t *p_result, request_t
        return UNLANG_ACTION_PUSHED_CHILD;
 }
 
+static int unlang_foreach_key_update(request_t *request, unlang_frame_state_foreach_t *state)
+{
+       fr_value_box_clear_value(&state->key->data);
+
+       if (tmpl_is_attr(state->vpt)) {
+               if (tmpl_dcursor_print(&FR_SBUFF_IN(state->buffer, BUFFER_SIZE), &state->cc) > 0) {
+                       fr_value_box_strdup(state->key, &state->key->data, NULL, state->buffer, false);
+               }
+       } else {
+               fr_value_box_t box;
+
+               fr_value_box(&box, state->index, false);
+
+               if (fr_value_box_cast(state->key, &state->key->data, state->key->vp_type, state->key->da, &box) < 0) {
+                       RDEBUG("Failed casting 'foreach' key variable '%s' from %u", state->key->da->name, state->index);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
 
 static unlang_action_t unlang_foreach_attr_next(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
 {
@@ -339,6 +365,12 @@ next:
                return UNLANG_ACTION_CALCULATE_RESULT;
        }
 
+       if (state->key) {
+               state->index++;
+
+               if (unlang_foreach_key_update(request, state) < 0) goto next;
+       }
+
        /*
         *      Copy the data.
         */
@@ -414,6 +446,16 @@ static unlang_action_t unlang_foreach_attr_init(rlm_rcode_t *p_result, request_t
        vp = tmpl_dcursor_init(NULL, NULL, &state->cc, &state->cursor, request, state->vpt);
        fr_assert(vp != NULL);
 
+       /*
+        *      Update the key with the current path or index.
+        */
+       if (state->key) {
+               if (unlang_foreach_key_update(request, state) < 0) {
+                       *p_result = RLM_MODULE_FAIL;
+                       return UNLANG_ACTION_CALCULATE_RESULT;                  
+               }
+       }
+
        if (vp->vp_type == FR_TYPE_GROUP) {
                fr_assert(state->value->vp_type == FR_TYPE_GROUP);
 
@@ -506,6 +548,17 @@ static unlang_action_t unlang_foreach(rlm_rcode_t *p_result, request_t *request,
                fr_assert(state->value != NULL);
 
                if (tmpl_is_attr(gext->vpt)) {
+                       if (gext->key) {
+                               if (fr_pair_append_by_da(request->local_ctx, &state->key, &request->local_pairs, gext->key) < 0) {
+                                       REDEBUG("Failed creating %s", gext->key->name);
+                                       *p_result = RLM_MODULE_FAIL;
+                                       return UNLANG_ACTION_CALCULATE_RESULT;
+                               }
+                               fr_assert(state->key != NULL);
+
+                               MEM(state->buffer = talloc_array(state, char, BUFFER_SIZE));
+                       }
+
                        return unlang_foreach_attr_init(p_result, request, frame, state);
                }
 
index 0e97dc6eb5c63f29590d19e2384f91389d0b7d61..430a1b7202116f9e5f8561e122420e34b9a7d8e9 100644 (file)
@@ -9,11 +9,14 @@ total = ""
 #  For now, just test that we can parse key things
 #
 foreach string key, string name (foo.User-Name[*]) {
+       total += key
+       total += " = "
        total += name
        total += ","
 }
 
-if (total != "a,c,") {
+
+if (total != "foo.User-Name[0] = a,foo.User-Name[1] = c,") {
        test_fail
 }