From d1943f4238687cb2248618305d5df8a8d17bae14 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Sun, 8 Mar 2026 17:44:53 +0000 Subject: [PATCH 01/15] Add support for generating flat and unique sequences in blueprint enumeration tags --- authentik/blueprints/tests/fixtures/tags.yaml | 10 ++++++++++ authentik/blueprints/tests/test_v1.py | 10 ++++++++++ authentik/blueprints/v1/common.py | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index b0e53727f0b0..98f44b5a975b 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -121,6 +121,16 @@ entries: SEQ, !Format ["prefixed-items-%%s-%%s", !Index 0, !Value 0] ] + enumerate_nested_sequence_to_flat_sequence: !Enumerate [ + [!Context sequence, !Context sequence], + FLAT_SEQ, + !Format ["flattened-%%s-%%s", !Index 0, !Value 0] + ] + enumerate_sequence_to_uniq_sequence: !Enumerate [ + ["foo", "bar", "foo"], + UNIQ_SEQ, + !Format ["uniq-%%s-%%s", !Index 0, !Value 0] + ] enumerate_sequence_to_mapping: !Enumerate [ !Context sequence, MAP, diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 04cabe36cc3f..4b9708aee324 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -186,6 +186,16 @@ def test_import_yaml_tags(self): "prefixed-items-0-foo", "prefixed-items-1-bar", ], + "enumerate_nested_sequence_to_flat_sequence": [ + "flattened-0-foo", + "flattened-1-bar", + "flattened-2-foo", + "flattened-3-bar", + ], + "enumerate_sequence_to_uniq_sequence": [ + "uniq-0-foo", + "uniq-0-bar", + ], "enumerate_sequence_to_mapping": {"index: 0": "foo", "index: 1": "bar"}, "nested_complex_enumeration": { "0": { diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index b27929358cbd..58b73ee084e6 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -504,10 +504,12 @@ class Enumerate(YAMLTag, YAMLTagContext): iterable: YAMLTag | Iterable item_body: Any - output_body: Literal["SEQ", "MAP"] + output_body: Literal["SEQ", "FLAT_SEQ", "UNIQ_SEQ", "MAP"] _OUTPUT_BODIES = { "SEQ": (list, lambda a, b: [*a, b]), + "FLAT_SEQ": (list, lambda a, b: [*a, *b]), + "UNIQ_SEQ": (list, lambda a, b: list(set([*a, *b]))), "MAP": ( dict, lambda a, b: always_merger.merge(a, {b[0]: b[1]} if isinstance(b, tuple | list) else b), From c2c5f28311fe799e42a4d90c443ed2b69aa9f583 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Sun, 8 Mar 2026 18:01:47 +0000 Subject: [PATCH 02/15] fix --- authentik/blueprints/tests/fixtures/tags.yaml | 2 +- authentik/blueprints/tests/test_v1.py | 1 + authentik/blueprints/v1/common.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index 98f44b5a975b..52bf8df88c0f 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -122,7 +122,7 @@ entries: !Format ["prefixed-items-%%s-%%s", !Index 0, !Value 0] ] enumerate_nested_sequence_to_flat_sequence: !Enumerate [ - [!Context sequence, !Context sequence], + [!Context sequence, !Context sequence, scalar_value], FLAT_SEQ, !Format ["flattened-%%s-%%s", !Index 0, !Value 0] ] diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 4b9708aee324..d3adbd112759 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -191,6 +191,7 @@ def test_import_yaml_tags(self): "flattened-1-bar", "flattened-2-foo", "flattened-3-bar", + "flattened-4-scalar_value", ], "enumerate_sequence_to_uniq_sequence": [ "uniq-0-foo", diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index 58b73ee084e6..e19504337857 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -508,7 +508,7 @@ class Enumerate(YAMLTag, YAMLTagContext): _OUTPUT_BODIES = { "SEQ": (list, lambda a, b: [*a, b]), - "FLAT_SEQ": (list, lambda a, b: [*a, *b]), + "FLAT_SEQ": (list, lambda a, b: a + (list(b) if isinstance(b, (tuple, list)) else [b])), "UNIQ_SEQ": (list, lambda a, b: list(set([*a, *b]))), "MAP": ( dict, From 39b7813385bb573556fe7a268873c23debdc98c4 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Sun, 8 Mar 2026 18:07:39 +0000 Subject: [PATCH 03/15] fix --- authentik/blueprints/v1/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index e19504337857..c4b9ea19bbf6 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -509,7 +509,7 @@ class Enumerate(YAMLTag, YAMLTagContext): _OUTPUT_BODIES = { "SEQ": (list, lambda a, b: [*a, b]), "FLAT_SEQ": (list, lambda a, b: a + (list(b) if isinstance(b, (tuple, list)) else [b])), - "UNIQ_SEQ": (list, lambda a, b: list(set([*a, *b]))), + "UNIQ_SEQ": (list, lambda a, b: list(set([*a, b]))), "MAP": ( dict, lambda a, b: always_merger.merge(a, {b[0]: b[1]} if isinstance(b, tuple | list) else b), From 99cc1ecfca686ef666714bae7a3cdb7e8ed8d2ee Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Sun, 8 Mar 2026 18:13:23 +0000 Subject: [PATCH 04/15] add docs --- website/docs/customize/blueprints/v1/tags.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/docs/customize/blueprints/v1/tags.mdx b/website/docs/customize/blueprints/v1/tags.mdx index 04a786b1cb3d..ffc966abf324 100644 --- a/website/docs/customize/blueprints/v1/tags.mdx +++ b/website/docs/customize/blueprints/v1/tags.mdx @@ -208,9 +208,13 @@ This tag takes 3 arguments: ``` - **iterable**: Any Python iterable or custom tag that resolves to such iterable -- **output_object_type**: `SEQ` or `MAP`. Controls whether the returned YAML will be a mapping or a sequence. +- **output_object_type**: `SEQ`, `FLAT_SEQ`, `UNIQ_SEQ` or `MAP`. Controls whether the returned YAML will be a mapping or a sequence. With `FLAT_SEQ` the resulting list will also be flattened, while with `UNIQ_SEQ` duplicated entries will be removed - **single_item_yaml**: The YAML to use to create a single entry in the output object +:::note +`FLAT_SEQ` and `UNIQ_SEQ` require authentik 2026.05+ +::: + 2. `!Index` tag: :::info From d1ab3db12d8b0e38fb75876a89bbbe85592a0c2d Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Sun, 8 Mar 2026 18:23:24 +0000 Subject: [PATCH 05/15] fix test --- authentik/blueprints/tests/fixtures/tags.yaml | 2 +- authentik/blueprints/tests/test_v1.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index 52bf8df88c0f..bbd4775436e9 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -129,7 +129,7 @@ entries: enumerate_sequence_to_uniq_sequence: !Enumerate [ ["foo", "bar", "foo"], UNIQ_SEQ, - !Format ["uniq-%%s-%%s", !Index 0, !Value 0] + !Value 0 ] enumerate_sequence_to_mapping: !Enumerate [ !Context sequence, diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index d3adbd112759..327f587e3605 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -194,8 +194,8 @@ def test_import_yaml_tags(self): "flattened-4-scalar_value", ], "enumerate_sequence_to_uniq_sequence": [ - "uniq-0-foo", - "uniq-0-bar", + "foo", + "bar", ], "enumerate_sequence_to_mapping": {"index: 0": "foo", "index: 1": "bar"}, "nested_complex_enumeration": { From ea11d00893c3763d5515da9d5e016f166095da6a Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 01:45:47 +0000 Subject: [PATCH 06/15] fix --- authentik/blueprints/tests/fixtures/tags.yaml | 2 +- authentik/blueprints/tests/test_v1.py | 10 +++++----- authentik/blueprints/v1/common.py | 12 +++++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index bbd4775436e9..ed0324ec2ede 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -124,7 +124,7 @@ entries: enumerate_nested_sequence_to_flat_sequence: !Enumerate [ [!Context sequence, !Context sequence, scalar_value], FLAT_SEQ, - !Format ["flattened-%%s-%%s", !Index 0, !Value 0] + !Value 0 ] enumerate_sequence_to_uniq_sequence: !Enumerate [ ["foo", "bar", "foo"], diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 327f587e3605..9dcab9598442 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -187,11 +187,11 @@ def test_import_yaml_tags(self): "prefixed-items-1-bar", ], "enumerate_nested_sequence_to_flat_sequence": [ - "flattened-0-foo", - "flattened-1-bar", - "flattened-2-foo", - "flattened-3-bar", - "flattened-4-scalar_value", + "foo", + "bar", + "foo", + "bar", + "scalar_value", ], "enumerate_sequence_to_uniq_sequence": [ "foo", diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index c4b9ea19bbf6..e1580c6bf0f9 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -15,6 +15,7 @@ from deepmerge import always_merger from django.apps import apps from django.db.models import Model, Q +from more_itertools import collapse from rest_framework.exceptions import ValidationError from rest_framework.fields import Field from rest_framework.serializers import Serializer @@ -507,12 +508,13 @@ class Enumerate(YAMLTag, YAMLTagContext): output_body: Literal["SEQ", "FLAT_SEQ", "UNIQ_SEQ", "MAP"] _OUTPUT_BODIES = { - "SEQ": (list, lambda a, b: [*a, b]), - "FLAT_SEQ": (list, lambda a, b: a + (list(b) if isinstance(b, (tuple, list)) else [b])), - "UNIQ_SEQ": (list, lambda a, b: list(set([*a, b]))), + "SEQ": (list, lambda a, b: [*a, b], lambda x: x), + "FLAT_SEQ": (list, lambda a, b: [*a, b], collapse), + "UNIQ_SEQ": (list, lambda a, b: [*a, b], lambda x: list(set(x))), "MAP": ( dict, lambda a, b: always_merger.merge(a, {b[0]: b[1]} if isinstance(b, tuple | list) else b), + lambda x: x, ), } @@ -552,7 +554,7 @@ def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: iterable = tuple(enumerate(iterable)) try: - output_class, add_fn = self._OUTPUT_BODIES[self.output_body.upper()] + output_class, add_fn, post_process_fn = self._OUTPUT_BODIES[self.output_body.upper()] except KeyError as exc: raise EntryInvalidError.from_entry(exc, entry) from exc @@ -572,7 +574,7 @@ def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: finally: self.__current_context = tuple() - return result + return post_process_fn(result) class EnumeratedItem(YAMLTag): From fa67bb710b990bf79cac2d2cfaa8878bbd1ea050 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 01:47:05 +0000 Subject: [PATCH 07/15] fix note --- website/docs/customize/blueprints/v1/tags.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/customize/blueprints/v1/tags.mdx b/website/docs/customize/blueprints/v1/tags.mdx index ffc966abf324..f00e845ddcae 100644 --- a/website/docs/customize/blueprints/v1/tags.mdx +++ b/website/docs/customize/blueprints/v1/tags.mdx @@ -211,7 +211,7 @@ This tag takes 3 arguments: - **output_object_type**: `SEQ`, `FLAT_SEQ`, `UNIQ_SEQ` or `MAP`. Controls whether the returned YAML will be a mapping or a sequence. With `FLAT_SEQ` the resulting list will also be flattened, while with `UNIQ_SEQ` duplicated entries will be removed - **single_item_yaml**: The YAML to use to create a single entry in the output object -:::note +:::info `FLAT_SEQ` and `UNIQ_SEQ` require authentik 2026.05+ ::: From 85f8de366db3cb0d10d19f40518b23888bea76e5 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 01:49:11 +0000 Subject: [PATCH 08/15] add test case --- authentik/blueprints/tests/fixtures/tags.yaml | 2 +- authentik/blueprints/tests/test_v1.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index ed0324ec2ede..65284054e1f4 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -122,7 +122,7 @@ entries: !Format ["prefixed-items-%%s-%%s", !Index 0, !Value 0] ] enumerate_nested_sequence_to_flat_sequence: !Enumerate [ - [!Context sequence, !Context sequence, scalar_value], + [!Context sequence, !Context sequence, scalar_value, [[deeply_nested]]], FLAT_SEQ, !Value 0 ] diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 9dcab9598442..1c1e517f18f3 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -192,6 +192,7 @@ def test_import_yaml_tags(self): "foo", "bar", "scalar_value", + "deeply_nested", ], "enumerate_sequence_to_uniq_sequence": [ "foo", From 2f77c049a2e3d7ee31ca1182764d9c20f425bc3c Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 02:06:05 +0000 Subject: [PATCH 09/15] add more-itertools package --- pyproject.toml | 1 + uv.lock | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 02c497c04a83..20b035b2b763 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dependencies = [ "kubernetes==35.0.0", "ldap3==2.9.1", "lxml==6.0.2", + "more-itertools>=10.8.0", "msgraph-sdk==1.55.0", "opencontainers==0.0.15", "packaging==26.0", diff --git a/uv.lock b/uv.lock index f1420d7a1a05..073e49924f52 100644 --- a/uv.lock +++ b/uv.lock @@ -245,6 +245,7 @@ dependencies = [ { name = "kubernetes" }, { name = "ldap3" }, { name = "lxml" }, + { name = "more-itertools" }, { name = "msgraph-sdk" }, { name = "opencontainers" }, { name = "packaging" }, @@ -354,6 +355,7 @@ requires-dist = [ { name = "kubernetes", specifier = "==35.0.0" }, { name = "ldap3", specifier = "==2.9.1" }, { name = "lxml", specifier = "==6.0.2" }, + { name = "more-itertools", specifier = ">=10.8.0" }, { name = "msgraph-sdk", specifier = "==1.55.0" }, { name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" }, { name = "packaging", specifier = "==26.0" }, @@ -2331,6 +2333,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/e5/bd382327e7ddaaa091f1f323d760408213136f41940d074b2ffffd9a1127/microsoft_kiota_serialization_text-1.9.8-py3-none-any.whl", hash = "sha256:dd89ae49693623c0e1d8f07414aa7d71b34959a5253131b8b63d81920f08c6b1", size = 8883, upload-time = "2025-12-29T15:25:23.662Z" }, ] +[[package]] +name = "more-itertools" +version = "10.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, +] + [[package]] name = "msal" version = "1.34.0" From fd2c20318967790b4cbd0b783ce0c8c9f3a68b96 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 02:29:23 +0000 Subject: [PATCH 10/15] improve --- authentik/blueprints/v1/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index e1580c6bf0f9..4bdf3a3d18c0 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -508,13 +508,13 @@ class Enumerate(YAMLTag, YAMLTagContext): output_body: Literal["SEQ", "FLAT_SEQ", "UNIQ_SEQ", "MAP"] _OUTPUT_BODIES = { - "SEQ": (list, lambda a, b: [*a, b], lambda x: x), + "SEQ": (list, lambda a, b: [*a, b], None), "FLAT_SEQ": (list, lambda a, b: [*a, b], collapse), "UNIQ_SEQ": (list, lambda a, b: [*a, b], lambda x: list(set(x))), "MAP": ( dict, lambda a, b: always_merger.merge(a, {b[0]: b[1]} if isinstance(b, tuple | list) else b), - lambda x: x, + None, ), } @@ -574,7 +574,7 @@ def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: finally: self.__current_context = tuple() - return post_process_fn(result) + return post_process_fn(result) if post_process_fn else result class EnumeratedItem(YAMLTag): From 8e5b0240026ff3c4861590356d6ba805d3d076a0 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 02:52:17 +0000 Subject: [PATCH 11/15] test --- authentik/blueprints/tests/fixtures/tags.yaml | 10 ---------- authentik/blueprints/tests/test_v1.py | 12 ------------ 2 files changed, 22 deletions(-) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index 65284054e1f4..b0e53727f0b0 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -121,16 +121,6 @@ entries: SEQ, !Format ["prefixed-items-%%s-%%s", !Index 0, !Value 0] ] - enumerate_nested_sequence_to_flat_sequence: !Enumerate [ - [!Context sequence, !Context sequence, scalar_value, [[deeply_nested]]], - FLAT_SEQ, - !Value 0 - ] - enumerate_sequence_to_uniq_sequence: !Enumerate [ - ["foo", "bar", "foo"], - UNIQ_SEQ, - !Value 0 - ] enumerate_sequence_to_mapping: !Enumerate [ !Context sequence, MAP, diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 1c1e517f18f3..04cabe36cc3f 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -186,18 +186,6 @@ def test_import_yaml_tags(self): "prefixed-items-0-foo", "prefixed-items-1-bar", ], - "enumerate_nested_sequence_to_flat_sequence": [ - "foo", - "bar", - "foo", - "bar", - "scalar_value", - "deeply_nested", - ], - "enumerate_sequence_to_uniq_sequence": [ - "foo", - "bar", - ], "enumerate_sequence_to_mapping": {"index: 0": "foo", "index: 1": "bar"}, "nested_complex_enumeration": { "0": { From 7a4b467690c1d362d6eb544f0693f1bec685ea62 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 03:13:20 +0000 Subject: [PATCH 12/15] test --- authentik/blueprints/tests/fixtures/tags.yaml | 5 +++++ authentik/blueprints/tests/test_v1.py | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index b0e53727f0b0..eeee54a5a886 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -121,6 +121,11 @@ entries: SEQ, !Format ["prefixed-items-%%s-%%s", !Index 0, !Value 0] ] + enumerate_sequence_to_uniq_sequence: !Enumerate [ + ["foo", "bar", "foo"], + UNIQ_SEQ, + !Value 0 + ] enumerate_sequence_to_mapping: !Enumerate [ !Context sequence, MAP, diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 04cabe36cc3f..95911a719d35 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -186,6 +186,10 @@ def test_import_yaml_tags(self): "prefixed-items-0-foo", "prefixed-items-1-bar", ], + "enumerate_sequence_to_uniq_sequence": [ + "foo", + "bar", + ], "enumerate_sequence_to_mapping": {"index: 0": "foo", "index: 1": "bar"}, "nested_complex_enumeration": { "0": { From ffb5cadc4f88c78cd893c64197302bf613274f0b Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 03:40:30 +0000 Subject: [PATCH 13/15] fix --- authentik/blueprints/v1/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index 4bdf3a3d18c0..37fb55efeff6 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -509,7 +509,7 @@ class Enumerate(YAMLTag, YAMLTagContext): _OUTPUT_BODIES = { "SEQ": (list, lambda a, b: [*a, b], None), - "FLAT_SEQ": (list, lambda a, b: [*a, b], collapse), + "FLAT_SEQ": (list, lambda a, b: [*a, b], lambda x: list(collapse(x))), "UNIQ_SEQ": (list, lambda a, b: [*a, b], lambda x: list(set(x))), "MAP": ( dict, From a9fefb72e6fe90473e9fdecc9e64f7084d5aa4b4 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 03:41:09 +0000 Subject: [PATCH 14/15] test --- authentik/blueprints/tests/fixtures/tags.yaml | 5 +++++ authentik/blueprints/tests/test_v1.py | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index eeee54a5a886..65284054e1f4 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -121,6 +121,11 @@ entries: SEQ, !Format ["prefixed-items-%%s-%%s", !Index 0, !Value 0] ] + enumerate_nested_sequence_to_flat_sequence: !Enumerate [ + [!Context sequence, !Context sequence, scalar_value, [[deeply_nested]]], + FLAT_SEQ, + !Value 0 + ] enumerate_sequence_to_uniq_sequence: !Enumerate [ ["foo", "bar", "foo"], UNIQ_SEQ, diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 95911a719d35..1c1e517f18f3 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -186,6 +186,14 @@ def test_import_yaml_tags(self): "prefixed-items-0-foo", "prefixed-items-1-bar", ], + "enumerate_nested_sequence_to_flat_sequence": [ + "foo", + "bar", + "foo", + "bar", + "scalar_value", + "deeply_nested", + ], "enumerate_sequence_to_uniq_sequence": [ "foo", "bar", From 2889ee8686a96717a3a141d128b37eba7b3403d3 Mon Sep 17 00:00:00 2001 From: Stanislav Dimov Date: Mon, 9 Mar 2026 03:48:34 +0000 Subject: [PATCH 15/15] fix order preservation when generating unique sequences --- authentik/blueprints/v1/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index 37fb55efeff6..b46c39ea72f1 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -15,7 +15,7 @@ from deepmerge import always_merger from django.apps import apps from django.db.models import Model, Q -from more_itertools import collapse +from more_itertools import collapse, unique_everseen from rest_framework.exceptions import ValidationError from rest_framework.fields import Field from rest_framework.serializers import Serializer @@ -510,7 +510,7 @@ class Enumerate(YAMLTag, YAMLTagContext): _OUTPUT_BODIES = { "SEQ": (list, lambda a, b: [*a, b], None), "FLAT_SEQ": (list, lambda a, b: [*a, b], lambda x: list(collapse(x))), - "UNIQ_SEQ": (list, lambda a, b: [*a, b], lambda x: list(set(x))), + "UNIQ_SEQ": (list, lambda a, b: [*a, b], lambda x: list(unique_everseen(x, key=tuple))), "MAP": ( dict, lambda a, b: always_merger.merge(a, {b[0]: b[1]} if isinstance(b, tuple | list) else b),