blueprints: add AtIndex tag (#12386)

This commit is contained in:
Marc 'risson' Schmitt
2024-12-18 14:10:37 +01:00
committed by GitHub
parent 22b0a1bd23
commit 98b5b75f29
5 changed files with 84 additions and 2 deletions

View File

@ -33,7 +33,8 @@
"!If sequence",
"!Index scalar",
"!KeyOf scalar",
"!Value scalar"
"!Value scalar",
"!AtIndex scalar"
],
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "index",

View File

@ -146,6 +146,10 @@ entries:
]
]
nested_context: !Context context2
at_index_sequence: !AtIndex [!Context sequence, 0]
at_index_sequence_default: !AtIndex [!Context sequence, 100, "non existent"]
at_index_mapping: !AtIndex [!Context mapping, "key2"]
at_index_mapping_default: !AtIndex [!Context mapping, "invalid", "non existent"]
identifiers:
name: test
conditions:

View File

@ -215,6 +215,10 @@ class TestBlueprintsV1(TransactionTestCase):
},
"nested_context": "context-nested-value",
"env_null": None,
"at_index_sequence": "foo",
"at_index_sequence_default": "non existent",
"at_index_mapping": 2,
"at_index_mapping_default": "non existent",
}
).exists()
)

View File

@ -24,6 +24,10 @@ from authentik.lib.sentry import SentryIgnoredException
from authentik.policies.models import PolicyBindingModel
class UNSET:
"""Used to test whether a key has not been set."""
def get_attrs(obj: SerializerModel) -> dict[str, Any]:
"""Get object's attributes via their serializer, and convert it to a normal dict"""
serializer: Serializer = obj.serializer(obj)
@ -556,6 +560,53 @@ class Value(EnumeratedItem):
raise EntryInvalidError.from_entry(f"Empty/invalid context: {context}", entry) from exc
class AtIndex(YAMLTag):
"""Get value at index of a sequence or mapping"""
obj: YAMLTag | dict | list | tuple
attribute: int | str | YAMLTag
default: Any | UNSET
def __init__(self, loader: "BlueprintLoader", node: SequenceNode) -> None:
super().__init__()
self.obj = loader.construct_object(node.value[0])
self.attribute = loader.construct_object(node.value[1])
if len(node.value) == 2: # noqa: PLR2004
self.default = UNSET
else:
self.default = loader.construct_object(node.value[2])
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
if isinstance(self.obj, YAMLTag):
obj = self.obj.resolve(entry, blueprint)
else:
obj = self.obj
if isinstance(self.attribute, YAMLTag):
attribute = self.attribute.resolve(entry, blueprint)
else:
attribute = self.attribute
if isinstance(obj, list | tuple):
try:
return obj[attribute]
except TypeError as exc:
raise EntryInvalidError.from_entry(
f"Invalid index for list: {attribute}", entry
) from exc
except IndexError as exc:
if self.default is UNSET:
raise EntryInvalidError.from_entry(
f"Index out of range: {attribute}", entry
) from exc
return self.default
if attribute in obj:
return obj[attribute]
else:
if self.default is UNSET:
raise EntryInvalidError.from_entry(f"Key does not exist: {attribute}", entry)
return self.default
class BlueprintDumper(SafeDumper):
"""Dump dataclasses to yaml"""
@ -606,6 +657,7 @@ class BlueprintLoader(SafeLoader):
self.add_constructor("!Enumerate", Enumerate)
self.add_constructor("!Value", Value)
self.add_constructor("!Index", Index)
self.add_constructor("!AtIndex", AtIndex)
class EntryInvalidError(SentryIgnoredException):

View File

@ -16,7 +16,8 @@ For VS Code, for example, add these entries to your `settings.json`:
"!If sequence",
"!Index scalar",
"!KeyOf scalar",
"!Value scalar"
"!Value scalar",
"!AtIndex scalar"
]
}
```
@ -299,3 +300,23 @@ The above example will resolve to something like this:
- "bar: (index: 1, letter: a)"
- "bar: (index: 2, letter: r)"
```
#### `!AtIndex` <span class="badge badge--version">authentik 2024.12+</span>
Minimal example:
```yaml
value_in_sequence: !AtIndex [["first", "second"], 0] # Resolves to "first"
value_in_mapping: !AtIndex [{ "foo": "bar", "other": "value" }, "foo"] # Resolves to "bar"
```
Example with default value:
```yaml
default_value: !AtIndex [["first"], 100, "default"] # Resolves to "default"
default_value: !AtIndex [["first"], 100] # Throws an error
```
Resolves to the value at a specific index in a sequence or a mapping.
If no value can be found at the specified index and no default is provided, an error is raised and the blueprint is invalid.