From: Alan T. DeKok Date: Fri, 13 Sep 2024 12:55:07 +0000 (-0400) Subject: parse key variable, create it, and test it X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cf76b4d09e2cf3f533393c3e089e0f9d2776455c;p=thirdparty%2Ffreeradius-server.git parse key variable, create it, and test it string path "foo.bar.baz[0]" for attributes, and a numerical index for xlat expansions --- diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index 562e055800d..9797df34852 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -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; diff --git a/src/lib/unlang/foreach.c b/src/lib/unlang/foreach.c index ce6fd45d259..8783c5fc6e0 100644 --- a/src/lib/unlang/foreach.c +++ b/src/lib/unlang/foreach.c @@ -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); } diff --git a/src/tests/keywords/foreach-key b/src/tests/keywords/foreach-key index 0e97dc6eb5c..430a1b72021 100644 --- a/src/tests/keywords/foreach-key +++ b/src/tests/keywords/foreach-key @@ -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 }