]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add purify callback for || and &&
authorAlan T. DeKok <aland@freeradius.org>
Tue, 31 May 2022 14:23:15 +0000 (10:23 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 3 Jun 2022 11:15:51 +0000 (07:15 -0400)
src/lib/unlang/xlat_expr.c
src/tests/unit/xlat/cond_base.txt

index c4826c348fc0642c2ba79bf8bb74fd87afe589db..a31893af63a595075900f0b51b2eb10464b425a6 100644 (file)
@@ -465,6 +465,146 @@ static int xlat_logical_instantiate(xlat_inst_ctx_t const *xctx)
        return 0;
 }
 
+static bool xlat_node_matches_bool(xlat_exp_t *parent, xlat_exp_head_t *head, bool sense, bool *result)
+{
+       fr_value_box_t *box;
+       xlat_exp_t *node;
+
+       if (!head->flags.pure) return false;
+
+       node = xlat_exp_head(head);
+       if (!node || xlat_exp_next(head, node)) {
+               return false;
+       }
+
+       if (node->type == XLAT_BOX) {
+               box = &node->data;
+               goto check;
+       }
+
+       if (node->type != XLAT_TMPL) {
+               return false;
+       }
+
+       if (!tmpl_is_data(node->vpt)) {
+               return false;
+       }
+
+       box = tmpl_value(node->vpt);
+
+check:
+       /*
+        *      On "true", replace the entire logical operation with the value-box.
+        *
+        *      On "false", omit this argument, and go to the next one.
+        */
+       *result = (truthiness(box) == sense);
+
+       if (!*result) return true;
+
+       xlat_inst_remove(parent);
+
+       parent->type = XLAT_BOX;
+       fr_value_box_copy(parent, &parent->data, box);
+       parent->flags = (xlat_flags_t) { .pure = true, };
+
+       talloc_free_children(parent);
+
+       return true;
+}
+
+/** Any argument resolves to inst->stop, the entire thing is a bool of inst->stop
+ *
+ *  @todo - for now, this does very simple checks, and doesn't purify
+ *  its arguments.  We also need a standard way to deregister xlat functions
+ */
+static int xlat_expr_logical_purify(xlat_exp_t *node, void *instance)
+{
+       int i, j;
+       int deleted = 0;
+       bool result;
+       xlat_logical_inst_t *inst = talloc_get_type_abort_const(instance, xlat_logical_inst_t);
+       xlat_exp_head_t *group;
+
+       fr_assert(node->type == XLAT_FUNC);
+
+       /*
+        *      Don't check the last argument.  If everything else gets deleted, then we just return the last
+        *      argument.
+        */
+       for (i = 0; i < inst->argc; i++) {
+               if (!xlat_node_matches_bool(node, inst->argv[i], inst->stop, &result)) continue;
+
+               /*
+                *      0 && EXPR --> 0.
+                *      1 || EXPR --> 1
+                *
+                *      Parent is now an XLAT_BOX, so we're done.
+                */
+               if (result) return 0;
+
+               /*
+                *      We're at the last argument, we should just return that, if it's 
+                */
+               if ((i + 1) == inst->argc) break;
+
+               TALLOC_FREE(inst->argv[i]);
+               deleted++;
+       }
+
+       if (!deleted) return 0;
+
+       /*
+        *      Pack the array.  We insert at i, and read from j.  We don't need to read the deleted entries,
+        *      as they all MUST be NULL.
+        */
+       i = 0, j = -1;
+       while (i < (inst->argc - deleted)) {
+               if (inst->argv[i]) {
+                       i++;
+                       continue;
+               }
+
+               /*
+                *      Start searching from the next entry, OR start seaching from where we left off before.
+                */
+               if (j < 0) j = i + 1;
+
+               /*
+                *      Find the first non-NULL entry, and insert it in argv[i].  We search here until the end
+                *      of the array, because we may have deleted entries from the start of the array.
+                */
+               while (j < inst->argc) {
+                       if (inst->argv[j]) break;
+                       j++;
+               }
+
+               /*
+                *      Move the entry down, and clear out the tail end of the array.
+                */
+               inst->argv[i++] = inst->argv[j];
+               inst->argv[j++] = NULL;
+       }
+
+       inst->argc -= deleted;
+
+       if (inst->argc > 1) return 0;
+
+       /*
+        *      Only one argument left/  We can hoist the child into ourselves, and omit the logical operation.
+        */
+       group = inst->argv[0];
+       fr_assert(group != NULL);
+       talloc_steal(node, group);
+
+       xlat_inst_remove(node);
+       node->type = XLAT_GROUP;
+       node->group = group;
+       node->flags = group->flags;
+
+       return 0;
+}
+
 static bool xlat_logical_match(fr_value_box_t **dst, fr_value_box_list_t *in, bool logical_or)
 {
        fr_value_box_t *last = NULL;
@@ -724,6 +864,7 @@ do { \
        xlat_async_instantiate_set(xlat, xlat_ ## _func_name ## _instantiate, xlat_ ## _func_name ## _inst_t, NULL, NULL); \
        xlat_internal(xlat); \
        xlat_print_set(xlat, xlat_expr_print_ ## _func_name); \
+       xlat_purify_set(xlat, xlat_expr_logical_purify); \
        xlat->token = _op; \
 } while (0)
 
@@ -1298,7 +1439,6 @@ static bool valid_type(xlat_exp_t *node)
                if (da->dict == fr_dict_internal()) goto list;
 
                fr_strerror_const("Cannot use structural types in condition");
-               fprintf(stderr, "FAIL %d\n", __LINE__);
                return false;
        }
 
index 9d5494bc2bdc34dbad84e44cc58c357c8ecd5e4f..0a93b547bd0d7c6d9d7412d553a02c8f157902de 100644 (file)
@@ -284,18 +284,18 @@ match 0
 #
 #  @todo - purify logical operators.  The instantiation function should update the "can_purify" flags.
 #
+xlat_purify true || (&User-Name == "bob")
+match true
+
 xlat_purify true && (&User-Name == "bob")
-match (true && (&User-Name == "bob"))
-#match &User-Name == "bob"
+match (&User-Name == "bob")
 
 xlat_purify false && (&User-Name == "bob")
-match (false && (&User-Name == "bob"))
+match false
 
 xlat_purify false || (&User-Name == "bob")
-match (false || (&User-Name == "bob"))
+match (&User-Name == "bob")
 
-xlat_purify true || (&User-Name == "bob")
-match (true || (&User-Name == "bob"))
 
 #
 #  Both sides static data with a cast: evaluate at parse time.
@@ -651,14 +651,30 @@ match false
 #  More short-circuit evaluations
 #
 xlat_purify (&User-Name == "bob") && (false)
-match ((&User-Name == "bob") && false)
+match false
+
+xlat_purify (&User-Name == "bob") && (false) && (&User-Password == "hello")
+match false
+
+xlat_purify (&User-Name == "bob") && (&User-Password == "hello") && false
+match false
+
+# @todo - last argument isn't purified!
+xlat_purify (&User-Name == "bob") && (&User-Password == "hello") && (1 > 2)
+match ((&User-Name == "bob") && (&User-Password == "hello") && (1 > 2))
 
 xlat_purify (&User-Name == "bob") || (true)
-match ((&User-Name == "bob") || true)
+match true
 
 xlat_purify 1 || 2
 match 1
 
+xlat_purify 1 || 2 || (&User-Name == "bob")
+match 1
+
+xlat_purify (&User-Name == "bob") || 1 || 2
+match 1
+
 xlat_purify 1 && 2
 match 2
 
@@ -672,4 +688,4 @@ xlat_purify (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
 match ((&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message))
 
 count
-match 270
+match 280