rename importer once again to make room for a JSON importer

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer 2023-08-07 17:46:14 +02:00
parent e2d18f6011
commit 42e2eb1529
No known key found for this signature in database
11 changed files with 53 additions and 37 deletions

View file

@ -14,7 +14,7 @@ from rest_framework.viewsets import ModelViewSet
from authentik.api.decorators import permission_required from authentik.api.decorators import permission_required
from authentik.blueprints.models import BlueprintInstance from authentik.blueprints.models import BlueprintInstance
from authentik.blueprints.v1.common import Blueprint, BlueprintEntry, BlueprintEntryDesiredState from authentik.blueprints.v1.common import Blueprint, BlueprintEntry, BlueprintEntryDesiredState
from authentik.blueprints.v1.importer import StringImporter, is_model_allowed from authentik.blueprints.v1.importer import YAMLStringImporter, is_model_allowed
from authentik.blueprints.v1.json_parser import BlueprintJSONParser from authentik.blueprints.v1.json_parser import BlueprintJSONParser
from authentik.blueprints.v1.oci import OCI_PREFIX from authentik.blueprints.v1.oci import OCI_PREFIX
from authentik.blueprints.v1.tasks import apply_blueprint, blueprints_find_dict from authentik.blueprints.v1.tasks import apply_blueprint, blueprints_find_dict
@ -52,7 +52,7 @@ class BlueprintInstanceSerializer(ModelSerializer):
if content == "": if content == "":
return content return content
context = self.instance.context if self.instance else {} context = self.instance.context if self.instance else {}
valid, logs = StringImporter(content, context).validate() valid, logs = YAMLStringImporter(content, context).validate()
if not valid: if not valid:
text_logs = "\n".join([x["event"] for x in logs]) text_logs = "\n".join([x["event"] for x in logs])
raise ValidationError(_("Failed to validate blueprint: %(logs)s" % {"logs": text_logs})) raise ValidationError(_("Failed to validate blueprint: %(logs)s" % {"logs": text_logs}))

View file

@ -5,7 +5,7 @@ from django.core.management.base import BaseCommand, no_translations
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.blueprints.models import BlueprintInstance from authentik.blueprints.models import BlueprintInstance
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
LOGGER = get_logger() LOGGER = get_logger()
@ -18,7 +18,7 @@ class Command(BaseCommand):
"""Apply all blueprints in order, abort when one fails to import""" """Apply all blueprints in order, abort when one fails to import"""
for blueprint_path in options.get("blueprints", []): for blueprint_path in options.get("blueprints", []):
content = BlueprintInstance(path=blueprint_path).retrieve() content = BlueprintInstance(path=blueprint_path).retrieve()
importer = StringImporter(content) importer = YAMLStringImporter(content)
valid, _ = importer.validate() valid, _ = importer.validate()
if not valid: if not valid:
self.stderr.write("blueprint invalid") self.stderr.write("blueprint invalid")

View file

@ -11,7 +11,7 @@ from authentik.blueprints.models import BlueprintInstance
def apply_blueprint(*files: str): def apply_blueprint(*files: str):
"""Apply blueprint before test""" """Apply blueprint before test"""
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
def wrapper_outer(func: Callable): def wrapper_outer(func: Callable):
"""Apply blueprint before test""" """Apply blueprint before test"""
@ -20,7 +20,7 @@ def apply_blueprint(*files: str):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
for file in files: for file in files:
content = BlueprintInstance(path=file).retrieve() content = BlueprintInstance(path=file).retrieve()
StringImporter(content).apply() YAMLStringImporter(content).apply()
return func(*args, **kwargs) return func(*args, **kwargs)
return wrapper return wrapper

View file

@ -6,7 +6,7 @@ from django.test import TransactionTestCase
from authentik.blueprints.models import BlueprintInstance from authentik.blueprints.models import BlueprintInstance
from authentik.blueprints.tests import apply_blueprint from authentik.blueprints.tests import apply_blueprint
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
from authentik.tenants.models import Tenant from authentik.tenants.models import Tenant
@ -25,7 +25,7 @@ def blueprint_tester(file_name: Path) -> Callable:
def tester(self: TestPackaged): def tester(self: TestPackaged):
base = Path("blueprints/") base = Path("blueprints/")
rel_path = Path(file_name).relative_to(base) rel_path = Path(file_name).relative_to(base)
importer = StringImporter(BlueprintInstance(path=str(rel_path)).retrieve()) importer = YAMLStringImporter(BlueprintInstance(path=str(rel_path)).retrieve())
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())

View file

