diff --git a/Dockerfile b/Dockerfile index 326253e68..ac0af7d9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -82,8 +82,7 @@ COPY ./pyproject.toml / COPY ./xml /xml COPY ./tests /tests COPY ./manage.py / -COPY ./blueprints/default /blueprints/default -COPY ./blueprints/system /blueprints/system +COPY ./blueprints /blueprints COPY ./lifecycle/ /lifecycle COPY --from=builder /work/authentik /authentik-proxy COPY --from=web-builder /work/web/dist/ /web/dist/ diff --git a/authentik/blueprints/migrations/0001_initial.py b/authentik/blueprints/migrations/0001_initial.py index 6496a023c..81502e660 100644 --- a/authentik/blueprints/migrations/0001_initial.py +++ b/authentik/blueprints/migrations/0001_initial.py @@ -19,10 +19,12 @@ def check_blueprint_v1_file(BlueprintInstance: type["BlueprintInstance"], path: """Check if blueprint should be imported""" from authentik.blueprints.models import BlueprintInstanceStatus from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata - from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_EXAMPLE + from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE with open(path, "r", encoding="utf-8") as blueprint_file: raw_blueprint = load(blueprint_file.read(), BlueprintLoader) + if not raw_blueprint: + return metadata = raw_blueprint.get("metadata", None) version = raw_blueprint.get("version", 1) if version != 1: @@ -33,7 +35,7 @@ def check_blueprint_v1_file(BlueprintInstance: type["BlueprintInstance"], path: meta = None if metadata: meta = from_dict(BlueprintMetadata, metadata) - if meta.labels.get(LABEL_AUTHENTIK_EXAMPLE, "").lower() == "true": + if meta.labels.get(LABEL_AUTHENTIK_INSTANTIATE, "").lower() == "false": return if not instance: instance = BlueprintInstance( diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index a52ad8835..fbd4724f3 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -24,6 +24,18 @@ entries: order: 0 """ +YAML_TAG_TESTS = """version: 1 +context: + foo: bar +entries: +- attrs: + expression: return True + identifiers: + name: !Format [foo-%s-%s, !Context foo, !Context bar] + id: default-source-enrollment-if-username + model: authentik_policies_expression.expressionpolicy +""" + class TestBlueprintsV1(TransactionTestCase): """Test Blueprints""" @@ -85,6 +97,14 @@ class TestBlueprintsV1(TransactionTestCase): self.assertEqual(Prompt.objects.filter(field_key="username").count(), count_before) + def test_import_yaml_tags(self): + """Test some yaml tags""" + ExpressionPolicy.objects.filter(name="foo-foo-bar").delete() + importer = Importer(YAML_TAG_TESTS, {"bar": "baz"}) + self.assertTrue(importer.validate()[0]) + self.assertTrue(importer.apply()) + self.assertTrue(ExpressionPolicy.objects.filter(name="foo-foo-bar")) + def test_export_validate_import_policies(self): """Test export and validate it""" flow_slug = generate_id() diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index 3d618d60b..d84f1d111 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -160,6 +160,29 @@ class Context(YAMLTag): return value +class Format(YAMLTag): + """Format a string""" + + format_string: str + args: list[Any] + + # pylint: disable=unused-argument + def __init__(self, loader: "BlueprintLoader", node: SequenceNode) -> None: + super().__init__() + self.format_string = node.value[0].value + self.args = [] + for raw_node in node.value[1:]: + self.args.append(raw_node.value) + + def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: + try: + print(self.format_string) + print(self.args) + return self.format_string % tuple(self.args) + except TypeError as exc: + raise EntryInvalidError(exc) + + class Find(YAMLTag): """Find any object""" @@ -214,6 +237,7 @@ class BlueprintLoader(SafeLoader): self.add_constructor("!KeyOf", KeyOf) self.add_constructor("!Find", Find) self.add_constructor("!Context", Context) + self.add_constructor("!Format", Format) class EntryInvalidError(SentryIgnoredException): diff --git a/authentik/blueprints/v1/labels.py b/authentik/blueprints/v1/labels.py index 4667bc7ca..fbfc5cd5a 100644 --- a/authentik/blueprints/v1/labels.py +++ b/authentik/blueprints/v1/labels.py @@ -1,4 +1,4 @@ """Blueprint labels""" LABEL_AUTHENTIK_SYSTEM = "blueprints.goauthentik.io/system" -LABEL_AUTHENTIK_EXAMPLE = "blueprints.goauthentik.io/example" +LABEL_AUTHENTIK_INSTANTIATE = "blueprints.goauthentik.io/instantiate" diff --git a/authentik/blueprints/v1/tasks.py b/authentik/blueprints/v1/tasks.py index 5fcc2aadf..a36fb0ed3 100644 --- a/authentik/blueprints/v1/tasks.py +++ b/authentik/blueprints/v1/tasks.py @@ -14,7 +14,7 @@ from yaml.error import YAMLError from authentik.blueprints.models import BlueprintInstance, BlueprintInstanceStatus from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata from authentik.blueprints.v1.importer import Importer -from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_EXAMPLE +from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE from authentik.events.monitored_tasks import ( MonitoredTask, TaskResult, @@ -59,11 +59,6 @@ def blueprints_find(): file_hash = sha512(path.read_bytes()).hexdigest() blueprint = BlueprintFile(path.relative_to(root), version, file_hash, path.stat().st_mtime) blueprint.meta = from_dict(BlueprintMetadata, metadata) if metadata else None - if ( - blueprint.meta - and blueprint.meta.labels.get(LABEL_AUTHENTIK_EXAMPLE, "").lower() == "true" - ): - continue blueprints.append(blueprint) return blueprints @@ -89,6 +84,11 @@ def blueprints_discover(self: MonitoredTask): def check_blueprint_v1_file(blueprint: BlueprintFile): """Check if blueprint should be imported""" instance: BlueprintInstance = BlueprintInstance.objects.filter(path=blueprint.path).first() + if ( + blueprint.meta + and blueprint.meta.labels.get(LABEL_AUTHENTIK_INSTANTIATE, "").lower() == "false" + ): + return if not instance: instance = BlueprintInstance( name=blueprint.meta.name if blueprint.meta else str(blueprint.path), diff --git a/blueprints/example/flows-enrollment-2-stage.yaml b/blueprints/example/flows-enrollment-2-stage.yaml index 117e41399..b077d9604 100644 --- a/blueprints/example/flows-enrollment-2-stage.yaml +++ b/blueprints/example/flows-enrollment-2-stage.yaml @@ -1,7 +1,7 @@ version: 1 metadata: labels: - blueprints.goauthentik.io/example: "true" + blueprints.goauthentik.io/instantiate: "false" name: Example - Enrollment (2 Stage) entries: - identifiers: diff --git a/blueprints/example/flows-enrollment-email-verification.yaml b/blueprints/example/flows-enrollment-email-verification.yaml index f6f24ecd4..b8bd2fb27 100644 --- a/blueprints/example/flows-enrollment-email-verification.yaml +++ b/blueprints/example/flows-enrollment-email-verification.yaml @@ -1,7 +1,7 @@ version: 1 metadata: labels: - blueprints.goauthentik.io/example: "true" + blueprints.goauthentik.io/instantiate: "false" name: Example - Enrollment with email verification entries: - identifiers: diff --git a/blueprints/example/flows-login-2fa.yaml b/blueprints/example/flows-login-2fa.yaml index b5261895f..903daa5aa 100644 --- a/blueprints/example/flows-login-2fa.yaml +++ b/blueprints/example/flows-login-2fa.yaml @@ -1,7 +1,7 @@ version: 1 metadata: labels: - blueprints.goauthentik.io/example: "true" + blueprints.goauthentik.io/instantiate: "false" name: Example - Two-factor Login entries: - identifiers: diff --git a/blueprints/example/flows-login-conditional-captcha.yaml b/blueprints/example/flows-login-conditional-captcha.yaml index 7d313bf8e..2d727acaf 100644 --- a/blueprints/example/flows-login-conditional-captcha.yaml +++ b/blueprints/example/flows-login-conditional-captcha.yaml @@ -1,7 +1,7 @@ version: 1 metadata: labels: - blueprints.goauthentik.io/example: "true" + blueprints.goauthentik.io/instantiate: "false" name: Example - Login with conditional Captcha entries: - identifiers: diff --git a/blueprints/example/flows-recovery-email-verification.yaml b/blueprints/example/flows-recovery-email-verification.yaml index ef407b568..f4a231b6e 100644 --- a/blueprints/example/flows-recovery-email-verification.yaml +++ b/blueprints/example/flows-recovery-email-verification.yaml @@ -1,7 +1,7 @@ version: 1 metadata: labels: - blueprints.goauthentik.io/example: "true" + blueprints.goauthentik.io/instantiate: "false" name: Example - Recovery with email verification entries: - identifiers: diff --git a/blueprints/example/flows-unenrollment.yaml b/blueprints/example/flows-unenrollment.yaml index c090ae7b9..898405cd6 100644 --- a/blueprints/example/flows-unenrollment.yaml +++ b/blueprints/example/flows-unenrollment.yaml @@ -1,7 +1,7 @@ version: 1 metadata: labels: - blueprints.goauthentik.io/example: "true" + blueprints.goauthentik.io/instantiate: "false" name: Example - User deletion entries: - identifiers: