diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index 6cb823690..d8b5a673d 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -1,10 +1,19 @@ version: 1 context: foo: bar + policy_property: name + policy_property_value: foo-bar-baz-qux entries: - attrs: expression: return True identifiers: - name: !Format [foo-%s-%s, !Context foo, !Context bar] - id: default-source-enrollment-if-username + name: !Format [foo-%s-%s-%s, !Context foo, !Context bar, qux] + id: policy model: authentik_policies_expression.expressionpolicy +- attrs: + attributes: + policy_pk1: !Format ["%s-%s", !Find [authentik_policies_expression.expressionpolicy, [!Context policy_property, !Context policy_property_value], [expression, return True]], suffix] + policy_pk2: !Format ["%s-%s", !KeyOf policy, suffix] + identifiers: + name: test + model: authentik_core.group diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index cdd543f01..b4abd4d46 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -4,6 +4,7 @@ from django.test import TransactionTestCase from authentik.blueprints.tests import load_yaml_fixture from authentik.blueprints.v1.exporter import FlowExporter from authentik.blueprints.v1.importer import Importer, transaction_rollback +from authentik.core.models import Group from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.lib.generators import generate_id from authentik.policies.expression.models import ExpressionPolicy @@ -74,11 +75,21 @@ class TestBlueprintsV1(TransactionTestCase): def test_import_yaml_tags(self): """Test some yaml tags""" - ExpressionPolicy.objects.filter(name="foo-foo-bar").delete() + ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").delete() + Group.objects.filter(name="test").delete() importer = Importer(load_yaml_fixture("fixtures/tags.yaml"), {"bar": "baz"}) self.assertTrue(importer.validate()[0]) self.assertTrue(importer.apply()) - self.assertTrue(ExpressionPolicy.objects.filter(name="foo-foo-bar")) + policy = ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").first() + self.assertTrue(policy) + self.assertTrue( + Group.objects.filter( + attributes__contains={ + "policy_pk1": str(policy.pk) + "-suffix", + "policy_pk2": str(policy.pk) + "-suffix", + } + ) + ) def test_export_validate_import_policies(self): """Test export and validate it""" diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index 52454486b..668f5091a 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -188,11 +188,18 @@ class Format(YAMLTag): self.format_string = node.value[0].value self.args = [] for raw_node in node.value[1:]: - self.args.append(raw_node.value) + self.args.append(loader.construct_object(raw_node)) def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: + args = [] + for arg in self.args: + if isinstance(arg, YAMLTag): + args.append(arg.resolve(entry, blueprint)) + else: + args.append(arg) + try: - return self.format_string % tuple(self.args) + return self.format_string % tuple(args) except TypeError as exc: raise EntryInvalidError(exc) @@ -219,7 +226,15 @@ class Find(YAMLTag): def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: query = Q() for cond in self.conditions: - query &= Q(**{cond[0]: cond[1]}) + if isinstance(cond[0], YAMLTag): + query_key = cond[0].resolve(entry, blueprint) + else: + query_key = cond[0] + if isinstance(cond[1], YAMLTag): + query_value = cond[1].resolve(entry, blueprint) + else: + query_value = cond[1] + query &= Q(**{query_key: query_value}) instance = self.model_class.objects.filter(query).first() if instance: return instance.pk diff --git a/website/developer-docs/blueprints/v1/tags.md b/website/developer-docs/blueprints/v1/tags.md index e75cc11a6..2970a3316 100644 --- a/website/developer-docs/blueprints/v1/tags.md +++ b/website/developer-docs/blueprints/v1/tags.md @@ -10,7 +10,19 @@ If no matching entry can be found, an error is raised and the blueprint is inval #### `!Find` -Example: `configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]` +Examples: + +`configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]` + +``` +configure_flow: !Find [ + authentik_flows.flow, + [ + !Context property_name, + !Context property_value + ] +] +``` Looks up any model and resolves to the the matches' primary key. First argument is the model to be queried, remaining arguments are expected to be pairs of key=value pairs to query for.