From: Arran Cudbard-Bell Date: Fri, 24 Jun 2022 18:51:35 +0000 (-0500) Subject: Support multiple levels of request qualifiers X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5cf1239df4d310ea4d719ce1c753b03154866912;p=thirdparty%2Ffreeradius-server.git Support multiple levels of request qualifiers --- diff --git a/src/bin/unit_test_attribute.c b/src/bin/unit_test_attribute.c index 50a4ae27ede..e038d9bc678 100644 --- a/src/bin/unit_test_attribute.c +++ b/src/bin/unit_test_attribute.c @@ -2417,9 +2417,9 @@ static size_t command_touch(command_result_t *result, UNUSED command_file_ctx_t /** Callback for a tmpl rule parser * */ -typedef ssize_t(*command_tmpl_rule_func)(tmpl_rules_t *rules, fr_sbuff_t *value); +typedef ssize_t(*command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value); -static ssize_t command_tmpl_rule_allow_foreign(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_allow_foreign(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { bool res; ssize_t slen; @@ -2429,7 +2429,7 @@ static ssize_t command_tmpl_rule_allow_foreign(tmpl_rules_t *rules, fr_sbuff_t * return slen; } -static ssize_t command_tmpl_rule_allow_unknown(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_allow_unknown(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { bool res; ssize_t slen; @@ -2439,7 +2439,7 @@ static ssize_t command_tmpl_rule_allow_unknown(tmpl_rules_t *rules, fr_sbuff_t * return slen; } -static ssize_t command_tmpl_rule_allow_unresolved(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_allow_unresolved(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { bool res; ssize_t slen; @@ -2449,7 +2449,7 @@ static ssize_t command_tmpl_rule_allow_unresolved(tmpl_rules_t *rules, fr_sbuff_ return slen; } -static ssize_t command_tmpl_rule_attr_parent(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_attr_parent(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { return fr_dict_attr_by_oid_substr(NULL, &rules->attr.parent, @@ -2458,7 +2458,7 @@ static ssize_t command_tmpl_rule_attr_parent(tmpl_rules_t *rules, fr_sbuff_t *va value, NULL); } -static ssize_t command_tmpl_rule_disallow_internal(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_disallow_internal(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { bool res; ssize_t slen; @@ -2468,7 +2468,7 @@ static ssize_t command_tmpl_rule_disallow_internal(tmpl_rules_t *rules, fr_sbuff return slen; } -static ssize_t command_tmpl_rule_disallow_qualifiers(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_disallow_qualifiers(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { bool res; ssize_t slen; @@ -2478,7 +2478,7 @@ static ssize_t command_tmpl_rule_disallow_qualifiers(tmpl_rules_t *rules, fr_sbu return slen; } -static ssize_t command_tmpl_rule_list_def(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_list_def(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { ssize_t slen; @@ -2492,13 +2492,16 @@ static ssize_t command_tmpl_rule_list_def(tmpl_rules_t *rules, fr_sbuff_t *value return slen; } -static ssize_t command_tmpl_rule_request_def(tmpl_rules_t *rules, fr_sbuff_t *value) +static ssize_t command_tmpl_rule_request_def(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value) { - ssize_t slen; - - fr_sbuff_out_by_longest_prefix(&slen, &rules->attr.request_def, tmpl_request_ref_table, value, REQUEST_UNKNOWN); + fr_slen_t slen; - if (rules->attr.request_def == REQUEST_UNKNOWN) { + slen = tmpl_request_ref_list_afrom_substr(ctx, NULL, + &rules->attr.request_def, + value, + NULL, + NULL); + if (slen < 0) { fr_strerror_printf("Invalid request specifier \"%pV\"", fr_box_strvalue_len(fr_sbuff_current(value), fr_sbuff_remaining(value))); } @@ -2547,7 +2550,7 @@ static size_t command_tmpl_rules(command_result_t *result, command_file_ctx_t *c fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL); - if (func(&cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR(); + if (func(cc->tmp_ctx, &cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR(); } return fr_sbuff_used(&sbuff); diff --git a/src/lib/server/map.c b/src/lib/server/map.c index 41984470dd1..a7d5acd6de0 100644 --- a/src/lib/server/map.c +++ b/src/lib/server/map.c @@ -657,6 +657,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S TALLOC_CTX *parent_ctx; tmpl_rules_t our_lhs_rules = *lhs_rules; /* Mutable copy of the destination */ + TALLOC_CTX *tmp_ctx = NULL; /* Temporary context for request lists */ /* * The first map has ctx as the parent context. @@ -671,15 +672,23 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S */ cs_list = p = cf_section_name2(cs); if (cs_list && (strcmp(cf_section_name1(cs), "update") == 0)) { - p += tmpl_request_ref_by_name(&our_lhs_rules.attr.request_def, p, REQUEST_CURRENT); - if (our_lhs_rules.attr.request_def == REQUEST_UNKNOWN) { + fr_slen_t slen; + + MEM(tmp_ctx = talloc_init_const("tmp")); + + slen = tmpl_request_ref_list_afrom_substr(ctx, NULL, &our_lhs_rules.attr.request_def, + &FR_SBUFF_IN(p, strlen(p)), NULL, NULL); + if (slen < 0) { cf_log_err(ci, "Default request specified in mapping section is invalid"); + talloc_free(tmp_ctx); return -1; } + p += slen; our_lhs_rules.attr.list_def = fr_table_value_by_str(pair_list_table, p, PAIR_LIST_UNKNOWN); if (our_lhs_rules.attr.list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified in mapping section is invalid", p); + talloc_free(tmp_ctx); return -1; } } @@ -694,6 +703,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S * Free in reverse as successive entries have their * prececessors as talloc parent contexts */ + talloc_free(tmp_ctx); map_list_talloc_reverse_free(out); return -1; } @@ -819,6 +829,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S map_list_insert_tail(out, map); } + talloc_free(tmp_ctx); return 0; } @@ -969,7 +980,7 @@ int map_afrom_vp(TALLOC_CTX *ctx, map_t **out, fr_pair_t *vp, tmpl_rules_t const tmpl_attr_set_leaf_da(map->lhs, vp->da); tmpl_attr_set_leaf_num(map->lhs, NUM_ANY); - tmpl_attr_set_request(map->lhs, rules->attr.request_def); + tmpl_attr_set_request_ref(map->lhs, rules->attr.request_def); tmpl_attr_set_list(map->lhs, rules->attr.list_def); tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL); @@ -1404,7 +1415,6 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f map_t exp_map; tmpl_t *exp_lhs; - tmpl_request_ref_t request_ref; tmpl_pair_list_t list_ref; tmpl_dcursor_ctx_t cc = {}; @@ -1488,11 +1498,9 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f } context = request; - request_ref = tmpl_request(map->lhs); - if (tmpl_request_ptr(&context, request_ref) < 0) { - REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to invalid request reference \"%s\" in left side of map", - (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name, - fr_table_str_by_value(tmpl_request_ref_table, request_ref, "")); + if (tmpl_request_ptr(&context, tmpl_request(map->lhs)) < 0) { + RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to error in left side of map", + (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name); rcode = -2; goto finish; } diff --git a/src/lib/server/map_async.c b/src/lib/server/map_async.c index 771d885cf21..3ab991224e6 100644 --- a/src/lib/server/map_async.c +++ b/src/lib/server/map_async.c @@ -212,14 +212,11 @@ static inline fr_pair_list_t *map_check_src_or_dst(request_t *request, map_t con { request_t *context = request; fr_pair_list_t *list; - tmpl_request_ref_t request_ref; tmpl_pair_list_t list_ref; - request_ref = tmpl_request(src_dst); - if (tmpl_request_ptr(&context, request_ref) < 0) { - REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to invalid request reference \"%s\"", - (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name, - fr_table_str_by_value(tmpl_request_ref_table, request_ref, "")); + if (tmpl_request_ptr(&context, tmpl_request(src_dst)) < 0) { + RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed", + (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name); return NULL; } diff --git a/src/lib/server/tmpl.h b/src/lib/server/tmpl.h index da08345fbba..bcca9d9b5c7 100644 --- a/src/lib/server/tmpl.h +++ b/src/lib/server/tmpl.h @@ -280,6 +280,11 @@ typedef enum { TMPL_ATTR_REF_PREFIX_AUTO //!< Attribute refs may have a '&' prefix. } tmpl_attr_prefix_t; +/** Define entry and head types for tmpl request references + * + */ +FR_DLIST_TYPES(tmpl_request_list) + struct tmpl_attr_rules_s { fr_dict_t const *dict_def; //!< Default dictionary to use ///< with unqualified attribute references. @@ -290,8 +295,18 @@ struct tmpl_attr_rules_s { ///< and the presence of any of those qualifiers ///< will be treated as an error. - tmpl_request_ref_t request_def; //!< Default request to use with + FR_DLIST_HEAD(tmpl_request_list) _CONST *request_def; //!< Default request to use with ///< unqualified attribute references. + ///< If NULL the request is assumed to + ///< but the current request. + ///< Usually this will be one of + ///< - tmpl_request_def_current + ///< - tmpl_request_def_outer + ///< - tmpl_request_def_parent + ///< If a custom list needs to be + ///< used it should be allocated on + ///< the stack and a pointer to it + ///< placed here. tmpl_pair_list_t list_def; //!< Default list to use with unqualified ///< attribute reference. @@ -434,11 +449,6 @@ typedef struct { */ FR_DLIST_FUNCS(tmpl_attr_list, tmpl_attr_t, entry) -/** Define entry and head types for tmpl request references - * - */ -FR_DLIST_TYPES(tmpl_request_list) - /** An element in a list of request references * */ @@ -575,7 +585,7 @@ typedef struct { ///< were evaluated. TALLOC_CTX *list_ctx; //!< Where to allocate new attributes if building - ///< out from the current extents of the tree.X + ///< out from the current extents of the tree. fr_pair_list_t *list; //!< List that we tried to evaluate ar in and failed. ///< Or if ar is NULL, the list that represents the ///< deepest grouping or TLV attribute the chain of @@ -618,13 +628,13 @@ static inline tmpl_type_t tmpl_type_from_str(char const *type) * * @{ */ -static inline tmpl_request_ref_t tmpl_request(tmpl_t const *vpt) +static inline FR_DLIST_HEAD(tmpl_request_list) const *tmpl_request(tmpl_t const *vpt) { tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_attr_unresolved(vpt) || tmpl_is_list(vpt)); - return ((tmpl_request_t *)tmpl_request_list_tail(&vpt->data.attribute.rr))->request; + return &vpt->data.attribute.rr; } /** The number of request references contained within a tmpl @@ -892,10 +902,6 @@ TALLOC_CTX *tmpl_list_ctx(request_t *request, tmpl_pair_list_t list_name); size_t tmpl_pair_list_name(tmpl_pair_list_t *out, char const *name, tmpl_pair_list_t default_list) CC_HINT(nonnull); -int tmpl_request_ptr(request_t **request, tmpl_request_ref_t name) CC_HINT(nonnull); - -size_t tmpl_request_ref_by_name(tmpl_request_ref_t *out, char const *name, tmpl_request_ref_t unknown) CC_HINT(nonnull); - tmpl_t *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt, ...) CC_HINT(nonnull(1,4)); tmpl_t *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, @@ -908,6 +914,70 @@ tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len); +/** @name Parse request qualifiers + * + * @{ + */ +/** Static default request ref list for the current request + * + * Passed as request_def in tmpl_attr_rules_t. + */ +extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_current; + +/** Static default request ref list for the outer request + * + * Passed as request_def in tmpl_attr_rules_t. + */ +extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_outer; + +/** Static default request ref list for the parent request + * + * Passed as request_def in tmpl_attr_rules_t. + */ +extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_parent; + +int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql) CC_HINT(nonnull); + +void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql); + +int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, + FR_DLIST_HEAD(tmpl_request_list) const *b); + +/** Returns true if the specified qualifier list points to the current request + * + * @param[in] _list to check. + * @return + * - true if the list only contains a current request qualifier. + * - false otherwise. + */ +#define tmpl_request_ref_is_current(_list) (tmpl_request_ref_list_cmp(_list, &tmpl_request_def_current) == 0) + +/** Returns true if the specified qualifier list points to the parent request + * + * @param[in] _list to check. + * @return + * - true if the list only contains a parent request qualifier. + * - false otherwise. + */ +#define tmpl_request_ref_is_parent(_list) (tmpl_request_ref_list_cmp(_list, &tmpl_request_def_parent) == 0) + +/** Returns true if the specified qualifier list points to the outer request + * + * @param[in] _list to check. + * @return + * - true if the list only contains a outer request qualifier. + * - false otherwise. + */ +#define tmpl_request_ref_is_outer(_list) (tmpl_request_ref_list_cmp(_list, &tmpl_request_def_outer) == 0) + + +fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, + FR_DLIST_HEAD(tmpl_request_list) _CONST **out, + fr_sbuff_t *in, + fr_sbuff_parse_rules_t const *p_rules, + tmpl_attr_rules_t const *t_rules); +/** @} */ + void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...) CC_HINT(nonnull(1,3)); void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len) CC_HINT(nonnull); @@ -936,7 +1006,7 @@ void tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT void tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT(nonnull); -void tmpl_attr_set_request(tmpl_t *vpt, tmpl_request_ref_t request) CC_HINT(nonnull); +void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def) CC_HINT(nonnull); void tmpl_attr_set_list(tmpl_t *vpt, tmpl_pair_list_t list) CC_HINT(nonnull); diff --git a/src/lib/server/tmpl_dcursor.c b/src/lib/server/tmpl_dcursor.c index 9bdcfdb4004..2135d8cd2df 100644 --- a/src/lib/server/tmpl_dcursor.c +++ b/src/lib/server/tmpl_dcursor.c @@ -340,7 +340,6 @@ fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc, { fr_pair_t *vp = NULL; fr_pair_list_t *list_head; - tmpl_request_t *rr = NULL; TALLOC_CTX *list_ctx; TMPL_VERIFY(vpt); @@ -352,17 +351,11 @@ fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc, /* * Navigate to the correct request context */ - while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) { - if (tmpl_request_ptr(&request, rr->request) < 0) { - if (err) { - *err = -3; - fr_strerror_printf("Request context \"%s\" not available", - fr_table_str_by_value(tmpl_request_ref_table, rr->request, "")); - } - error: - memset(cc, 0, sizeof(*cc)); /* so tmpl_dursor_clear doesn't explode */ - return NULL; - } + if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) { + if (err) *err = -3; + error: + memset(cc, 0, sizeof(*cc)); /* so tmpl_dursor_clear doesn't explode */ + return NULL; } /* @@ -497,7 +490,6 @@ int tmpl_extents_find(TALLOC_CTX *ctx, tmpl_dcursor_ctx_t cc; tmpl_dcursor_nested_t *ns = NULL; - tmpl_request_t *rr = NULL; tmpl_attr_t const *ar = NULL; TMPL_VERIFY(vpt); @@ -507,13 +499,7 @@ int tmpl_extents_find(TALLOC_CTX *ctx, /* * Navigate to the correct request context */ - while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) { - if (tmpl_request_ptr(&request, rr->request) < 0) { - fr_strerror_printf("Request context \"%s\" not available", - fr_table_str_by_value(tmpl_request_ref_table, rr->request, "")); - return -3; - } - } + if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) return -3; if (!vpt->rules.attr.list_as_attr) { /* diff --git a/src/lib/server/tmpl_eval.c b/src/lib/server/tmpl_eval.c index 40d7465c65d..57e961e2b3d 100644 --- a/src/lib/server/tmpl_eval.c +++ b/src/lib/server/tmpl_eval.c @@ -199,38 +199,42 @@ fr_radius_packet_t *tmpl_packet_ptr(request_t *request, tmpl_pair_list_t list) * to a #request_t higher in the chain than the current #request_t. * * @see tmpl_pair_list - * @param[in,out] context #request_t to start resolving from, and where to write - * a pointer to the resolved #request_t back to. - * @param[in] name (request) to resolve. + * @param[in,out] context #request_t to start resolving from, and where to write + * a pointer to the resolved #request_t back to. + * @param[in] rql list of request qualifiers to follow. * @return * - 0 if request is valid in this context. * - -1 if request is not valid in this context. */ -int tmpl_request_ptr(request_t **context, tmpl_request_ref_t name) +int tmpl_request_ptr(request_t **context, FR_DLIST_HEAD(tmpl_request_list) const *rql) { + tmpl_request_t *rr = NULL; request_t *request = *context; - switch (name) { - case REQUEST_CURRENT: - return 0; + while ((rr = tmpl_request_list_next(rql, rr))) { + switch (rr->request) { + case REQUEST_CURRENT: + continue; /* noop */ - case REQUEST_PARENT: /* Navigate up one level */ - if (!request->parent) return -1; - *context = request->parent; - break; + case REQUEST_PARENT: /* Navigate up one level */ + if (!request->parent) return -1; + request = request->parent; + break; - case REQUEST_OUTER: /* Navigate to the outermost request */ - if (!request->parent) return -1; - while (request->parent) request = request->parent; - *context = request; - break; + case REQUEST_OUTER: /* Navigate to the outermost request */ + if (!request->parent) return -1; + while (request->parent) request = request->parent; + break; - case REQUEST_UNKNOWN: - default: - fr_assert(0); - return -1; + case REQUEST_UNKNOWN: + default: + fr_assert(0); + return -1; + } } + *context = request; + return 0; } diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index 22491ea6754..b2d74e63ba0 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -40,6 +40,55 @@ RCSID("$Id$") #include +/** Define a global variable for specifying a default request reference + * + * @param[in] _name what the global variable should be called. + * @param[in] _ref one of the values of tmpl_request_ref_t + * - REQUEST_CURRENT + * - REQUEST_OUTER, + * - REQUEST_PARENT, + * - REQUEST_UNKNOWN + */ +#define TMPL_REQUEST_REF_DEF(_name, _def) \ +extern FR_DLIST_HEAD(tmpl_request_list) _name; \ +static tmpl_request_t _name ## _entry = { \ + .entry = { \ + .entry = { \ + .next = &_name.head.entry, \ + .prev = &_name.head.entry \ + } \ + }, \ + .request = _def \ +}; \ +FR_DLIST_HEAD(tmpl_request_list) _name = { \ + .head = { \ + .offset = offsetof(tmpl_request_t, entry), \ + .entry = { \ + .next = &_name ## _entry.entry.entry, \ + .prev = &_name ## _entry.entry.entry, \ + }, \ + .num_elements = 1, \ + } \ +} + +/** Use the current request as the default + * + * Used as .attr.request_def = &tmpl_request_def_current; + */ +TMPL_REQUEST_REF_DEF(tmpl_request_def_current, REQUEST_CURRENT); + +/** Use the outer request as the default + * + * Used as .attr.request_def = &tmpl_request_def_outer; + */ +TMPL_REQUEST_REF_DEF(tmpl_request_def_outer, REQUEST_OUTER); + +/** Use the parent request as the default + * + * Used as .attr.request_def = &tmpl_request_def_parent; + */ +TMPL_REQUEST_REF_DEF(tmpl_request_def_parent, REQUEST_PARENT); + /** Default parser rules * * Because this is getting to be a ridiculous number of parsing rules @@ -47,13 +96,11 @@ RCSID("$Id$") * * Defaults are used if a NULL rules pointer is passed to the parsing function. */ -static tmpl_rules_t const default_attr_rules = { - .attr = { - .request_def = REQUEST_CURRENT, - .list_def = PAIR_LIST_REQUEST - } +static tmpl_rules_t const default_rules = { + }; + /* clang-format off */ /** Map #tmpl_type_t values to descriptive strings */ @@ -130,6 +177,30 @@ static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref); #define UNRESOLVED_SET(_flags) (*(_flags) = (*(_flags) | TMPL_FLAG_UNRESOLVED)) #define RESOLVED_SET(_flags) (*(_flags) = (*(_flags) & ~TMPL_FLAG_UNRESOLVED)) +/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars + * + * @param[in] in the sbuff to check. + * @param[in] p_rules to use terminals from. + * @return + * - true if substr is terminated correctly. + * - false if subst is not terminated correctly. + */ +static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in, + fr_sbuff_parse_rules_t const *p_rules) +{ + fr_sbuff_marker_t m; + bool ret; + + if (!fr_sbuff_extend(in)) return true; /* we're at the end of the string */ + if (!p_rules || !p_rules->terminals) return false; /* more stuff to parse but don't have a terminal set */ + + fr_sbuff_marker(&m, in); + ret = fr_sbuff_is_terminal(in, p_rules->terminals); + fr_sbuff_set(in, &m); + fr_sbuff_marker_release(&m); + return ret; +} + void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i) { char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1]; @@ -403,54 +474,252 @@ size_t tmpl_pair_list_name(tmpl_pair_list_t *out, char const *name, tmpl_pair_li } } -/** Resolve attribute name to a #tmpl_request_ref_t value. - * - * Check the name string for qualifiers that reference a parent #request_t. + /** Allocate a new request reference and add it to the end of the attribute reference list * - * If we find a string that matches a #tmpl_request_ref_t qualifier, return the number of chars - * we consumed. + */ +static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3)) +void tmpl_request_ref_list_copy(TALLOC_CTX *ctx, + FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in) +{ + tmpl_request_t *rr = NULL; + tmpl_request_t *n_rr = NULL; + + /* + * Duplicate the complete default list + */ + while ((rr = tmpl_request_list_next(in, rr))) { + MEM(n_rr = talloc(ctx, tmpl_request_t)); + *n_rr = (tmpl_request_t){ + .request = rr->request + }; + tmpl_request_list_insert_tail(out, n_rr); + ctx = n_rr; /* Chain the contexts */ + } +} + + /** Allocate a new request reference list and copy request references into it * - * If we're sure we've definitely found a list qualifier token delimiter (``*``) but the - * qualifier doesn't match one of the #tmpl_request_ref_t qualifiers, return 0 and set out to - * #REQUEST_UNKNOWN. + */ +static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3)) +void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx, + FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in) +{ + FR_DLIST_HEAD(tmpl_request_list) *rql; + + MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list))); + tmpl_request_list_talloc_init(rql); + + tmpl_request_ref_list_copy(rql, rql, in); + + *out = rql; +} + +/** Dump a request list to stderr * - * If we can't find a string that looks like a request qualifier, set out to def, and - * return 0. + */ +void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql) +{ + tmpl_request_t *rr = NULL; + + while ((rr = tmpl_request_list_next(rql, rr))) { + FR_FAULT_LOG("request - %s (%u)", + fr_table_str_by_value(tmpl_request_ref_table, rr->request, ""), + rr->request); + } +} + +/** Compare a list of request qualifiers * - * @param[out] out The #tmpl_request_ref_t value the name resolved to (or #REQUEST_UNKNOWN). - * @param[in] name of attribute. - * @param[in] def default request ref to return if no request qualifier is present. - * @return 0 if no valid request qualifier could be found, else the number of bytes consumed. - * The caller may then advanced the name pointer by the value returned, to get the - * start of the attribute list or attribute name(if any). + * @param[in] a first list. If NULL tmpl_request_def_current will be used. + * @param[in] b second list. If NULL tmpl_request_def_current will be used. + * @return + * - >0 a > b + * - 0 a == b + * - <0 a < b + */ +int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b) +{ + tmpl_request_t *a_rr = NULL, *b_rr = NULL; + + /* + * NULL, uninit, empty are all equivalent + * to tmpl_request_def_current. + * + * We need all these equivalent checks to + * deal with uninitialised tmpl rules. + */ + if (!a || !tmpl_request_list_initialised(a) || tmpl_request_list_empty(a)) a = &tmpl_request_def_current; + if (!b || !tmpl_request_list_initialised(b) || tmpl_request_list_empty(b)) b = &tmpl_request_def_current; + + /* + * Fast path... + */ + if (a == b) return 0; + + for (;;) { + a_rr = tmpl_request_list_next(a, a_rr); + b_rr = tmpl_request_list_next(b, b_rr); + + if (!a_rr || !b_rr) return CMP(tmpl_request_list_num_elements(a), tmpl_request_list_num_elements(b)); + + CMP_RETURN(a_rr, b_rr, request); + } +} + +/** Parse one or more request references, writing the list to out * - * @see tmpl_pair_list_name - * @see tmpl_request_ref_table + * @parma[in] ctx to allocate request refs in. + * @param[out] err If !NULL where to write the parsing error. + * @param[in] in Sbuff to read request references from. + * @param[in] p_rules Parse rules. + * @param[in] at_rules Default list and other rules. + * @return + * - >= 0 the number of bytes parsed. + * - <0 negative offset for where the error occurred */ -size_t tmpl_request_ref_by_name(tmpl_request_ref_t *out, char const *name, tmpl_request_ref_t def) +static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, + FR_DLIST_HEAD(tmpl_request_list) *out, + fr_sbuff_t *in, + fr_sbuff_parse_rules_t const *p_rules, + tmpl_attr_rules_t const *at_rules) { - char const *p, *q; + tmpl_request_ref_t ref; + tmpl_request_t *rr; + size_t ref_len; + fr_sbuff_t our_in = FR_SBUFF(in); + tmpl_request_t *tail = tmpl_request_list_tail(out); + unsigned int depth = 0; + fr_sbuff_marker_t m; + + if (!at_rules) at_rules = &default_rules.attr; - p = name; /* - * Try and determine the end of the token + * We could make the caller do this but as this + * function is intended to help populate tmpl rules, + * just be nice... */ - for (q = p; fr_dict_attr_allowed_chars[(uint8_t) *q] && (*q != '.') && (*q != '-'); q++); + if (!tmpl_request_list_initialised(out)) tmpl_request_list_talloc_init(out); + + fr_sbuff_marker(&m, &our_in); + for (depth = 0; depth < TMPL_MAX_REQUEST_REF_NESTING; depth++) { + bool end; + + + /* + * Search for a known request reference like + * 'current', or 'parent'. + */ + fr_sbuff_out_by_longest_prefix(&ref_len, &ref, tmpl_request_ref_table, &our_in, REQUEST_UNKNOWN); + + /* + * No match + */ + if (ref_len == 0) { + /* + * If depth == 0, we're at the start + * so just use the default request + * reference. + */ + default_ref: + if ((depth == 0) && at_rules->request_def) { + tmpl_request_ref_list_copy(ctx, out, at_rules->request_def); + } + break; + } + + /* + * We don't want to misidentify the list + * as being part of an attribute. + */ + if (!fr_sbuff_is_char(&our_in, '.') && !tmpl_substr_terminal_check(&our_in, p_rules)) { + goto default_ref; + } + + if (at_rules->parent || at_rules->disallow_qualifiers) { + fr_strerror_const("It is not permitted to specify a request reference here"); + if (err) *err = TMPL_ATTR_ERROR_INVALID_LIST_QUALIFIER; + + fr_sbuff_set(&our_in, in); /* Marker at the start */ + error: + tmpl_request_list_talloc_free_to_tail(out, tail); + return fr_sbuff_error(&our_in); + } + + /* + * Add a new entry to the dlist + */ + MEM(rr = talloc(ctx, tmpl_request_t)); + *rr = (tmpl_request_t){ + .request = ref + }; + tmpl_request_list_insert_tail(out, rr); + + /* + * Advance past the separator (if there is one) + */ + end = !fr_sbuff_next_if_char(&our_in, '.'); + + /* + * Update to the last successfully parsed component + * + * This makes it easy to backtrack from refs like + * + * parent.outer-realm-name + */ + fr_sbuff_set(&m, &our_in); + + if (end) break; + } /* - * First token delimiter wasn't a '.' + * Nesting level too deep */ - if (*q != '.') { - *out = def; - return 0; + if (depth > TMPL_MAX_REQUEST_REF_NESTING) { + fr_strerror_const("Request ref nesting too deep"); + if (err) *err = TMPL_ATTR_ERROR_NESTING_TOO_DEEP; + goto error; /* Leave marker at the end */ } - *out = fr_table_value_by_substr(tmpl_request_ref_table, name, q - p, REQUEST_UNKNOWN); - if (*out == REQUEST_UNKNOWN) return 0; + return fr_sbuff_set(in, &m); - return (q + 1) - p; } +/** Parse one or more request references, allocing a new list and adding the references to it + * + * This can be used to create request ref lists for rules and for tmpls. + * + * @parma[in] ctx to allocate request refs in. + * @param[out] err If !NULL where to write the parsing error. + * @param[in] in Sbuff to read request references from. + * @param[in] p_rules Parse rules. + * @param[in] at_rules Default list and other rules. + * @return + * - >= 0 the number of bytes parsed. + * - <0 negative offset for where the error occurred + */ +fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, + FR_DLIST_HEAD(tmpl_request_list) **out, + fr_sbuff_t *in, + fr_sbuff_parse_rules_t const *p_rules, + tmpl_attr_rules_t const *at_rules) +{ + fr_slen_t slen; + + FR_DLIST_HEAD(tmpl_request_list) *rql; + + MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list))); + tmpl_request_list_talloc_init(rql); + + slen = tmpl_request_ref_list_from_substr(rql, err, rql, in, p_rules, at_rules); + if (slen < 0) { + talloc_free(rql); + return slen; + } + + *out = rql; + + return slen; +} /** @} */ /** @name Alloc or initialise #tmpl_t @@ -682,29 +951,6 @@ tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char con * @{ */ - /** Allocate a new request reference and add it to the end of the attribute reference list - * - */ -static tmpl_request_t *tmpl_req_ref_add(tmpl_t *vpt, tmpl_request_ref_t request) -{ - tmpl_request_t *rr; - TALLOC_CTX *ctx; - - if (tmpl_request_list_num_elements(&vpt->data.attribute.rr) == 0) { - ctx = vpt; - } else { - ctx = tmpl_request_list_tail(&vpt->data.attribute.rr); - } - - MEM(rr = talloc(ctx, tmpl_request_t)); - *rr = (tmpl_request_t){ - .request = request - }; - tmpl_request_list_insert_tail(&vpt->data.attribute.rr, rr); - - return rr; -} - /** Allocate a new attribute reference and add it to the end of the attribute reference list * */ @@ -772,8 +1018,7 @@ int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bo */ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src) { - tmpl_attr_t *src_ar = NULL, *dst_ar; - tmpl_request_t *src_rr = NULL, *dst_rr; + tmpl_attr_t *src_ar = NULL, *dst_ar; /* * Clear any existing attribute references @@ -804,14 +1049,10 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src) /* * Clear any existing request references + * and copy the ones from the source. */ - if (tmpl_request_list_num_elements(&dst->data.attribute.rr) > 0) { - tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr); - } - - while ((src_rr = tmpl_request_list_next(&src->data.attribute.rr, src_rr))) { - MEM(dst_rr = tmpl_req_ref_add(dst, src_rr->request)); - } + tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr); + tmpl_request_ref_list_copy(dst, &dst->data.attribute.rr, &src->data.attribute.rr); /* * Remove me... @@ -962,14 +1203,16 @@ void tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to) /** Set the request for an attribute ref * */ -void tmpl_attr_set_request(tmpl_t *vpt, tmpl_request_ref_t request) +void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def) { fr_assert_msg(tmpl_is_attr(vpt), "Expected tmpl type 'attr', got '%s'", tmpl_type_to_str(vpt->type)); - if (tmpl_request_list_num_elements(&vpt->data.attribute.rr) > 0) tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr); - - tmpl_req_ref_add(vpt, request); + /* + * Clear any existing request references + */ + tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr); + tmpl_request_ref_list_copy(vpt, &vpt->data.attribute.rr, request_def); TMPL_ATTR_VERIFY(vpt); } @@ -1026,30 +1269,6 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_d } /** @} */ -/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars - * - * @param[in] in the sbuff to check. - * @param[in] p_rules to use terminals from. - * @return - * - true if substr is terminated correctly. - * - false if subst is not terminated correctly. - */ -static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in, - fr_sbuff_parse_rules_t const *p_rules) -{ - fr_sbuff_marker_t m; - bool ret; - - if (!fr_sbuff_extend(in)) return true; /* we're at the end of the string */ - if (!p_rules || !p_rules->terminals) return false; /* more stuff to parse but don't have a terminal set */ - - fr_sbuff_marker(&m, in); - ret = fr_sbuff_is_terminal(in, p_rules->terminals); - fr_sbuff_set(in, &m); - fr_sbuff_marker_release(&m); - return ret; -} - /** Descriptive return values for filter parsing * */ @@ -1692,94 +1911,6 @@ do_suffix: return 0; } -static inline int tmpl_request_ref_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, - tmpl_t *vpt, - fr_sbuff_t *name, - fr_sbuff_parse_rules_t const *p_rules, - tmpl_attr_rules_t const **pt_rules, - unsigned int depth) -{ - tmpl_request_ref_t ref; - size_t ref_len; - tmpl_request_t *rr; - FR_DLIST_HEAD(tmpl_request_list) *list = &vpt->data.attribute.rr; - fr_sbuff_marker_t s_m; - tmpl_attr_rules_t const *t_rules = *pt_rules; - - fr_sbuff_marker(&s_m, name); - fr_sbuff_out_by_longest_prefix(&ref_len, &ref, tmpl_request_ref_table, name, t_rules->request_def); - - /* - * No match - */ - if (ref_len == 0) { - /* - * If depth == 0, then just use - * the default request reference. - */ - default_ref: - if (depth == 0) { - MEM(rr = talloc(ctx, tmpl_request_t)); - *rr = (tmpl_request_t){ - .request = ref - }; - tmpl_request_list_insert_tail(list, rr); - } - fr_sbuff_marker_release(&s_m); - - return 0; - } - - /* - * We don't want to misidentify the list - * as being part of an attribute. - */ - if (!fr_sbuff_is_char(name, '.') && - ((!p_rules || !p_rules->terminals) || - !tmpl_substr_terminal_check(name, p_rules))) { - fr_sbuff_set(name, &s_m); - goto default_ref; - } - - fr_sbuff_marker_release(&s_m); - - /* - * Nesting level too deep - */ - if (depth > TMPL_MAX_REQUEST_REF_NESTING) { - fr_strerror_const("Request ref nesting too deep"); - if (err) *err = TMPL_ATTR_ERROR_NESTING_TOO_DEEP; - return -1; - } - - if (t_rules->parent || t_rules->disallow_qualifiers) { - fr_strerror_const("It is not permitted to specify a request reference here"); - if (err) *err = TMPL_ATTR_ERROR_INVALID_LIST_QUALIFIER; - return -1; - } - - /* - * Add a new entry to the dlist - */ - MEM(rr = talloc(ctx, tmpl_request_t)); - *rr = (tmpl_request_t){ - .request = ref - }; - tmpl_request_list_insert_tail(list, rr); - - /* - * Advance past the separator (if there is one) - */ - if (fr_sbuff_next_if_char(name, '.')) { - if (tmpl_request_ref_afrom_attr_substr(ctx, err, vpt, name, p_rules, pt_rules, depth + 1) < 0) { - tmpl_request_list_talloc_free_tail(list); /* Remove and free rr */ - return -1; - } - } - - return 0; -} - /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t * * @param[in,out] ctx to allocate #tmpl_t in. @@ -1839,9 +1970,11 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_attr_rules_t const *t_attr_rules; fr_sbuff_marker_t m_l; - if (!t_rules) t_rules = &default_attr_rules; /* Use the defaults */ - - t_attr_rules = &t_rules->attr; + if (!t_rules) { + t_attr_rules = &default_rules.attr; /* Use the defaults */ + } else { + t_attr_rules = &t_rules->attr; + } if (err) *err = TMPL_ATTR_ERROR_NONE; @@ -1894,7 +2027,11 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, /* * Parse one or more request references */ - ret = tmpl_request_ref_afrom_attr_substr(vpt, err, vpt, &our_name, p_rules, &t_attr_rules, 0); + ret = tmpl_request_ref_list_from_substr(vpt, NULL, + &vpt->data.attribute.rr, + &our_name, + p_rules, + t_attr_rules); if (ret < 0) { error: *out = NULL; @@ -2044,6 +2181,18 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name)); vpt->rules = *t_rules; /* Record the rules */ + + /* + * If there are actual requests, duplicate them + * and move them into the list. + * + * A NULL request_def pointer is equivalent to the + * current request. + */ + if (t_rules->attr.request_def) { + tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def); + } + if (tmpl_is_attr(vpt) && (tmpl_da(vpt)->type == tmpl_rules_cast(vpt))) vpt->rules.cast = FR_TYPE_NULL; if (!tmpl_substr_terminal_check(&our_name, p_rules)) { @@ -2090,7 +2239,7 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, { ssize_t slen, name_len; - if (!t_rules) t_rules = &default_attr_rules; /* Use the defaults */ + if (!t_rules) t_rules = &default_rules; /* Use the defaults */ name_len = strlen(name); slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules); @@ -2688,7 +2837,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t *vpt = NULL; - if (!t_rules) t_rules = &default_attr_rules; /* Use the defaults */ + if (!t_rules) t_rules = &default_rules; /* Use the defaults */ *out = NULL; diff --git a/src/lib/server/users_file.c b/src/lib/server/users_file.c index 1d937bfae55..fc957c8dc89 100644 --- a/src/lib/server/users_file.c +++ b/src/lib/server/users_file.c @@ -271,7 +271,6 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR lhs_rules = (tmpl_rules_t) { .attr = { .dict_def = dict, - .request_def = REQUEST_CURRENT, .prefix = TMPL_ATTR_REF_PREFIX_NO, .disallow_qualifiers = true, /* for now, until more tests are made */ @@ -288,7 +287,6 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR rhs_rules = (tmpl_rules_t) { .attr = { .dict_def = dict, - .request_def = REQUEST_CURRENT, .prefix = TMPL_ATTR_REF_PREFIX_YES, .disallow_qualifiers = true, /* for now, until rlm_files supports it */ } diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index 76170b74a66..970daa95cbf 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -485,7 +485,7 @@ static bool pass2_fixup_cond_map(fr_cond_t *c, CONF_ITEM *ci, fr_dict_t const *d * with the request pairs. */ if (!tmpl_is_attr(map->lhs) || - (tmpl_request(map->lhs) != REQUEST_CURRENT) || + !tmpl_request_ref_is_current(tmpl_request(map->lhs)) || (tmpl_list(map->lhs) != PAIR_LIST_REQUEST)) { return true; } diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 717092c570e..f53ff884252 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -1102,12 +1102,23 @@ static void _xlat_debug(xlat_exp_head_t const *head, int depth) break; case XLAT_TMPL: + { if (tmpl_is_attr(node->vpt)) { fr_assert(!node->flags.pure); INFO_INDENT("attribute (%s)", tmpl_da(node->vpt)->name); if (tmpl_num(node->vpt) != NUM_ANY) { + FR_DLIST_HEAD(tmpl_request_list) const *list; + tmpl_request_t *rr = NULL; + INFO_INDENT("{"); - INFO_INDENT("ref %d", tmpl_request(node->vpt)); + + /* + * Loop over the request references + */ + list = tmpl_request(node->vpt); + while ((rr = tmpl_request_list_next(list, rr))) { + INFO_INDENT("ref %d", rr->request); + } INFO_INDENT("list %d", tmpl_list(node->vpt)); if (tmpl_num(node->vpt) != NUM_ANY) { if (tmpl_num(node->vpt) == NUM_COUNT) { @@ -1125,6 +1136,7 @@ static void _xlat_debug(xlat_exp_head_t const *head, int depth) } else { INFO_INDENT("tmpl (%s)", node->fmt); } + } break; case XLAT_VIRTUAL: diff --git a/src/tests/keywords/subrequest-request-qualifiers b/src/tests/keywords/subrequest-request-qualifiers new file mode 100644 index 00000000000..24e8ec7cec0 --- /dev/null +++ b/src/tests/keywords/subrequest-request-qualifiers @@ -0,0 +1,79 @@ +subrequest Access-Request { + # + # Prefix/attribute parsing disambiguation + # + update outer.request { + &Outer-Realm-Name := 'testing123' + } + + update { + &User-Name := "obo" + } + + subrequest Access-Request { + update { + &User-Name := "jim" + } + + subrequest Access-Request { + update { + &User-Name := "joe" + } + + update outer.request { + &Tmp-String-0 := ¤t.User-Name + &Tmp-String-1 := &parent.User-Name + &Tmp-String-2 := &parent.parent.User-Name + &Tmp-String-3 := &parent.parent.parent.User-Name + &Tmp-String-4 := &outer.User-Name + &Tmp-String-5 := ¤t.outer.User-Name + &Tmp-String-6 := &parent.current.outer.User-Name + &Tmp-String-7 := "%{parent.parent.parent.parent.User-Name}" + &Tmp-String-8 := "%{outer.parent.User-Name}" + + } + } + } +} + +if (&Outer-Realm-Name != 'testing123') { + test_fail +} + +if (&Tmp-String-0 != 'joe') { + test_fail +} + +if (&Tmp-String-1 != 'jim') { + test_fail +} + +if (&tmp-String-2 != 'obo') { + test_fail +} + +if (&Tmp-String-3 != 'bob') { + test_fail +} + +if (&Tmp-String-4 != 'bob') { + test_fail +} + +if (&Tmp-String-5 != 'bob') { + test_fail +} + +if (&Tmp-String-6 != 'bob') { + test_fail +} + +if (&Tmp-String-7 != '') { + test_fail +} + +if (&Tmp-String-8 != '') { + test_fail +} + +success