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
13
authentik/blueprints/tests/fixtures/tags.yaml
vendored
13
authentik/blueprints/tests/fixtures/tags.yaml
vendored
|
@ -1,10 +1,19 @@
|
||||||
version: 1
|
version: 1
|
||||||
context:
|
context:
|
||||||
foo: bar
|
foo: bar
|
||||||
|
policy_property: name
|
||||||
|
policy_property_value: foo-bar-baz-qux
|
||||||
entries:
|
entries:
|
||||||
- attrs:
|
- attrs:
|
||||||
expression: return True
|
expression: return True
|
||||||
identifiers:
|
identifiers:
|
||||||
name: !Format [foo-%s-%s, !Context foo, !Context bar]
|
name: !Format [foo-%s-%s-%s, !Context foo, !Context bar, qux]
|
||||||
id: default-source-enrollment-if-username
|
id: policy
|
||||||
model: authentik_policies_expression.expressionpolicy
|
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.tests import load_yaml_fixture
|
||||||
from authentik.blueprints.v1.exporter import FlowExporter
|
from authentik.blueprints.v1.exporter import FlowExporter
|
||||||
from authentik.blueprints.v1.importer import Importer, transaction_rollback
|
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.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.policies.expression.models import ExpressionPolicy
|
from authentik.policies.expression.models import ExpressionPolicy
|
||||||
|
@ -74,11 +75,21 @@ class TestBlueprintsV1(TransactionTestCase):
|
||||||
|
|
||||||
def test_import_yaml_tags(self):
|
def test_import_yaml_tags(self):
|
||||||
"""Test some yaml tags"""
|
"""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"})
|
importer = Importer(load_yaml_fixture("fixtures/tags.yaml"), {"bar": "baz"})
|
||||||
self.assertTrue(importer.validate()[0])
|
self.assertTrue(importer.validate()[0])
|
||||||
self.assertTrue(importer.apply())
|
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):
|
def test_export_validate_import_policies(self):
|
||||||
"""Test export and validate it"""
|
"""Test export and validate it"""
|
||||||
|
|
|
@ -188,11 +188,18 @@ class Format(YAMLTag):
|
||||||
self.format_string = node.value[0].value
|
self.format_string = node.value[0].value
|
||||||
self.args = []
|
self.args = []
|
||||||
for raw_node in node.value[1:]:
|
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:
|
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:
|
try:
|
||||||
return self.format_string % tuple(self.args)
|
return self.format_string % tuple(args)
|
||||||
except TypeError as exc:
|
except TypeError as exc:
|
||||||
raise EntryInvalidError(exc)
|
raise EntryInvalidError(exc)
|
||||||
|
|
||||||
|
@ -219,7 +226,15 @@ class Find(YAMLTag):
|
||||||
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
|
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
|
||||||
query = Q()
|
query = Q()
|
||||||
for cond in self.conditions:
|
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()
|
instance = self.model_class.objects.filter(query).first()
|
||||||
if instance:
|
if instance:
|
||||||
return instance.pk
|
return instance.pk
|
||||||
|
|
|
@ -10,7 +10,19 @@ If no matching entry can be found, an error is raised and the blueprint is inval
|
||||||
|
|
||||||
#### `!Find`
|
#### `!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.
|
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.
|
First argument is the model to be queried, remaining arguments are expected to be pairs of key=value pairs to query for.
|
||||||
|
|
Reference in a new issue