@ -4,7 +4,7 @@ from os import environ
from django.test import TransactionTestCase from django.test import TransactionTestCase
from authentik.blueprints.v1.exporter import FlowExporter from authentik.blueprints.v1.exporter import FlowExporter
from authentik.blueprints.v1.importer import StringImporter, transaction_rollback from authentik.blueprints.v1.importer import YAMLStringImporter, transaction_rollback
from authentik.core.models import Group 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
@ -21,14 +21,14 @@ class TestBlueprintsV1(TransactionTestCase):
def test_blueprint_invalid_format(self): def test_blueprint_invalid_format(self):
"""Test blueprint with invalid format""" """Test blueprint with invalid format"""
importer = StringImporter('{"version": 3}') importer = YAMLStringImporter('{"version": 3}')
self.assertFalse(importer.validate()[0]) self.assertFalse(importer.validate()[0])
importer = StringImporter( importer = YAMLStringImporter(
'{"version": 1,"entries":[{"identifiers":{},"attrs":{},' '{"version": 1,"entries":[{"identifiers":{},"attrs":{},'
'"model": "authentik_core.User"}]}' '"model": "authentik_core.User"}]}'
) )
self.assertFalse(importer.validate()[0]) self.assertFalse(importer.validate()[0])
importer = StringImporter( importer = YAMLStringImporter(
'{"version": 1, "entries": [{"attrs": {"name": "test"}, ' '{"version": 1, "entries": [{"attrs": {"name": "test"}, '
'"identifiers": {}, ' '"identifiers": {}, '
'"model": "authentik_core.Group"}]}' '"model": "authentik_core.Group"}]}'
@ -54,7 +54,7 @@ class TestBlueprintsV1(TransactionTestCase):
}, },
) )
importer = StringImporter( importer = YAMLStringImporter(
'{"version": 1, "entries": [{"attrs": {"name": "test999", "attributes": ' '{"version": 1, "entries": [{"attrs": {"name": "test999", "attributes": '
'{"key": ["updated_value"]}}, "identifiers": {"attributes": {"other_key": ' '{"key": ["updated_value"]}}, "identifiers": {"attributes": {"other_key": '
'["other_value"]}}, "model": "authentik_core.Group"}]}' '["other_value"]}}, "model": "authentik_core.Group"}]}'
@ -103,7 +103,7 @@ class TestBlueprintsV1(TransactionTestCase):
self.assertEqual(len(export.entries), 3) self.assertEqual(len(export.entries), 3)
export_yaml = exporter.export_to_string() export_yaml = exporter.export_to_string()
importer = StringImporter(export_yaml) importer = YAMLStringImporter(export_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
@ -113,14 +113,14 @@ class TestBlueprintsV1(TransactionTestCase):
"""Test export and import it twice""" """Test export and import it twice"""
count_initial = Prompt.objects.filter(field_key="username").count() count_initial = Prompt.objects.filter(field_key="username").count()
importer = StringImporter(load_fixture("fixtures/static_prompt_export.yaml")) importer = YAMLStringImporter(load_fixture("fixtures/static_prompt_export.yaml"))
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
count_before = Prompt.objects.filter(field_key="username").count() count_before = Prompt.objects.filter(field_key="username").count()
self.assertEqual(count_initial + 1, count_before) self.assertEqual(count_initial + 1, count_before)
importer = StringImporter(load_fixture("fixtures/static_prompt_export.yaml")) importer = YAMLStringImporter(load_fixture("fixtures/static_prompt_export.yaml"))
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
self.assertEqual(Prompt.objects.filter(field_key="username").count(), count_before) self.assertEqual(Prompt.objects.filter(field_key="username").count(), count_before)
@ -130,7 +130,7 @@ class TestBlueprintsV1(TransactionTestCase):
ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").delete() ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").delete()
Group.objects.filter(name="test").delete() Group.objects.filter(name="test").delete()
environ["foo"] = generate_id() environ["foo"] = generate_id()
importer = StringImporter(load_fixture("fixtures/tags.yaml"), {"bar": "baz"}) importer = YAMLStringImporter(load_fixture("fixtures/tags.yaml"), {"bar": "baz"})
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
policy = ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").first() policy = ExpressionPolicy.objects.filter(name="foo-bar-baz-qux").first()
@ -247,7 +247,7 @@ class TestBlueprintsV1(TransactionTestCase):
exporter = FlowExporter(flow) exporter = FlowExporter(flow)
export_yaml = exporter.export_to_string() export_yaml = exporter.export_to_string()
importer = StringImporter(export_yaml) importer = YAMLStringImporter(export_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
self.assertTrue(UserLoginStage.objects.filter(name=stage_name).exists()) self.assertTrue(UserLoginStage.objects.filter(name=stage_name).exists())
@ -296,7 +296,7 @@ class TestBlueprintsV1(TransactionTestCase):
exporter = FlowExporter(flow) exporter = FlowExporter(flow)
export_yaml = exporter.export_to_string() export_yaml = exporter.export_to_string()
importer = StringImporter(export_yaml) importer = YAMLStringImporter(export_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())

View file

@ -1,7 +1,7 @@
"""Test blueprints v1""" """Test blueprints v1"""
from django.test import TransactionTestCase from django.test import TransactionTestCase
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
from authentik.core.models import Application, Token, User from authentik.core.models import Application, Token, User
from authentik.core.tests.utils import create_test_admin_user from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.models import Flow from authentik.flows.models import Flow
@ -18,7 +18,7 @@ class TestBlueprintsV1ConditionalFields(TransactionTestCase):
self.uid = generate_id() self.uid = generate_id()
import_yaml = load_fixture("fixtures/conditional_fields.yaml", uid=self.uid, user=user.pk) import_yaml = load_fixture("fixtures/conditional_fields.yaml", uid=self.uid, user=user.pk)
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())

View file

@ -1,7 +1,7 @@
"""Test blueprints v1""" """Test blueprints v1"""
from django.test import TransactionTestCase from django.test import TransactionTestCase
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import load_fixture from authentik.lib.tests.utils import load_fixture
@ -18,7 +18,7 @@ class TestBlueprintsV1Conditions(TransactionTestCase):
"fixtures/conditions_fulfilled.yaml", id1=flow_slug1, id2=flow_slug2 "fixtures/conditions_fulfilled.yaml", id1=flow_slug1, id2=flow_slug2
) )
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
# Ensure objects exist # Ensure objects exist
@ -35,7 +35,7 @@ class TestBlueprintsV1Conditions(TransactionTestCase):
"fixtures/conditions_not_fulfilled.yaml", id1=flow_slug1, id2=flow_slug2 "fixtures/conditions_not_fulfilled.yaml", id1=flow_slug1, id2=flow_slug2
) )
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
# Ensure objects do not exist # Ensure objects do not exist

View file

@ -1,7 +1,7 @@
"""Test blueprints v1""" """Test blueprints v1"""
from django.test import TransactionTestCase from django.test import TransactionTestCase
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import load_fixture from authentik.lib.tests.utils import load_fixture
@ -15,7 +15,7 @@ class TestBlueprintsV1State(TransactionTestCase):
flow_slug = generate_id() flow_slug = generate_id()
import_yaml = load_fixture("fixtures/state_present.yaml", id=flow_slug) import_yaml = load_fixture("fixtures/state_present.yaml", id=flow_slug)
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
# Ensure object exists # Ensure object exists
@ -30,7 +30,7 @@ class TestBlueprintsV1State(TransactionTestCase):
self.assertEqual(flow.title, "bar") self.assertEqual(flow.title, "bar")
# Ensure importer updates it # Ensure importer updates it
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
flow: Flow = Flow.objects.filter(slug=flow_slug).first() flow: Flow = Flow.objects.filter(slug=flow_slug).first()
@ -41,7 +41,7 @@ class TestBlueprintsV1State(TransactionTestCase):
flow_slug = generate_id() flow_slug = generate_id()
import_yaml = load_fixture("fixtures/state_created.yaml", id=flow_slug) import_yaml = load_fixture("fixtures/state_created.yaml", id=flow_slug)
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
# Ensure object exists # Ensure object exists
@ -56,7 +56,7 @@ class TestBlueprintsV1State(TransactionTestCase):
self.assertEqual(flow.title, "bar") self.assertEqual(flow.title, "bar")
# Ensure importer doesn't update it # Ensure importer doesn't update it
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
flow: Flow = Flow.objects.filter(slug=flow_slug).first() flow: Flow = Flow.objects.filter(slug=flow_slug).first()
@ -67,7 +67,7 @@ class TestBlueprintsV1State(TransactionTestCase):
flow_slug = generate_id() flow_slug = generate_id()
import_yaml = load_fixture("fixtures/state_created.yaml", id=flow_slug) import_yaml = load_fixture("fixtures/state_created.yaml", id=flow_slug)
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
# Ensure object exists # Ensure object exists
@ -75,7 +75,7 @@ class TestBlueprintsV1State(TransactionTestCase):
self.assertEqual(flow.slug, flow_slug) self.assertEqual(flow.slug, flow_slug)
import_yaml = load_fixture("fixtures/state_absent.yaml", id=flow_slug) import_yaml = load_fixture("fixtures/state_absent.yaml", id=flow_slug)
importer = StringImporter(import_yaml) importer = YAMLStringImporter(import_yaml)
self.assertTrue(importer.validate()[0]) self.assertTrue(importer.validate()[0])
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
flow: Flow = Flow.objects.filter(slug=flow_slug).first() flow: Flow = Flow.objects.filter(slug=flow_slug).first()

