diff --git a/.vscode/settings.json b/.vscode/settings.json index 85b38a41b..b4282cb72 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,8 +24,11 @@ "!Find sequence", "!KeyOf scalar", "!Context scalar", + "!Context sequence", "!Format sequence", - "!Condition sequence" + "!Condition sequence", + "!Env sequence", + "!Env scalar" ], "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.importModuleSpecifierEnding": "index", diff --git a/authentik/blueprints/tests/fixtures/tags.yaml b/authentik/blueprints/tests/fixtures/tags.yaml index b91132df5..a05482bd2 100644 --- a/authentik/blueprints/tests/fixtures/tags.yaml +++ b/authentik/blueprints/tests/fixtures/tags.yaml @@ -4,6 +4,16 @@ context: policy_property: name policy_property_value: foo-bar-baz-qux entries: +- model: authentik_sources_oauth.oauthsource + identifiers: + slug: test + attrs: + name: test + provider_type: github + consumer_key: !Env foo + consumer_secret: !Env [bar, baz] + authentication_flow: !Find [authentik_flows.Flow, [slug, default-source-authentication]] + enrollment_flow: !Find [authentik_flows.Flow, [slug, default-source-enrollment]] - attrs: expression: return True identifiers: diff --git a/authentik/blueprints/tests/test_v1.py b/authentik/blueprints/tests/test_v1.py index 666c6de43..5cb5966b2 100644 --- a/authentik/blueprints/tests/test_v1.py +++ b/authentik/blueprints/tests/test_v1.py @@ -1,4 +1,6 @@ """Test blueprints v1""" +from os import environ + from django.test import TransactionTestCase from authentik.blueprints.tests import load_yaml_fixture @@ -9,6 +11,7 @@ from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.lib.generators import generate_id from authentik.policies.expression.models import ExpressionPolicy from authentik.policies.models import PolicyBinding +from authentik.sources.oauth.models import OAuthSource from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage from authentik.stages.user_login.models import UserLoginStage @@ -132,6 +135,7 @@ class TestBlueprintsV1(TransactionTestCase): """Test some yaml tags""" ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").delete() Group.objects.filter(name="test").delete() + environ["foo"] = generate_id() importer = Importer(load_yaml_fixture("fixtures/tags.yaml"), {"bar": "baz"}) self.assertTrue(importer.validate()[0]) self.assertTrue(importer.apply()) @@ -152,6 +156,12 @@ class TestBlueprintsV1(TransactionTestCase): } ) ) + self.assertTrue( + OAuthSource.objects.filter( + slug="test", + consumer_key=environ["foo"], + ) + ) def test_export_validate_import_policies(self): """Test export and validate it""" diff --git a/authentik/blueprints/v1/common.py b/authentik/blueprints/v1/common.py index 9e725a1d9..ad9c98bf3 100644 --- a/authentik/blueprints/v1/common.py +++ b/authentik/blueprints/v1/common.py @@ -4,6 +4,7 @@ from dataclasses import asdict, dataclass, field, is_dataclass from enum import Enum from functools import reduce from operator import ixor +from os import getenv from typing import Any, Literal, Optional from uuid import UUID @@ -160,6 +161,26 @@ class KeyOf(YAMLTag): ) +class Env(YAMLTag): + """Lookup environment variable with optional default""" + + key: str + default: Optional[Any] + + # pylint: disable=unused-argument + def __init__(self, loader: "BlueprintLoader", node: ScalarNode | SequenceNode) -> None: + super().__init__() + self.default = None + if isinstance(node, ScalarNode): + self.key = node.value + if isinstance(node, SequenceNode): + self.key = node.value[0].value + self.default = node.value[1].value + + def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: + return getenv(self.key, self.default) + + class Context(YAMLTag): """Lookup key from instance context""" @@ -332,6 +353,7 @@ class BlueprintLoader(SafeLoader): self.add_constructor("!Context", Context) self.add_constructor("!Format", Format) self.add_constructor("!Condition", Condition) + self.add_constructor("!Env", Env) class EntryInvalidError(SentryIgnoredException): diff --git a/website/developer-docs/blueprints/v1/tags.md b/website/developer-docs/blueprints/v1/tags.md index 9a72cab7b..562f34ed2 100644 --- a/website/developer-docs/blueprints/v1/tags.md +++ b/website/developer-docs/blueprints/v1/tags.md @@ -8,6 +8,12 @@ Resolves to the primary key of the model instance defined by id _my-policy-id_. If no matching entry can be found, an error is raised and the blueprint is invalid. +#### `!Env` + +Example: `password: !Env my_env_var` + +Returns the value of the given environment variable. Can be used as a scalar with `!Env my_env_var, default` to return a default value. + #### `!Find` Examples: