blueprints: Support nested custom tags in `!Find` and `!Format` tags (#4127)
* Added support for nested tags to !Find and !Format * Added tests * Fix variable names * Added docs * Fixed small mistake in tests * Fixed variable names * Broke example into multiple lines
This commit is contained in:
parent
3251bdc220
commit
1f7d52c5ce
|
@ -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
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Reference in New Issue