View file

@ -1,6 +1,7 @@
"""Blueprint importer""" """Blueprint importer"""
from contextlib import contextmanager from contextlib import contextmanager
from copy import deepcopy from copy import deepcopy
from json import loads
from typing import Any, Optional from typing import Any, Optional
from dacite.config import Config from dacite.config import Config
@ -27,6 +28,7 @@ from authentik.blueprints.v1.common import (
BlueprintLoader, BlueprintLoader,
EntryInvalidError, EntryInvalidError,
) )
from authentik.blueprints.v1.json_parser import BlueprintJSONDecoder
from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry
from authentik.core.models import ( from authentik.core.models import (
AuthenticatedSession, AuthenticatedSession,
@ -330,8 +332,8 @@ class Importer:
return successful, logs return successful, logs
class StringImporter(Importer): class YAMLStringImporter(Importer):
"""Importer that also parses from string""" """Importer that also parses from YAML string"""
def __init__(self, yaml_input: str, context: dict | None = None): def __init__(self, yaml_input: str, context: dict | None = None):
import_dict = load(yaml_input, BlueprintLoader) import_dict = load(yaml_input, BlueprintLoader)
@ -342,3 +344,17 @@ class StringImporter(Importer):
except DaciteError as exc: except DaciteError as exc:
raise EntryInvalidError from exc raise EntryInvalidError from exc
super().__init__(_import, context) super().__init__(_import, context)
class JSONStringImporter(Importer):
"""Importer that also parses from JSON string"""
def __init__(self, json_import: str, context: dict | None = None):
import_dict = loads(json_import, cls=BlueprintJSONDecoder)
try:
_import = from_dict(
Blueprint, import_dict, config=Config(cast=[BlueprintEntryDesiredState])
)
except DaciteError as exc:
raise EntryInvalidError from exc
super().__init__(_import, context)

View file

@ -26,7 +26,7 @@ from authentik.blueprints.models import (
BlueprintRetrievalFailed, BlueprintRetrievalFailed,
) )
from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, EntryInvalidError from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, EntryInvalidError
from authentik.blueprints.v1.importer import StringImporter from authentik.blueprints.v1.importer import YAMLStringImporter
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
from authentik.blueprints.v1.oci import OCI_PREFIX from authentik.blueprints.v1.oci import OCI_PREFIX
from authentik.events.monitored_tasks import ( from authentik.events.monitored_tasks import (
@ -190,7 +190,7 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
self.set_uid(slugify(instance.name)) self.set_uid(slugify(instance.name))
blueprint_content = instance.retrieve() blueprint_content = instance.retrieve()
file_hash = sha512(blueprint_content.encode()).hexdigest() file_hash = sha512(blueprint_content.encode()).hexdigest()
importer = StringImporter(blueprint_content, instance.context) importer = YAMLStringImporter(blueprint_content, instance.context)
if importer.blueprint.metadata: if importer.blueprint.metadata:
instance.metadata = asdict(importer.blueprint.metadata) instance.metadata = asdict(importer.blueprint.metadata)
valid, logs = importer.validate() valid, logs = importer.validate()

View file

@ -16,7 +16,7 @@ from structlog.stdlib import get_logger
from authentik.api.decorators import permission_required from authentik.api.decorators import permission_required
from authentik.blueprints.v1.exporter import FlowExporter from authentik.blueprints.v1.exporter import FlowExporter
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, StringImporter from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, YAMLStringImporter
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import CacheSerializer, LinkSerializer, PassiveSerializer from authentik.core.api.utils import CacheSerializer, LinkSerializer, PassiveSerializer
from authentik.events.utils import sanitize_dict from authentik.events.utils import sanitize_dict
@ -181,7 +181,7 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
if not file: if not file:
return Response(data=import_response.initial_data, status=400) return Response(data=import_response.initial_data, status=400)
importer = StringImporter(file.read().decode()) importer = YAMLStringImporter(file.read().decode())
valid, logs = importer.validate() valid, logs = importer.validate()
import_response.initial_data["logs"] = [sanitize_dict(log) for log in logs] import_response.initial_data["logs"] = [sanitize_dict(log) for log in logs]
import_response.initial_data["success"] = valid import_response.initial_data["success"] = valid