blueprints: v1 (#1573)

* managed: move flowexporter to managed

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* *: implement SerializerModel in all models

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* managed: add initial api

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* managed: start blueprint

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* managed: spec

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* version blueprint

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* yep

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* remove v2, improve v1

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* start custom tag, more rebrand

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add default flows

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* move blueprints out of website

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* try new things

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add !lookup, fix web

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* update and cleanup default

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix tags in lists

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* don't save field if its set to default value

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* more flow cleanup

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* format web

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix missing serializer for sms

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* ignore _set fields

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* remove custom file extension

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate default flow to tenant

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* include blueprints

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens L 2022-07-31 17:11:44 +02:00 committed by GitHub
parent 882250a85e
commit 89c84f10d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
119 changed files with 2171 additions and 748 deletions

View File

@ -20,9 +20,10 @@
"todo-tree.tree.showCountsInTree": true, "todo-tree.tree.showCountsInTree": true,
"todo-tree.tree.showBadges": true, "todo-tree.tree.showBadges": true,
"python.formatting.provider": "black", "python.formatting.provider": "black",
"files.associations": { "yaml.customTags": [
"*.akflow": "yaml" "!Find sequence",
}, "!KeyOf scalar"
],
"typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "index", "typescript.preferences.importModuleSpecifierEnding": "index",
"typescript.tsdk": "./web/node_modules/typescript/lib", "typescript.tsdk": "./web/node_modules/typescript/lib",

View File

@ -81,6 +81,7 @@ COPY ./pyproject.toml /
COPY ./xml /xml COPY ./xml /xml
COPY ./tests /tests COPY ./tests /tests
COPY ./manage.py / COPY ./manage.py /
COPY ./blueprints/default /blueprints
COPY ./lifecycle/ /lifecycle COPY ./lifecycle/ /lifecycle
COPY --from=builder /work/authentik /authentik-proxy COPY --from=builder /work/authentik /authentik-proxy
COPY --from=web-builder /work/web/dist/ /web/dist/ COPY --from=web-builder /work/web/dist/ /web/dist/

View File

@ -5,10 +5,10 @@ from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from authentik import __version__ from authentik import __version__
from authentik.blueprints.tasks import managed_reconcile
from authentik.core.models import Group, User from authentik.core.models import Group, User
from authentik.core.tasks import clean_expired_models from authentik.core.tasks import clean_expired_models
from authentik.events.monitored_tasks import TaskResultStatus from authentik.events.monitored_tasks import TaskResultStatus
from authentik.managed.tasks import managed_reconcile
class TestAdminAPI(TestCase): class TestAdminAPI(TestCase):

View File

@ -12,6 +12,7 @@ from authentik.admin.api.version import VersionView
from authentik.admin.api.workers import WorkerView from authentik.admin.api.workers import WorkerView
from authentik.api.v3.config import ConfigView from authentik.api.v3.config import ConfigView
from authentik.api.views import APIBrowserView from authentik.api.views import APIBrowserView
from authentik.blueprints.api import BlueprintInstanceViewSet
from authentik.core.api.applications import ApplicationViewSet from authentik.core.api.applications import ApplicationViewSet
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet
from authentik.core.api.devices import AdminDeviceViewSet, DeviceViewSet from authentik.core.api.devices import AdminDeviceViewSet, DeviceViewSet
@ -131,6 +132,8 @@ router.register("events/notifications", NotificationViewSet)
router.register("events/transports", NotificationTransportViewSet) router.register("events/transports", NotificationTransportViewSet)
router.register("events/rules", NotificationRuleViewSet) router.register("events/rules", NotificationRuleViewSet)
router.register("managed/blueprints", BlueprintInstanceViewSet)
router.register("sources/all", SourceViewSet) router.register("sources/all", SourceViewSet)
router.register("sources/user_connections/all", UserSourceConnectionViewSet) router.register("sources/user_connections/all", UserSourceConnectionViewSet)
router.register("sources/user_connections/oauth", UserOAuthSourceConnectionViewSet) router.register("sources/user_connections/oauth", UserOAuthSourceConnectionViewSet)

View File

@ -0,0 +1,56 @@
"""Serializer mixin for managed models"""
from glob import glob
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.fields import CharField
from rest_framework.permissions import IsAdminUser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ListSerializer, ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.blueprints.models import BlueprintInstance
from authentik.lib.config import CONFIG
class ManagedSerializer:
"""Managed Serializer"""
managed = CharField(read_only=True, allow_null=True)
class BlueprintInstanceSerializer(ModelSerializer):
"""Info about a single blueprint instance file"""
class Meta:
model = BlueprintInstance
fields = [
"name",
"path",
"context",
"last_applied",
"status",
"enabled",
]
class BlueprintInstanceViewSet(ModelViewSet):
"""Blueprint instances"""
permission_classes = [IsAdminUser]
serializer_class = BlueprintInstanceSerializer
queryset = BlueprintInstance.objects.all()
search_fields = ["name", "path"]
filterset_fields = ["name", "path"]
@extend_schema(responses={200: ListSerializer(child=CharField())})
@action(detail=False, pagination_class=None, filter_backends=[])
def available(self, request: Request) -> Response:
"""Get blueprints"""
files = []
for folder in CONFIG.y("blueprint_locations"):
for file in glob(f"{folder}/**", recursive=True):
files.append(file)
return Response(files)

View File

@ -0,0 +1,15 @@
"""authentik Blueprints app"""
from django.apps import AppConfig
class AuthentikBlueprintsConfig(AppConfig):
"""authentik Blueprints app"""
name = "authentik.blueprints"
label = "authentik_blueprints"
verbose_name = "authentik Blueprints"
def ready(self) -> None:
from authentik.blueprints.tasks import managed_reconcile
managed_reconcile.delay()

View File

@ -0,0 +1,22 @@
"""Apply blueprint from commandline"""
from django.core.management.base import BaseCommand, no_translations
from authentik.blueprints.v1.importer import Importer
class Command(BaseCommand): # pragma: no cover
"""Apply blueprint from commandline"""
@no_translations
def handle(self, *args, **options):
"""Apply all blueprints in order, abort when one fails to import"""
for blueprint_path in options.get("blueprints", []):
with open(blueprint_path, "r", encoding="utf8") as blueprint_file:
importer = Importer(blueprint_file.read())
valid = importer.validate()
if not valid:
raise ValueError("blueprint invalid")
importer.apply()
def add_arguments(self, parser):
parser.add_argument("blueprints", nargs="+", type=str)

View File

@ -3,7 +3,7 @@ from typing import Callable, Optional
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.managed.models import ManagedModel from authentik.blueprints.models import ManagedModel
LOGGER = get_logger() LOGGER = get_logger()

View File

@ -0,0 +1,66 @@
# Generated by Django 4.0.6 on 2022-07-30 22:45
import uuid
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="BlueprintInstance",
fields=[
("created", models.DateTimeField(auto_now_add=True)),
("last_updated", models.DateTimeField(auto_now=True)),
(
"managed",
models.TextField(
default=None,
help_text="Objects which are managed by authentik. These objects are created and updated automatically. This is flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.",
null=True,
unique=True,
verbose_name="Managed by authentik",
),
),
(
"instance_uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("name", models.TextField()),
("path", models.TextField()),
("context", models.JSONField()),
("last_applied", models.DateTimeField(auto_now=True)),
(
"status",
models.TextField(
choices=[
("successful", "Successful"),
("warning", "Warning"),
("error", "Error"),
("unknown", "Unknown"),
]
),
),
("enabled", models.BooleanField(default=True)),
(
"managed_models",
django.contrib.postgres.fields.ArrayField(
base_field=models.TextField(), size=None
),
),
],
options={
"verbose_name": "Blueprint Instance",
"verbose_name_plural": "Blueprint Instances",
"unique_together": {("name", "path")},
},
),
]

View File

@ -0,0 +1,76 @@
"""Managed Object models"""
from uuid import uuid4
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
class ManagedModel(models.Model):
"""Model which can be managed by authentik exclusively"""
managed = models.TextField(
default=None,
null=True,
verbose_name=_("Managed by authentik"),
help_text=_(
(
"Objects which are managed by authentik. These objects are created and updated "
"automatically. This is flag only indicates that an object can be overwritten by "
"migrations. You can still modify the objects via the API, but expect changes "
"to be overwritten in a later update."
)
),
unique=True,
)
class Meta:
abstract = True
class BlueprintInstanceStatus(models.TextChoices):
"""Instance status"""
SUCCESSFUL = "successful"
WARNING = "warning"
ERROR = "error"
UNKNOWN = "unknown"
class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel):
"""Instance of a single blueprint. Can be parameterized via context attribute when
blueprint in `path` has inputs."""
instance_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.TextField()
path = models.TextField()
context = models.JSONField()
last_applied = models.DateTimeField(auto_now=True)
status = models.TextField(choices=BlueprintInstanceStatus.choices)
enabled = models.BooleanField(default=True)
managed_models = ArrayField(models.TextField())
@property
def serializer(self) -> Serializer:
from authentik.blueprints.api import BlueprintInstanceSerializer
return BlueprintInstanceSerializer
def __str__(self) -> str:
return f"Blueprint Instance {self.name}"
class Meta:
verbose_name = _("Blueprint Instance")
verbose_name_plural = _("Blueprint Instances")
unique_together = (
(
"name",
"path",
),
)

View File

@ -0,0 +1,17 @@
"""managed Settings"""
from celery.schedules import crontab
from authentik.lib.utils.time import fqdn_rand
CELERY_BEAT_SCHEDULE = {
"blueprints_reconcile": {
"task": "authentik.blueprints.tasks.managed_reconcile",
"schedule": crontab(minute=fqdn_rand("managed_reconcile"), hour="*/4"),
"options": {"queue": "authentik_scheduled"},
},
"blueprints_config_file_discovery": {
"task": "authentik.blueprints.tasks.config_file_discovery",
"schedule": crontab(minute=fqdn_rand("config_file_discovery"), hour="*"),
"options": {"queue": "authentik_scheduled"},
},
}

View File

@ -1,6 +1,8 @@
"""managed tasks""" """managed tasks"""
from django.db import DatabaseError from django.db import DatabaseError
from django.db.utils import ProgrammingError
from authentik.blueprints.manager import ObjectManager
from authentik.core.tasks import CELERY_APP from authentik.core.tasks import CELERY_APP
from authentik.events.monitored_tasks import ( from authentik.events.monitored_tasks import (
MonitoredTask, MonitoredTask,
@ -8,7 +10,6 @@ from authentik.events.monitored_tasks import (
TaskResultStatus, TaskResultStatus,
prefill_task, prefill_task,
) )
from authentik.managed.manager import ObjectManager
@CELERY_APP.task( @CELERY_APP.task(
@ -24,6 +25,5 @@ def managed_reconcile(self: MonitoredTask):
self.set_status( self.set_status(
TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated managed models."]) TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated managed models."])
) )
except DatabaseError as exc: # pragma: no cover except (DatabaseError, ProgrammingError) as exc: # pragma: no cover
self.set_status(TaskResult(TaskResultStatus.WARNING, [str(exc)])) self.set_status(TaskResult(TaskResultStatus.WARNING, [str(exc)]))
self.retry()

View File

View File

@ -1,7 +1,7 @@
"""managed tests""" """managed tests"""
from django.test import TestCase from django.test import TestCase
from authentik.managed.tasks import managed_reconcile from authentik.blueprints.tasks import managed_reconcile
class TestManaged(TestCase): class TestManaged(TestCase):

View File

@ -0,0 +1,34 @@
"""authentik managed models tests"""
from typing import Callable, Type
from django.apps import apps
from django.test import TestCase
from authentik.blueprints.v1.importer import EXCLUDED_MODELS
from authentik.lib.models import SerializerModel
class TestModels(TestCase):
"""Test Models"""
def serializer_tester_factory(test_model: Type[SerializerModel]) -> Callable:
"""Test serializer"""
def tester(self: TestModels):
if test_model._meta.abstract:
return
model_class = test_model()
self.assertTrue(isinstance(model_class, SerializerModel))
self.assertIsNotNone(model_class.serializer)
return tester
for app in apps.get_app_configs():
if not app.label.startswith("authentik"):
continue
for model in app.get_models():
if model in EXCLUDED_MODELS:
continue
setattr(TestModels, f"test_{app.label}_{model.__name__}", serializer_tester_factory(model))

View File

@ -1,11 +1,9 @@
"""Test flow transfer""" """Test flow Transport"""
from django.test import TransactionTestCase from django.test import TransactionTestCase
from yaml import dump
from authentik.blueprints.v1.exporter import Exporter
from authentik.blueprints.v1.importer import Importer, transaction_rollback
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.flows.transfer.common import DataclassDumper
from authentik.flows.transfer.exporter import FlowExporter
from authentik.flows.transfer.importer import FlowImporter, transaction_rollback
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.policies.expression.models import ExpressionPolicy from authentik.policies.expression.models import ExpressionPolicy
from authentik.policies.models import PolicyBinding from authentik.policies.models import PolicyBinding
@ -33,14 +31,14 @@ STATIC_PROMPT_EXPORT = """{
}""" }"""
class TestFlowTransfer(TransactionTestCase): class TestFlowTransport(TransactionTestCase):
"""Test flow transfer""" """Test flow Transport"""
def test_bundle_invalid_format(self): def test_bundle_invalid_format(self):
"""Test bundle with invalid format""" """Test bundle with invalid format"""
importer = FlowImporter('{"version": 3}') importer = Importer('{"version": 3}')
self.assertFalse(importer.validate()) self.assertFalse(importer.validate())
importer = FlowImporter( importer = Importer(
( (
'{"version": 1,"entries":[{"identifiers":{},"attrs":{},' '{"version": 1,"entries":[{"identifiers":{},"attrs":{},'
'"model": "authentik_core.User"}]}' '"model": "authentik_core.User"}]}'
@ -66,12 +64,12 @@ class TestFlowTransfer(TransactionTestCase):
order=0, order=0,
) )
exporter = FlowExporter(flow) exporter = Exporter(flow)
export = exporter.export() export = exporter.export()
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 = FlowImporter(export_yaml) importer = Importer(export_yaml)
self.assertTrue(importer.validate()) self.assertTrue(importer.validate())
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
@ -81,14 +79,14 @@ class TestFlowTransfer(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 = FlowImporter(STATIC_PROMPT_EXPORT) importer = Importer(STATIC_PROMPT_EXPORT)
self.assertTrue(importer.validate()) self.assertTrue(importer.validate())
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 = FlowImporter(STATIC_PROMPT_EXPORT) importer = Importer(STATIC_PROMPT_EXPORT)
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)
@ -114,12 +112,10 @@ class TestFlowTransfer(TransactionTestCase):
fsb = FlowStageBinding.objects.create(target=flow, stage=user_login, order=0) fsb = FlowStageBinding.objects.create(target=flow, stage=user_login, order=0)
PolicyBinding.objects.create(policy=flow_policy, target=fsb, order=0) PolicyBinding.objects.create(policy=flow_policy, target=fsb, order=0)
exporter = FlowExporter(flow) exporter = Exporter(flow)
export = exporter.export() export_yaml = exporter.export_to_string()
export_yaml = dump(export, Dumper=DataclassDumper) importer = Importer(export_yaml)
importer = FlowImporter(export_yaml)
self.assertTrue(importer.validate()) self.assertTrue(importer.validate())
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())
@ -159,11 +155,10 @@ class TestFlowTransfer(TransactionTestCase):
FlowStageBinding.objects.create(target=flow, stage=first_stage, order=0) FlowStageBinding.objects.create(target=flow, stage=first_stage, order=0)
exporter = FlowExporter(flow) exporter = Exporter(flow)
export = exporter.export() export_yaml = exporter.export_to_string()
export_yaml = dump(export, Dumper=DataclassDumper)
importer = FlowImporter(export_yaml) importer = Importer(export_yaml)
self.assertTrue(importer.validate()) self.assertTrue(importer.validate())
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())

View File

@ -5,25 +5,25 @@ from typing import Callable
from django.test import TransactionTestCase from django.test import TransactionTestCase
from authentik.flows.transfer.importer import FlowImporter from authentik.blueprints.v1.importer import Importer
class TestTransferDocs(TransactionTestCase): class TestTransportDocs(TransactionTestCase):
"""Empty class, test methods are added dynamically""" """Empty class, test methods are added dynamically"""
def pbflow_tester(file_name: str) -> Callable: def pbflow_tester(file_name: str) -> Callable:
"""This is used instead of subTest for better visibility""" """This is used instead of subTest for better visibility"""
def tester(self: TestTransferDocs): def tester(self: TestTransportDocs):
with open(file_name, "r", encoding="utf8") as flow_json: with open(file_name, "r", encoding="utf8") as flow_json:
importer = FlowImporter(flow_json.read()) importer = Importer(flow_json.read())
self.assertTrue(importer.validate()) self.assertTrue(importer.validate())
self.assertTrue(importer.apply()) self.assertTrue(importer.apply())
return tester return tester
for flow_file in glob("website/static/flows/*.akflow"): for flow_file in glob("website/static/flows/*.yaml"):
method_name = Path(flow_file).stem.replace("-", "_").replace(".", "_") method_name = Path(flow_file).stem.replace("-", "_").replace(".", "_")
setattr(TestTransferDocs, f"test_flow_{method_name}", pbflow_tester(flow_file)) setattr(TestTransportDocs, f"test_flow_{method_name}", pbflow_tester(flow_file))

View File

View File

@ -0,0 +1,177 @@
"""transfer common classes"""
from collections import OrderedDict
from dataclasses import asdict, dataclass, field, is_dataclass
from enum import Enum
from typing import Any, Optional
from uuid import UUID
from django.apps import apps
from django.db.models import Model, Q
from rest_framework.fields import Field
from rest_framework.serializers import Serializer
from yaml import SafeDumper, SafeLoader, ScalarNode, SequenceNode
from authentik.lib.models import SerializerModel
from authentik.lib.sentry import SentryIgnoredException
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)
data = dict(serializer.data)
for field_name, _field in serializer.fields.items():
_field: Field
if field_name not in data:
continue
if _field.read_only:
data.pop(field_name, None)
if _field.default == data.get(field_name, None):
data.pop(field_name, None)
if field_name.endswith("_set"):
data.pop(field_name, None)
return data
@dataclass
class BlueprintEntry:
"""Single entry of a bundle"""
identifiers: dict[str, Any]
model: str
attrs: dict[str, Any]
# pylint: disable=invalid-name
id: Optional[str] = None
_instance: Optional[Model] = None
@staticmethod
def from_model(model: SerializerModel, *extra_identifier_names: str) -> "BlueprintEntry":
"""Convert a SerializerModel instance to a Bundle Entry"""
identifiers = {
"pk": model.pk,
}
all_attrs = get_attrs(model)
for extra_identifier_name in extra_identifier_names:
identifiers[extra_identifier_name] = all_attrs.pop(extra_identifier_name)
return BlueprintEntry(
identifiers=identifiers,
model=f"{model._meta.app_label}.{model._meta.model_name}",
attrs=all_attrs,
)
def tag_resolver(self, value: Any, blueprint: "Blueprint") -> Any:
"""Check if we have any special tags that need handling"""
if isinstance(value, YAMLTag):
return value.resolve(self, blueprint)
if isinstance(value, dict):
for key, inner_value in value.items():
value[key] = self.tag_resolver(inner_value, blueprint)
if isinstance(value, list):
for idx, inner_value in enumerate(value):
value[idx] = self.tag_resolver(inner_value, blueprint)
return value
def get_attrs(self, blueprint: "Blueprint") -> dict[str, Any]:
"""Get attributes of this entry, with all yaml tags resolved"""
return self.tag_resolver(self.attrs, blueprint)
def get_identifiers(self, blueprint: "Blueprint") -> dict[str, Any]:
"""Get attributes of this entry, with all yaml tags resolved"""
return self.tag_resolver(self.identifiers, blueprint)
@dataclass
class Blueprint:
"""Dataclass used for a full export"""
version: int = field(default=1)
entries: list[BlueprintEntry] = field(default_factory=list)
class YAMLTag:
"""Base class for all YAML Tags"""
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
"""Implement yaml tag logic"""
raise NotImplementedError
class KeyOf(YAMLTag):
"""Reference another object by their ID"""
id_from: str
# pylint: disable=unused-argument
def __init__(self, loader: "BlueprintLoader", node: ScalarNode) -> None:
super().__init__()
self.id_from = node.value
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
for _entry in blueprint.entries:
if _entry.id == self.id_from and _entry._instance:
return _entry._instance.pk
raise ValueError(
f"KeyOf: failed to find entry with `id` of `{self.id_from}` and a model instance"
)
class Find(YAMLTag):
"""Find any object"""
model_name: str
conditions: list[list]
model_class: type[Model]
def __init__(self, loader: "BlueprintLoader", node: SequenceNode) -> None:
super().__init__()
self.model_name = node.value[0].value
self.model_class = apps.get_model(*self.model_name.split("."))
self.conditions = []
for raw_node in node.value[1:]:
values = []
for node_values in raw_node.value:
values.append(loader.construct_object(node_values))
self.conditions.append(values)
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
query = Q()
for cond in self.conditions:
query &= Q(**{cond[0]: cond[1]})
instance = self.model_class.objects.filter(query).first()
if instance:
return instance.pk
return None
class BlueprintDumper(SafeDumper):
"""Dump dataclasses to yaml"""
default_flow_style = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_representer(UUID, lambda self, data: self.represent_str(str(data)))
self.add_representer(OrderedDict, lambda self, data: self.represent_dict(dict(data)))
self.add_representer(Enum, lambda self, data: self.represent_str(data.value))
def represent(self, data) -> None:
if is_dataclass(data):
data = asdict(data)
return super().represent(data)
class BlueprintLoader(SafeLoader):
"""Loader for blueprints with custom tag support"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_constructor("!KeyOf", KeyOf)
self.add_constructor("!Find", Find)
class EntryInvalidError(SentryIgnoredException):
"""Error raised when an entry is invalid"""

View File

@ -5,14 +5,14 @@ from uuid import UUID
from django.db.models import Q from django.db.models import Q
from yaml import dump from yaml import dump
from authentik.blueprints.v1.common import Blueprint, BlueprintDumper, BlueprintEntry
from authentik.flows.models import Flow, FlowStageBinding, Stage from authentik.flows.models import Flow, FlowStageBinding, Stage
from authentik.flows.transfer.common import DataclassDumper, FlowBundle, FlowBundleEntry
from authentik.policies.models import Policy, PolicyBinding from authentik.policies.models import Policy, PolicyBinding
from authentik.stages.prompt.models import PromptStage from authentik.stages.prompt.models import PromptStage
class FlowExporter: class Exporter:
"""Export flow with attached stages into json""" """Export flow with attached stages into yaml"""
flow: Flow flow: Flow
with_policies: bool with_policies: bool
@ -31,21 +31,21 @@ class FlowExporter:
"pbm_uuid", flat=True "pbm_uuid", flat=True
) )
def walk_stages(self) -> Iterator[FlowBundleEntry]: def walk_stages(self) -> Iterator[BlueprintEntry]:
"""Convert all stages attached to self.flow into FlowBundleEntry objects""" """Convert all stages attached to self.flow into BlueprintEntry objects"""
stages = Stage.objects.filter(flow=self.flow).select_related().select_subclasses() stages = Stage.objects.filter(flow=self.flow).select_related().select_subclasses()
for stage in stages: for stage in stages:
if isinstance(stage, PromptStage): if isinstance(stage, PromptStage):
pass pass
yield FlowBundleEntry.from_model(stage, "name") yield BlueprintEntry.from_model(stage, "name")
def walk_stage_bindings(self) -> Iterator[FlowBundleEntry]: def walk_stage_bindings(self) -> Iterator[BlueprintEntry]:
"""Convert all bindings attached to self.flow into FlowBundleEntry objects""" """Convert all bindings attached to self.flow into BlueprintEntry objects"""
bindings = FlowStageBinding.objects.filter(target=self.flow).select_related() bindings = FlowStageBinding.objects.filter(target=self.flow).select_related()
for binding in bindings: for binding in bindings:
yield FlowBundleEntry.from_model(binding, "target", "stage", "order") yield BlueprintEntry.from_model(binding, "target", "stage", "order")
def walk_policies(self) -> Iterator[FlowBundleEntry]: def walk_policies(self) -> Iterator[BlueprintEntry]:
"""Walk over all policies. This is done at the beginning of the export for stages that have """Walk over all policies. This is done at the beginning of the export for stages that have
a direct foreign key to a policy.""" a direct foreign key to a policy."""
# Special case for PromptStage as that has a direct M2M to policy, we have to ensure # Special case for PromptStage as that has a direct M2M to policy, we have to ensure
@ -54,28 +54,28 @@ class FlowExporter:
query = Q(bindings__in=self.pbm_uuids) | Q(promptstage__in=prompt_stages) query = Q(bindings__in=self.pbm_uuids) | Q(promptstage__in=prompt_stages)
policies = Policy.objects.filter(query).select_related() policies = Policy.objects.filter(query).select_related()
for policy in policies: for policy in policies:
yield FlowBundleEntry.from_model(policy) yield BlueprintEntry.from_model(policy)
def walk_policy_bindings(self) -> Iterator[FlowBundleEntry]: def walk_policy_bindings(self) -> Iterator[BlueprintEntry]:
"""Walk over all policybindings relative to us. This is run at the end of the export, as """Walk over all policybindings relative to us. This is run at the end of the export, as
we are sure all objects exist now.""" we are sure all objects exist now."""
bindings = PolicyBinding.objects.filter(target__in=self.pbm_uuids).select_related() bindings = PolicyBinding.objects.filter(target__in=self.pbm_uuids).select_related()
for binding in bindings: for binding in bindings:
yield FlowBundleEntry.from_model(binding, "policy", "target", "order") yield BlueprintEntry.from_model(binding, "policy", "target", "order")
def walk_stage_prompts(self) -> Iterator[FlowBundleEntry]: def walk_stage_prompts(self) -> Iterator[BlueprintEntry]:
"""Walk over all prompts associated with any PromptStages""" """Walk over all prompts associated with any PromptStages"""
prompt_stages = PromptStage.objects.filter(flow=self.flow) prompt_stages = PromptStage.objects.filter(flow=self.flow)
for stage in prompt_stages: for stage in prompt_stages:
for prompt in stage.fields.all(): for prompt in stage.fields.all():
yield FlowBundleEntry.from_model(prompt) yield BlueprintEntry.from_model(prompt)
def export(self) -> FlowBundle: def export(self) -> Blueprint:
"""Create a list of all objects including the flow""" """Create a list of all objects including the flow"""
if self.with_policies: if self.with_policies:
self._prepare_pbm() self._prepare_pbm()
bundle = FlowBundle() bundle = Blueprint()
bundle.entries.append(FlowBundleEntry.from_model(self.flow, "slug")) bundle.entries.append(BlueprintEntry.from_model(self.flow, "slug"))
if self.with_stage_prompts: if self.with_stage_prompts:
bundle.entries.extend(self.walk_stage_prompts()) bundle.entries.extend(self.walk_stage_prompts())
if self.with_policies: if self.with_policies:
@ -87,6 +87,6 @@ class FlowExporter:
return bundle return bundle
def export_to_string(self) -> str: def export_to_string(self) -> str:
"""Call export and convert it to json""" """Call export and convert it to yaml"""
bundle = self.export() bundle = self.export()
return dump(bundle, Dumper=DataclassDumper) return dump(bundle, Dumper=BlueprintDumper)

View File

@ -1,4 +1,4 @@
"""Flow importer""" """Blueprint importer"""
from contextlib import contextmanager from contextlib import contextmanager
from copy import deepcopy from copy import deepcopy
from typing import Any from typing import Any
@ -13,15 +13,39 @@ from django.db.utils import IntegrityError
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.serializers import BaseSerializer, Serializer from rest_framework.serializers import BaseSerializer, Serializer
from structlog.stdlib import BoundLogger, get_logger from structlog.stdlib import BoundLogger, get_logger
from yaml import safe_load from yaml import load
from authentik.flows.models import Flow, FlowStageBinding, Stage from authentik.blueprints.v1.common import (
from authentik.flows.transfer.common import EntryInvalidError, FlowBundle, FlowBundleEntry Blueprint,
BlueprintEntry,
BlueprintLoader,
EntryInvalidError,
)
from authentik.core.models import (
AuthenticatedSession,
PropertyMapping,
Provider,
Source,
UserSourceConnection,
)
from authentik.flows.models import Stage
from authentik.lib.models import SerializerModel from authentik.lib.models import SerializerModel
from authentik.policies.models import Policy, PolicyBinding from authentik.outposts.models import OutpostServiceConnection
from authentik.stages.prompt.models import Prompt from authentik.policies.models import Policy, PolicyBindingModel
ALLOWED_MODELS = (Flow, FlowStageBinding, Stage, Policy, PolicyBinding, Prompt) EXCLUDED_MODELS = (
# Base classes
Provider,
Source,
PropertyMapping,
UserSourceConnection,
Stage,
OutpostServiceConnection,
Policy,
PolicyBindingModel,
# Classes that have other dependencies
AuthenticatedSession,
)
@contextmanager @contextmanager
@ -34,17 +58,17 @@ def transaction_rollback():
atomic.__exit__(IntegrityError, None, None) atomic.__exit__(IntegrityError, None, None)
class FlowImporter: class Importer:
"""Import Flow from json""" """Import Blueprint from YAML"""
logger: BoundLogger logger: BoundLogger
def __init__(self, yaml_input: str): def __init__(self, yaml_input: str):
self.__pk_map: dict[Any, Model] = {} self.__pk_map: dict[Any, Model] = {}
self.logger = get_logger() self.logger = get_logger()
import_dict = safe_load(yaml_input) import_dict = load(yaml_input, BlueprintLoader)
try: try:
self.__import = from_dict(FlowBundle, import_dict) self.__import = from_dict(Blueprint, import_dict)
except DaciteError as exc: except DaciteError as exc:
raise EntryInvalidError from exc raise EntryInvalidError from exc
@ -75,7 +99,9 @@ class FlowImporter:
"""Generate an or'd query from all identifiers in an entry""" """Generate an or'd query from all identifiers in an entry"""
# Since identifiers can also be pk-references to other objects (see FlowStageBinding) # Since identifiers can also be pk-references to other objects (see FlowStageBinding)
# we have to ensure those references are also replaced # we have to ensure those references are also replaced
main_query = Q(pk=attrs["pk"]) main_query = Q()
if "pk" in attrs:
main_query = Q(pk=attrs["pk"])
sub_query = Q() sub_query = Q()
for identifier, value in attrs.items(): for identifier, value in attrs.items():
if isinstance(value, dict): if isinstance(value, dict):
@ -85,11 +111,12 @@ class FlowImporter:
sub_query &= Q(**{identifier: value}) sub_query &= Q(**{identifier: value})
return main_query | sub_query return main_query | sub_query
def _validate_single(self, entry: FlowBundleEntry) -> BaseSerializer: def _validate_single(self, entry: BlueprintEntry) -> BaseSerializer:
"""Validate a single entry""" """Validate a single entry"""
model_app_label, model_name = entry.model.split(".") model_app_label, model_name = entry.model.split(".")
model: type[SerializerModel] = apps.get_model(model_app_label, model_name) model: type[SerializerModel] = apps.get_model(model_app_label, model_name)
if not isinstance(model(), ALLOWED_MODELS): # Don't use isinstance since we don't want to check for inheritance
if model in EXCLUDED_MODELS:
raise EntryInvalidError(f"Model {model} not allowed") raise EntryInvalidError(f"Model {model} not allowed")
# If we try to validate without referencing a possible instance # If we try to validate without referencing a possible instance
@ -97,7 +124,7 @@ class FlowImporter:
# the full serializer for later usage # the full serializer for later usage
# Because a model might have multiple unique columns, we chain all identifiers together # Because a model might have multiple unique columns, we chain all identifiers together
# to create an OR query. # to create an OR query.
updated_identifiers = self.__update_pks_for_attrs(entry.identifiers) updated_identifiers = self.__update_pks_for_attrs(entry.get_identifiers(self.__import))
for key, value in list(updated_identifiers.items()): for key, value in list(updated_identifiers.items()):
if isinstance(value, dict) and "pk" in value: if isinstance(value, dict) and "pk" in value:
del updated_identifiers[key] del updated_identifiers[key]
@ -121,7 +148,7 @@ class FlowImporter:
if "pk" in updated_identifiers: if "pk" in updated_identifiers:
model_instance.pk = updated_identifiers["pk"] model_instance.pk = updated_identifiers["pk"]
serializer_kwargs["instance"] = model_instance serializer_kwargs["instance"] = model_instance
full_data = self.__update_pks_for_attrs(entry.attrs) full_data = self.__update_pks_for_attrs(entry.get_attrs(self.__import))
full_data.update(updated_identifiers) full_data.update(updated_identifiers)
serializer_kwargs["data"] = full_data serializer_kwargs["data"] = full_data
@ -133,7 +160,7 @@ class FlowImporter:
return serializer return serializer
def apply(self) -> bool: def apply(self) -> bool:
"""Apply (create/update) flow json, in database transaction""" """Apply (create/update) models yaml, in database transaction"""
try: try:
with transaction.atomic(): with transaction.atomic():
if not self._apply_models(): if not self._apply_models():
@ -146,10 +173,9 @@ class FlowImporter:
return True return True
def _apply_models(self) -> bool: def _apply_models(self) -> bool:
"""Apply (create/update) flow json""" """Apply (create/update) models yaml"""
self.__pk_map = {} self.__pk_map = {}
entries = deepcopy(self.__import.entries) for entry in self.__import.entries:
for entry in entries:
model_app_label, model_name = entry.model.split(".") model_app_label, model_name = entry.model.split(".")
try: try:
model: SerializerModel = apps.get_model(model_app_label, model_name) model: SerializerModel = apps.get_model(model_app_label, model_name)
@ -166,7 +192,9 @@ class FlowImporter:
return False return False
model = serializer.save() model = serializer.save()
self.__pk_map[entry.identifiers["pk"]] = model.pk if "pk" in entry.identifiers:
self.__pk_map[entry.identifiers["pk"]] = model.pk
entry._instance = model
self.logger.debug("updated model", model=model, pk=model.pk) self.logger.debug("updated model", model=model, pk=model.pk)
return True return True
@ -174,6 +202,7 @@ class FlowImporter:
"""Validate loaded flow export, ensure all models are allowed """Validate loaded flow export, ensure all models are allowed
and serializers have no errors""" and serializers have no errors"""
self.logger.debug("Starting flow import validation") self.logger.debug("Starting flow import validation")
orig_import = deepcopy(self.__import)
if self.__import.version != 1: if self.__import.version != 1:
self.logger.warning("Invalid bundle version") self.logger.warning("Invalid bundle version")
return False return False
@ -181,4 +210,5 @@ class FlowImporter:
successful = self._apply_models() successful = self._apply_models()
if not successful: if not successful:
self.logger.debug("Flow validation failed") self.logger.debug("Flow validation failed")
self.__import = orig_import
return successful return successful

View File

@ -14,12 +14,12 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import GenericViewSet
from authentik.api.decorators import permission_required from authentik.api.decorators import permission_required
from authentik.blueprints.api import ManagedSerializer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer
from authentik.core.expression import PropertyMappingEvaluator from authentik.core.expression import PropertyMappingEvaluator
from authentik.core.models import PropertyMapping from authentik.core.models import PropertyMapping
from authentik.lib.utils.reflection import all_subclasses from authentik.lib.utils.reflection import all_subclasses
from authentik.managed.api import ManagedSerializer
from authentik.policies.api.exec import PolicyTestSerializer from authentik.policies.api.exec import PolicyTestSerializer

View File

@ -15,13 +15,13 @@ from rest_framework.viewsets import ModelViewSet
from authentik.api.authorization import OwnerSuperuserPermissions from authentik.api.authorization import OwnerSuperuserPermissions
from authentik.api.decorators import permission_required from authentik.api.decorators import permission_required
from authentik.blueprints.api import ManagedSerializer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserSerializer from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import PassiveSerializer from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.events.utils import model_to_dict from authentik.events.utils import model_to_dict
from authentik.managed.api import ManagedSerializer
class TokenSerializer(ManagedSerializer, ModelSerializer): class TokenSerializer(ManagedSerializer, ModelSerializer):

View File

@ -1,6 +1,6 @@
"""Core managed objects""" """Core managed objects"""
from authentik.blueprints.manager import EnsureExists, ObjectManager
from authentik.core.models import Source from authentik.core.models import Source
from authentik.managed.manager import EnsureExists, ObjectManager
class CoreManager(ObjectManager): class CoreManager(ObjectManager):

View File

@ -20,9 +20,10 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from guardian.mixins import GuardianUserMixin from guardian.mixins import GuardianUserMixin
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from rest_framework.serializers import Serializer from rest_framework.serializers import BaseSerializer, Serializer
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.blueprints.models import ManagedModel
from authentik.core.exceptions import PropertyMappingExpressionException from authentik.core.exceptions import PropertyMappingExpressionException
from authentik.core.signals import password_changed from authentik.core.signals import password_changed
from authentik.core.types import UILoginButton, UserSettingSerializer from authentik.core.types import UILoginButton, UserSettingSerializer
@ -30,7 +31,6 @@ from authentik.lib.config import CONFIG, get_path_from_dict
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.models import CreatedUpdatedModel, DomainlessURLValidator, SerializerModel from authentik.lib.models import CreatedUpdatedModel, DomainlessURLValidator, SerializerModel
from authentik.lib.utils.http import get_client_ip from authentik.lib.utils.http import get_client_ip
from authentik.managed.models import ManagedModel
from authentik.policies.models import PolicyBindingModel from authentik.policies.models import PolicyBindingModel
LOGGER = get_logger() LOGGER = get_logger()
@ -68,7 +68,7 @@ def default_token_key():
return generate_id(int(CONFIG.y("default_token_length"))) return generate_id(int(CONFIG.y("default_token_length")))
class Group(models.Model): class Group(SerializerModel):
"""Custom Group model which supports a basic hierarchy""" """Custom Group model which supports a basic hierarchy"""
group_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) group_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -87,6 +87,12 @@ class Group(models.Model):
) )
attributes = models.JSONField(default=dict, blank=True) attributes = models.JSONField(default=dict, blank=True)
@property
def serializer(self) -> Serializer:
from authentik.core.api.groups import GroupSerializer
return GroupSerializer
@property @property
def num_pk(self) -> int: def num_pk(self) -> int:
"""Get a numerical, int32 ID for the group""" """Get a numerical, int32 ID for the group"""
@ -139,7 +145,7 @@ class UserManager(DjangoUserManager):
return self._create_user(username, email, password, **extra_fields) return self._create_user(username, email, password, **extra_fields)
class User(GuardianUserMixin, AbstractUser): class User(SerializerModel, GuardianUserMixin, AbstractUser):
"""Custom User model to allow easier adding of user-based settings""" """Custom User model to allow easier adding of user-based settings"""
uuid = models.UUIDField(default=uuid4, editable=False) uuid = models.UUIDField(default=uuid4, editable=False)
@ -170,6 +176,12 @@ class User(GuardianUserMixin, AbstractUser):
always_merger.merge(final_attributes, self.attributes) always_merger.merge(final_attributes, self.attributes)
return final_attributes return final_attributes
@property
def serializer(self) -> Serializer:
from authentik.core.api.users import UserSerializer
return UserSerializer
@cached_property @cached_property
def is_superuser(self) -> bool: def is_superuser(self) -> bool:
"""Get supseruser status based on membership in a group with superuser status""" """Get supseruser status based on membership in a group with superuser status"""
@ -276,7 +288,7 @@ class Provider(SerializerModel):
return self.name return self.name
class Application(PolicyBindingModel): class Application(SerializerModel, PolicyBindingModel):
"""Every Application which uses authentik for authentication/identification/authorization """Every Application which uses authentik for authentication/identification/authorization
needs an Application record. Other authentication types can subclass this Model to needs an Application record. Other authentication types can subclass this Model to
add custom fields and other properties""" add custom fields and other properties"""
@ -307,6 +319,12 @@ class Application(PolicyBindingModel):
meta_description = models.TextField(default="", blank=True) meta_description = models.TextField(default="", blank=True)
meta_publisher = models.TextField(default="", blank=True) meta_publisher = models.TextField(default="", blank=True)
@property
def serializer(self) -> Serializer:
from authentik.core.api.applications import ApplicationSerializer
return ApplicationSerializer
@property @property
def get_meta_icon(self) -> Optional[str]: def get_meta_icon(self) -> Optional[str]:
"""Get the URL to the App Icon image. If the name is /static or starts with http """Get the URL to the App Icon image. If the name is /static or starts with http
@ -454,7 +472,7 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
return self.name return self.name
class UserSourceConnection(CreatedUpdatedModel): class UserSourceConnection(SerializerModel, CreatedUpdatedModel):
"""Connection between User and Source.""" """Connection between User and Source."""
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
@ -462,6 +480,11 @@ class UserSourceConnection(CreatedUpdatedModel):
objects = InheritanceManager() objects = InheritanceManager()
@property
def serializer(self) -> BaseSerializer:
"""Get serializer for this model"""
raise NotImplementedError
class Meta: class Meta:
unique_together = (("user", "source"),) unique_together = (("user", "source"),)
@ -516,7 +539,7 @@ class TokenIntents(models.TextChoices):
INTENT_APP_PASSWORD = "app_password" # nosec INTENT_APP_PASSWORD = "app_password" # nosec
class Token(ManagedModel, ExpiringModel): class Token(SerializerModel, ManagedModel, ExpiringModel):
"""Token used to authenticate the User for API Access or confirm another Stage like Email.""" """Token used to authenticate the User for API Access or confirm another Stage like Email."""
token_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) token_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -528,6 +551,12 @@ class Token(ManagedModel, ExpiringModel):
user = models.ForeignKey("User", on_delete=models.CASCADE, related_name="+") user = models.ForeignKey("User", on_delete=models.CASCADE, related_name="+")
description = models.TextField(default="", blank=True) description = models.TextField(default="", blank=True)
@property
def serializer(self) -> Serializer:
from authentik.core.api.tokens import TokenSerializer
return TokenSerializer
def expire_action(self, *args, **kwargs): def expire_action(self, *args, **kwargs):
"""Handler which is called when this object is expired.""" """Handler which is called when this object is expired."""
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction

View File

@ -2,9 +2,9 @@
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from authentik.blueprints.manager import ObjectManager
from authentik.crypto.builder import CertificateBuilder from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.managed.manager import ObjectManager
MANAGED_KEY = "goauthentik.io/crypto/jwt-managed" MANAGED_KEY = "goauthentik.io/crypto/jwt-managed"

View File

@ -16,15 +16,16 @@ from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import Certificate, load_pem_x509_certificate from cryptography.x509 import Certificate, load_pem_x509_certificate
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.lib.models import CreatedUpdatedModel from authentik.blueprints.models import ManagedModel
from authentik.managed.models import ManagedModel from authentik.lib.models import CreatedUpdatedModel, SerializerModel
LOGGER = get_logger() LOGGER = get_logger()
class CertificateKeyPair(ManagedModel, CreatedUpdatedModel): class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
"""CertificateKeyPair that can be used for signing or encrypting if `key_data` """CertificateKeyPair that can be used for signing or encrypting if `key_data`
is set, otherwise it can be used to verify remote data.""" is set, otherwise it can be used to verify remote data."""
@ -44,6 +45,12 @@ class CertificateKeyPair(ManagedModel, CreatedUpdatedModel):
_private_key: Optional[RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey] = None _private_key: Optional[RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey] = None
_public_key: Optional[RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey] = None _public_key: Optional[RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey] = None
@property
def serializer(self) -> Serializer:
from authentik.crypto.api import CertificateKeyPairSerializer
return CertificateKeyPairSerializer
@property @property
def certificate(self) -> Certificate: def certificate(self) -> Certificate:
"""Get python cryptography Certificate instance""" """Get python cryptography Certificate instance"""

View File

@ -30,7 +30,7 @@ from authentik.core.middleware import (
from authentik.core.models import ExpiringModel, Group, PropertyMapping, User from authentik.core.models import ExpiringModel, Group, PropertyMapping, User
from authentik.events.geo import GEOIP_READER from authentik.events.geo import GEOIP_READER
from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict
from authentik.lib.models import DomainlessURLValidator from authentik.lib.models import DomainlessURLValidator, SerializerModel
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_client_ip, get_http_session from authentik.lib.utils.http import get_client_ip, get_http_session
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
@ -168,7 +168,7 @@ class EventManager(Manager):
return self.get_queryset().get_events_per_day() return self.get_queryset().get_events_per_day()
class Event(ExpiringModel): class Event(SerializerModel, ExpiringModel):
"""An individual Audit/Metrics/Notification/Error Event""" """An individual Audit/Metrics/Notification/Error Event"""
event_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) event_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -273,6 +273,12 @@ class Event(ExpiringModel):
) )
super().save(*args, **kwargs) super().save(*args, **kwargs)
@property
def serializer(self) -> "Serializer":
from authentik.events.api.events import EventSerializer
return EventSerializer
@property @property
def summary(self) -> str: def summary(self) -> str:
"""Return a summary of this event.""" """Return a summary of this event."""
@ -298,7 +304,7 @@ class TransportMode(models.TextChoices):
EMAIL = "email", _("Email") EMAIL = "email", _("Email")
class NotificationTransport(models.Model): class NotificationTransport(SerializerModel):
"""Action which is executed when a Rule matches""" """Action which is executed when a Rule matches"""
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -448,6 +454,12 @@ class NotificationTransport(models.Model):
except (SMTPException, ConnectionError, OSError) as exc: except (SMTPException, ConnectionError, OSError) as exc:
raise NotificationTransportError from exc raise NotificationTransportError from exc
@property
def serializer(self) -> "Serializer":
from authentik.events.api.notification_transports import NotificationTransportSerializer
return NotificationTransportSerializer
def __str__(self) -> str: def __str__(self) -> str:
return f"Notification Transport {self.name}" return f"Notification Transport {self.name}"
@ -465,7 +477,7 @@ class NotificationSeverity(models.TextChoices):
ALERT = "alert", _("Alert") ALERT = "alert", _("Alert")
class Notification(models.Model): class Notification(SerializerModel):
"""Event Notification""" """Event Notification"""
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -476,6 +488,12 @@ class Notification(models.Model):
seen = models.BooleanField(default=False) seen = models.BooleanField(default=False)
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
@property
def serializer(self) -> "Serializer":
from authentik.events.api.notifications import NotificationSerializer
return NotificationSerializer
def __str__(self) -> str: def __str__(self) -> str:
body_trunc = (self.body[:75] + "..") if len(self.body) > 75 else self.body body_trunc = (self.body[:75] + "..") if len(self.body) > 75 else self.body
return f"Notification for user {self.user}: {body_trunc}" return f"Notification for user {self.user}: {body_trunc}"
@ -486,7 +504,7 @@ class Notification(models.Model):
verbose_name_plural = _("Notifications") verbose_name_plural = _("Notifications")
class NotificationRule(PolicyBindingModel): class NotificationRule(SerializerModel, PolicyBindingModel):
"""Decide when to create a Notification based on policies attached to this object.""" """Decide when to create a Notification based on policies attached to this object."""
name = models.TextField(unique=True) name = models.TextField(unique=True)
@ -518,6 +536,12 @@ class NotificationRule(PolicyBindingModel):
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
@property
def serializer(self) -> "Serializer":
from authentik.events.api.notification_rules import NotificationRuleSerializer
return NotificationRuleSerializer
def __str__(self) -> str: def __str__(self) -> str:
return f"Notification Rule {self.name}" return f"Notification Rule {self.name}"

View File

@ -20,6 +20,8 @@ from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger 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 Exporter
from authentik.blueprints.v1.importer import Importer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ( from authentik.core.api.utils import (
CacheSerializer, CacheSerializer,
@ -30,8 +32,6 @@ from authentik.core.api.utils import (
from authentik.flows.exceptions import FlowNonApplicableException from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
from authentik.flows.transfer.exporter import FlowExporter
from authentik.flows.transfer.importer import FlowImporter
from authentik.flows.views.executor import SESSION_KEY_HISTORY, SESSION_KEY_PLAN from authentik.flows.views.executor import SESSION_KEY_HISTORY, SESSION_KEY_PLAN
from authentik.lib.views import bad_request_message from authentik.lib.views import bad_request_message
@ -163,11 +163,11 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
) )
@action(detail=False, methods=["POST"], parser_classes=(MultiPartParser,)) @action(detail=False, methods=["POST"], parser_classes=(MultiPartParser,))
def import_flow(self, request: Request) -> Response: def import_flow(self, request: Request) -> Response:
"""Import flow from .akflow file""" """Import flow from .yaml file"""
file = request.FILES.get("file", None) file = request.FILES.get("file", None)
if not file: if not file:
return HttpResponseBadRequest() return HttpResponseBadRequest()
importer = FlowImporter(file.read().decode()) importer = Importer(file.read().decode())
valid = importer.validate() valid = importer.validate()
if not valid: if not valid:
return HttpResponseBadRequest() return HttpResponseBadRequest()
@ -195,11 +195,11 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
@action(detail=True, pagination_class=None, filter_backends=[]) @action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=unused-argument # pylint: disable=unused-argument
def export(self, request: Request, slug: str) -> Response: def export(self, request: Request, slug: str) -> Response:
"""Export flow to .akflow file""" """Export flow to .yaml file"""
flow = self.get_object() flow = self.get_object()
exporter = FlowExporter(flow) exporter = Exporter(flow)
response = HttpResponse(content=exporter.export_to_string()) response = HttpResponse(content=exporter.export_to_string())
response["Content-Disposition"] = f'attachment; filename="{flow.slug}.akflow"' response["Content-Disposition"] = f'attachment; filename="{flow.slug}.yaml"'
return response return response
@extend_schema(responses={200: FlowDiagramSerializer()}) @extend_schema(responses={200: FlowDiagramSerializer()})

View File

@ -1,14 +1,16 @@
"""Challenge helpers""" """Challenge helpers"""
from dataclasses import asdict, is_dataclass
from enum import Enum from enum import Enum
from typing import TYPE_CHECKING, Optional, TypedDict from typing import TYPE_CHECKING, Optional, TypedDict
from uuid import UUID
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models from django.db import models
from django.http import JsonResponse from django.http import JsonResponse
from rest_framework.fields import ChoiceField, DictField from rest_framework.fields import ChoiceField, DictField
from rest_framework.serializers import CharField from rest_framework.serializers import CharField
from authentik.core.api.utils import PassiveSerializer from authentik.core.api.utils import PassiveSerializer
from authentik.flows.transfer.common import DataclassEncoder
if TYPE_CHECKING: if TYPE_CHECKING:
from authentik.flows.stage import StageView from authentik.flows.stage import StageView
@ -135,6 +137,19 @@ class AutoSubmitChallengeResponse(ChallengeResponse):
component = CharField(default="ak-stage-autosubmit") component = CharField(default="ak-stage-autosubmit")
class DataclassEncoder(DjangoJSONEncoder):
"""Convert any dataclass to json"""
def default(self, o):
if is_dataclass(o):
return asdict(o)
if isinstance(o, UUID):
return str(o)
if isinstance(o, Enum):
return o.value
return super().default(o) # pragma: no cover
class HttpChallengeResponse(JsonResponse): class HttpChallengeResponse(JsonResponse):
"""Subclass of JsonResponse that uses the `DataclassEncoder`""" """Subclass of JsonResponse that uses the `DataclassEncoder`"""

View File

@ -1,22 +0,0 @@
"""Apply flow from commandline"""
from django.core.management.base import BaseCommand, no_translations
from authentik.flows.transfer.importer import FlowImporter
class Command(BaseCommand): # pragma: no cover
"""Apply flow from commandline"""
@no_translations
def handle(self, *args, **options):
"""Apply all flows in order, abort when one fails to import"""
for flow_path in options.get("flows", []):
with open(flow_path, "r", encoding="utf8") as flow_file:
importer = FlowImporter(flow_file.read())
valid = importer.validate()
if not valid:
raise ValueError("Flow invalid")
importer.apply()
def add_arguments(self, parser):
parser.add_argument("flows", nargs="+", type=str)

View File

@ -1,105 +0,0 @@
"""transfer common classes"""
from dataclasses import asdict, dataclass, field, is_dataclass
from enum import Enum
from typing import Any
from uuid import UUID
from django.core.serializers.json import DjangoJSONEncoder
from yaml import SafeDumper
from authentik.lib.models import SerializerModel
from authentik.lib.sentry import SentryIgnoredException
def get_attrs(obj: SerializerModel) -> dict[str, Any]:
"""Get object's attributes via their serializer, and convert it to a normal dict"""
data = dict(obj.serializer(obj).data)
to_remove = (
"policies",
"stages",
"pk",
"background",
"group",
"user",
"verbose_name",
"verbose_name_plural",
"component",
"flow_set",
"promptstage_set",
"policybindingmodel_ptr_id",
"export_url",
"meta_model_name",
)
for to_remove_name in to_remove:
if to_remove_name in data:
data.pop(to_remove_name)
for key in list(data.keys()):
if key.endswith("_obj"):
data.pop(key)
return data
@dataclass
class FlowBundleEntry:
"""Single entry of a bundle"""
identifiers: dict[str, Any]
model: str
attrs: dict[str, Any]
@staticmethod
def from_model(model: SerializerModel, *extra_identifier_names: str) -> "FlowBundleEntry":
"""Convert a SerializerModel instance to a Bundle Entry"""
identifiers = {
"pk": model.pk,
}
all_attrs = get_attrs(model)
for extra_identifier_name in extra_identifier_names:
identifiers[extra_identifier_name] = all_attrs.pop(extra_identifier_name)
return FlowBundleEntry(
identifiers=identifiers,
model=f"{model._meta.app_label}.{model._meta.model_name}",
attrs=all_attrs,
)
@dataclass
class FlowBundle:
"""Dataclass used for a full export"""
version: int = field(default=1)
entries: list[FlowBundleEntry] = field(default_factory=list)
class DataclassEncoder(DjangoJSONEncoder):
"""Convert FlowBundleEntry to json"""
def default(self, o):
if is_dataclass(o):
return asdict(o)
if isinstance(o, UUID):
return str(o)
if isinstance(o, Enum):
return o.value
return super().default(o) # pragma: no cover
class DataclassDumper(SafeDumper):
"""Dump dataclasses to yaml"""
default_flow_style = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_representer(UUID, lambda self, data: self.represent_str(str(data)))
self.add_representer(Enum, lambda self, data: self.represent_str(data.value))
def represent(self, data) -> None:
if is_dataclass(data):
data = asdict(data)
return super().represent(data)
class EntryInvalidError(SentryIgnoredException):
"""Error raised when an entry is invalid"""

View File

@ -62,6 +62,7 @@ ldap:
tls: tls:
ciphers: null ciphers: null
config_file_dir: "/config"
cookie_domain: null cookie_domain: null
disable_update_check: false disable_update_check: false
disable_startup_analytics: false disable_startup_analytics: false

View File

@ -1,8 +0,0 @@
"""authentik lib template utilities"""
from django.template import Context, loader
def render_to_string(template_path: str, ctx: Context) -> str:
"""Render a template to string"""
template = loader.get_template(template_path)
return template.render(ctx)

View File

@ -1,8 +0,0 @@
"""Serializer mixin for managed models"""
from rest_framework.fields import CharField
class ManagedSerializer:
"""Managed Serializer"""
managed = CharField(read_only=True, allow_null=True)

View File

@ -1,15 +0,0 @@
"""authentik Managed app"""
from django.apps import AppConfig
class AuthentikManagedConfig(AppConfig):
"""authentik Managed app"""
name = "authentik.managed"
label = "authentik_managed"
verbose_name = "authentik Managed"
def ready(self) -> None:
from authentik.managed.tasks import managed_reconcile
managed_reconcile.delay()

View File

@ -1,26 +0,0 @@
"""Managed Object models"""
from django.db import models
from django.utils.translation import gettext_lazy as _
class ManagedModel(models.Model):
"""Model which can be managed by authentik exclusively"""
managed = models.TextField(
default=None,
null=True,
verbose_name=_("Managed by authentik"),
help_text=_(
(
"Objects which are managed by authentik. These objects are created and updated "
"automatically. This is flag only indicates that an object can be overwritten by "
"migrations. You can still modify the objects via the API, but expect changes "
"to be overwritten in a later update."
)
),
unique=True,
)
class Meta:
abstract = True

View File

@ -1,12 +0,0 @@
"""managed Settings"""
from celery.schedules import crontab
from authentik.lib.utils.time import fqdn_rand
CELERY_BEAT_SCHEDULE = {
"managed_reconcile": {
"task": "authentik.managed.tasks.managed_reconcile",
"schedule": crontab(minute=fqdn_rand("managed_reconcile"), hour="*/4"),
"options": {"queue": "authentik_scheduled"},
},
}

View File

@ -1,5 +1,5 @@
"""Outpost managed objects""" """Outpost managed objects"""
from authentik.managed.manager import EnsureExists, ObjectManager from authentik.blueprints.manager import EnsureExists, ObjectManager
from authentik.outposts.models import ( from authentik.outposts.models import (
DockerServiceConnection, DockerServiceConnection,
KubernetesServiceConnection, KubernetesServiceConnection,

View File

@ -14,9 +14,11 @@ from guardian.models import UserObjectPermission
from guardian.shortcuts import assign_perm from guardian.shortcuts import assign_perm
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from packaging.version import LegacyVersion, Version, parse from packaging.version import LegacyVersion, Version, parse
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik import __version__, get_build_hash from authentik import __version__, get_build_hash
from authentik.blueprints.models import ManagedModel
from authentik.core.models import ( from authentik.core.models import (
USER_ATTRIBUTE_CAN_OVERRIDE_IP, USER_ATTRIBUTE_CAN_OVERRIDE_IP,
USER_ATTRIBUTE_SA, USER_ATTRIBUTE_SA,
@ -29,10 +31,9 @@ from authentik.core.models import (
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey from authentik.lib.models import InheritanceForeignKey, SerializerModel
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.errors import exception_to_string from authentik.lib.utils.errors import exception_to_string
from authentik.managed.models import ManagedModel
from authentik.outposts.controllers.k8s.utils import get_namespace from authentik.outposts.controllers.k8s.utils import get_namespace
from authentik.tenants.models import Tenant from authentik.tenants.models import Tenant
@ -155,7 +156,7 @@ class OutpostServiceConnection(models.Model):
verbose_name_plural = _("Outpost Service-Connections") verbose_name_plural = _("Outpost Service-Connections")
class DockerServiceConnection(OutpostServiceConnection): class DockerServiceConnection(SerializerModel, OutpostServiceConnection):
"""Service Connection to a Docker endpoint""" """Service Connection to a Docker endpoint"""
url = models.TextField( url = models.TextField(
@ -192,6 +193,12 @@ class DockerServiceConnection(OutpostServiceConnection):
), ),
) )
@property
def serializer(self) -> Serializer:
from authentik.outposts.api.service_connections import DockerServiceConnectionSerializer
return DockerServiceConnectionSerializer
@property @property
def component(self) -> str: def component(self) -> str:
return "ak-service-connection-docker-form" return "ak-service-connection-docker-form"
@ -205,7 +212,7 @@ class DockerServiceConnection(OutpostServiceConnection):
verbose_name_plural = _("Docker Service-Connections") verbose_name_plural = _("Docker Service-Connections")
class KubernetesServiceConnection(OutpostServiceConnection): class KubernetesServiceConnection(SerializerModel, OutpostServiceConnection):
"""Service Connection to a Kubernetes cluster""" """Service Connection to a Kubernetes cluster"""
kubeconfig = models.JSONField( kubeconfig = models.JSONField(
@ -218,6 +225,12 @@ class KubernetesServiceConnection(OutpostServiceConnection):
blank=True, blank=True,
) )
@property
def serializer(self) -> Serializer:
from authentik.outposts.api.service_connections import KubernetesServiceConnectionSerializer
return KubernetesServiceConnectionSerializer
@property @property
def component(self) -> str: def component(self) -> str:
return "ak-service-connection-kubernetes-form" return "ak-service-connection-kubernetes-form"
@ -231,7 +244,7 @@ class KubernetesServiceConnection(OutpostServiceConnection):
verbose_name_plural = _("Kubernetes Service-Connections") verbose_name_plural = _("Kubernetes Service-Connections")
class Outpost(ManagedModel): class Outpost(SerializerModel, ManagedModel):
"""Outpost instance which manages a service user and token""" """Outpost instance which manages a service user and token"""
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True) uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
@ -256,6 +269,12 @@ class Outpost(ManagedModel):
providers = models.ManyToManyField(Provider) providers = models.ManyToManyField(Provider)
@property
def serializer(self) -> Serializer:
from authentik.outposts.api.outposts import OutpostSerializer
return OutpostSerializer
@property @property
def config(self) -> OutpostConfig: def config(self) -> OutpostConfig:
"""Load config as OutpostConfig object""" """Load config as OutpostConfig object"""

View File

@ -2,7 +2,7 @@
from django.test import TestCase from django.test import TestCase
from docker.models.containers import Container from docker.models.containers import Container
from authentik.managed.manager import ObjectManager from authentik.blueprints.manager import ObjectManager
from authentik.outposts.controllers.base import ControllerException from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.controllers.docker import DockerController from authentik.outposts.controllers.docker import DockerController
from authentik.outposts.managed import MANAGED_OUTPOST from authentik.outposts.managed import MANAGED_OUTPOST

View File

@ -158,7 +158,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.tenants", "authentik Tenants"), ("authentik.tenants", "authentik Tenants"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
], ],
default="", default="",
help_text="Match events created by selected application. When left empty, all applications are matched.", help_text="Match events created by selected application. When left empty, all applications are matched.",

View File

@ -69,7 +69,7 @@ class Migration(migrations.Migration):
"authentik Stages.OTP.Validate", "authentik Stages.OTP.Validate",
), ),
("authentik.stages.password", "authentik Stages.Password"), ("authentik.stages.password", "authentik Stages.Password"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
], ],
default="", default="",

View File

@ -73,7 +73,7 @@ class Migration(migrations.Migration):
"authentik.stages.authenticator_webauthn", "authentik.stages.authenticator_webauthn",
"authentik Stages.WebAuthn", "authentik Stages.WebAuthn",
), ),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
], ],
default="", default="",

View File

@ -76,7 +76,7 @@ class Migration(migrations.Migration):
"authentik Stages.Authenticator.WebAuthn", "authentik Stages.Authenticator.WebAuthn",
), ),
("authentik.stages.password", "authentik Stages.Password"), ("authentik.stages.password", "authentik Stages.Password"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
], ],
default="", default="",

View File

@ -76,7 +76,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_login", "authentik Stages.User Login"), ("authentik.stages.user_login", "authentik Stages.User Login"),
("authentik.stages.user_logout", "authentik Stages.User Logout"), ("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
], ],
default="", default="",

View File

@ -77,7 +77,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_login", "authentik Stages.User Login"), ("authentik.stages.user_login", "authentik Stages.User Login"),
("authentik.stages.user_logout", "authentik Stages.User Logout"), ("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
], ],
default="", default="",

View File

@ -74,7 +74,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_logout", "authentik Stages.User Logout"), ("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
], ],
default="", default="",
help_text="Match events created by selected application. When left empty, all applications are matched.", help_text="Match events created by selected application. When left empty, all applications are matched.",

View File

@ -75,7 +75,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_logout", "authentik Stages.User Logout"), ("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
], ],
default="", default="",
help_text="Match events created by selected application. When left empty, all applications are matched.", help_text="Match events created by selected application. When left empty, all applications are matched.",

View File

@ -76,7 +76,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_logout", "authentik Stages.User Logout"), ("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
], ],
default="", default="",
help_text="Match events created by selected application. When left empty, all applications are matched.", help_text="Match events created by selected application. When left empty, all applications are matched.",

View File

@ -81,7 +81,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.tenants", "authentik Tenants"), ("authentik.tenants", "authentik Tenants"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
], ],
default="", default="",
help_text="Match events created by selected application. When left empty, all applications are matched.", help_text="Match events created by selected application. When left empty, all applications are matched.",

View File

@ -69,7 +69,7 @@ class Migration(migrations.Migration):
("authentik.stages.user_logout", "authentik Stages.User Logout"), ("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"), ("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.tenants", "authentik Tenants"), ("authentik.tenants", "authentik Tenants"),
("authentik.managed", "authentik Managed"), ("authentik.blueprints", "authentik Blueprints"),
("authentik.core", "authentik Core"), ("authentik.core", "authentik Core"),
], ],
default="", default="",

View File

@ -1,5 +1,5 @@
"""OAuth2 Provider managed objects""" """OAuth2 Provider managed objects"""
from authentik.managed.manager import EnsureExists, ObjectManager from authentik.blueprints.manager import EnsureExists, ObjectManager
from authentik.providers.oauth2.models import ScopeMapping from authentik.providers.oauth2.models import ScopeMapping
SCOPE_OPENID_EXPRESSION = """ SCOPE_OPENID_EXPRESSION = """

View File

@ -24,6 +24,7 @@ from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.events.utils import get_user from authentik.events.utils import get_user
from authentik.lib.generators import generate_id, generate_key from authentik.lib.generators import generate_id, generate_key
from authentik.lib.models import SerializerModel
from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator
from authentik.providers.oauth2.apps import AuthentikProviderOAuth2Config from authentik.providers.oauth2.apps import AuthentikProviderOAuth2Config
from authentik.providers.oauth2.constants import ACR_AUTHENTIK_DEFAULT from authentik.providers.oauth2.constants import ACR_AUTHENTIK_DEFAULT
@ -335,7 +336,7 @@ class BaseGrantModel(models.Model):
abstract = True abstract = True
class AuthorizationCode(ExpiringModel, BaseGrantModel): class AuthorizationCode(SerializerModel, ExpiringModel, BaseGrantModel):
"""OAuth2 Authorization Code""" """OAuth2 Authorization Code"""
code = models.CharField(max_length=255, unique=True, verbose_name=_("Code")) code = models.CharField(max_length=255, unique=True, verbose_name=_("Code"))
@ -346,6 +347,12 @@ class AuthorizationCode(ExpiringModel, BaseGrantModel):
max_length=255, null=True, verbose_name=_("Code Challenge Method") max_length=255, null=True, verbose_name=_("Code Challenge Method")
) )
@property
def serializer(self) -> Serializer:
from authentik.providers.oauth2.api.tokens import ExpiringBaseGrantModelSerializer
return ExpiringBaseGrantModelSerializer
@property @property
def c_hash(self): def c_hash(self):
"""https://openid.net/specs/openid-connect-core-1_0.html#IDToken""" """https://openid.net/specs/openid-connect-core-1_0.html#IDToken"""
@ -398,13 +405,19 @@ class IDToken:
return dic return dic
class RefreshToken(ExpiringModel, BaseGrantModel): class RefreshToken(SerializerModel, ExpiringModel, BaseGrantModel):
"""OAuth2 Refresh Token""" """OAuth2 Refresh Token"""
access_token = models.TextField(verbose_name=_("Access Token")) access_token = models.TextField(verbose_name=_("Access Token"))
refresh_token = models.CharField(max_length=255, unique=True, verbose_name=_("Refresh Token")) refresh_token = models.CharField(max_length=255, unique=True, verbose_name=_("Refresh Token"))
_id_token = models.TextField(verbose_name=_("ID Token")) _id_token = models.TextField(verbose_name=_("ID Token"))
@property
def serializer(self) -> Serializer:
from authentik.providers.oauth2.api.tokens import ExpiringBaseGrantModelSerializer
return ExpiringBaseGrantModelSerializer
class Meta: class Meta:
verbose_name = _("OAuth2 Token") verbose_name = _("OAuth2 Token")
verbose_name_plural = _("OAuth2 Tokens") verbose_name_plural = _("OAuth2 Tokens")

View File

@ -5,10 +5,10 @@ from django.test import RequestFactory
from django.urls import reverse from django.urls import reverse
from jwt import decode from jwt import decode
from authentik.blueprints.manager import ObjectManager
from authentik.core.models import USER_ATTRIBUTE_SA, Application, Group, Token, TokenIntents from authentik.core.models import USER_ATTRIBUTE_SA, Application, Group, Token, TokenIntents
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.lib.generators import generate_id, generate_key from authentik.lib.generators import generate_id, generate_key
from authentik.managed.manager import ObjectManager
from authentik.policies.models import PolicyBinding from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.constants import ( from authentik.providers.oauth2.constants import (
GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_CLIENT_CREDENTIALS,

View File

@ -6,10 +6,10 @@ from django.test import RequestFactory
from django.urls import reverse from django.urls import reverse
from jwt import decode from jwt import decode
from authentik.blueprints.manager import ObjectManager
from authentik.core.models import Application, Group from authentik.core.models import Application, Group
from authentik.core.tests.utils import create_test_cert, create_test_flow from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.lib.generators import generate_id, generate_key from authentik.lib.generators import generate_id, generate_key
from authentik.managed.manager import ObjectManager
from authentik.policies.models import PolicyBinding from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.constants import ( from authentik.providers.oauth2.constants import (
GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_CLIENT_CREDENTIALS,

View File

@ -4,11 +4,11 @@ from dataclasses import asdict
from django.urls import reverse from django.urls import reverse
from authentik.blueprints.manager import ObjectManager
from authentik.core.models import Application from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id, generate_key from authentik.lib.generators import generate_id, generate_key
from authentik.managed.manager import ObjectManager
from authentik.providers.oauth2.models import IDToken, OAuth2Provider, RefreshToken, ScopeMapping from authentik.providers.oauth2.models import IDToken, OAuth2Provider, RefreshToken, ScopeMapping
from authentik.providers.oauth2.tests.utils import OAuthTestCase from authentik.providers.oauth2.tests.utils import OAuthTestCase

View File

@ -1,5 +1,5 @@
"""OAuth2 Provider managed objects""" """OAuth2 Provider managed objects"""
from authentik.managed.manager import EnsureExists, ObjectManager from authentik.blueprints.manager import EnsureExists, ObjectManager
from authentik.providers.oauth2.models import ScopeMapping from authentik.providers.oauth2.models import ScopeMapping
from authentik.providers.proxy.models import SCOPE_AK_PROXY from authentik.providers.proxy.models import SCOPE_AK_PROXY

View File

@ -1,5 +1,5 @@
"""SAML Provider managed objects""" """SAML Provider managed objects"""
from authentik.managed.manager import EnsureExists, ObjectManager from authentik.blueprints.manager import EnsureExists, ObjectManager
from authentik.providers.saml.models import SAMLPropertyMapping from authentik.providers.saml.models import SAMLPropertyMapping
GROUP_EXPRESSION = """ GROUP_EXPRESSION = """

View File

@ -4,11 +4,11 @@ from base64 import b64encode
from django.http.request import QueryDict from django.http.request import QueryDict
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from authentik.blueprints.manager import ObjectManager
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.tests.utils import get_request from authentik.lib.tests.utils import get_request
from authentik.managed.manager import ObjectManager
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.request_parser import AuthNRequestParser from authentik.providers.saml.processors.request_parser import AuthNRequestParser

View File

@ -4,10 +4,10 @@ from base64 import b64encode
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from lxml import etree # nosec from lxml import etree # nosec
from authentik.blueprints.manager import ObjectManager
from authentik.core.tests.utils import create_test_cert, create_test_flow from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.lib.tests.utils import get_request from authentik.lib.tests.utils import get_request
from authentik.lib.xml import lxml_from_string from authentik.lib.xml import lxml_from_string
from authentik.managed.manager import ObjectManager
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.request_parser import AuthNRequestParser from authentik.providers.saml.processors.request_parser import AuthNRequestParser

View File

@ -76,7 +76,7 @@ def task_error_hook(task_id, exception: Exception, traceback, *args, **kwargs):
def _get_startup_tasks() -> list[Callable]: def _get_startup_tasks() -> list[Callable]:
"""Get all tasks to be run on startup""" """Get all tasks to be run on startup"""
from authentik.admin.tasks import clear_update_notifications from authentik.admin.tasks import clear_update_notifications
from authentik.managed.tasks import managed_reconcile from authentik.blueprints.tasks import managed_reconcile
from authentik.outposts.tasks import outpost_controller_all, outpost_local_connection from authentik.outposts.tasks import outpost_controller_all, outpost_local_connection
from authentik.providers.proxy.tasks import proxy_set_defaults from authentik.providers.proxy.tasks import proxy_set_defaults

View File

@ -122,7 +122,7 @@ INSTALLED_APPS = [
"authentik.stages.user_logout", "authentik.stages.user_logout",
"authentik.stages.user_write", "authentik.stages.user_write",
"authentik.tenants", "authentik.tenants",
"authentik.managed", "authentik.blueprints",
"rest_framework", "rest_framework",
"django_filters", "django_filters",
"drf_spectacular", "drf_spectacular",

View File

@ -1,5 +1,5 @@
"""LDAP Source managed objects""" """LDAP Source managed objects"""
from authentik.managed.manager import EnsureExists, ObjectManager from authentik.blueprints.manager import EnsureExists, ObjectManager
from authentik.sources.ldap.models import LDAPPropertyMapping from authentik.sources.ldap.models import LDAPPropertyMapping

View File

@ -4,9 +4,9 @@ from unittest.mock import Mock, PropertyMock, patch
from django.db.models import Q from django.db.models import Q
from django.test import TestCase from django.test import TestCase
from authentik.blueprints.manager import ObjectManager
from authentik.core.models import User from authentik.core.models import User
from authentik.lib.generators import generate_key from authentik.lib.generators import generate_key
from authentik.managed.manager import ObjectManager
from authentik.sources.ldap.auth import LDAPBackend from authentik.sources.ldap.auth import LDAPBackend
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer from authentik.sources.ldap.sync.users import UserLDAPSynchronizer

View File

@ -4,11 +4,11 @@ from unittest.mock import PropertyMock, patch
from django.db.models import Q from django.db.models import Q
from django.test import TestCase from django.test import TestCase
from authentik.blueprints.manager import ObjectManager
from authentik.core.models import Group, User from authentik.core.models import Group, User
from authentik.core.tests.utils import create_test_admin_user from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_key from authentik.lib.generators import generate_key
from authentik.managed.manager import ObjectManager
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer

View File

@ -211,6 +211,14 @@ class UserOAuthSourceConnection(UserSourceConnection):
identifier = models.CharField(max_length=255) identifier = models.CharField(max_length=255)
access_token = models.TextField(blank=True, null=True, default=None) access_token = models.TextField(blank=True, null=True, default=None)
@property
def serializer(self) -> Serializer:
from authentik.sources.oauth.api.source_connection import (
UserOAuthSourceConnectionSerializer,
)
return UserOAuthSourceConnectionSerializer
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.access_token = self.access_token or None self.access_token = self.access_token or None
super().save(*args, **kwargs) super().save(*args, **kwargs)

View File

@ -7,7 +7,7 @@ from django.http.request import HttpRequest
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.fields import CharField from rest_framework.fields import CharField
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer, Serializer
from authentik.core.models import Source, UserSourceConnection from authentik.core.models import Source, UserSourceConnection
from authentik.core.types import UILoginButton, UserSettingSerializer from authentik.core.types import UILoginButton, UserSettingSerializer
@ -99,6 +99,12 @@ class PlexSourceConnection(UserSourceConnection):
plex_token = models.TextField() plex_token = models.TextField()
identifier = models.TextField() identifier = models.TextField()
@property
def serializer(self) -> Serializer:
from authentik.sources.plex.api.source_connection import PlexSourceConnectionSerializer
return PlexSourceConnectionSerializer
class Meta: class Meta:
verbose_name = _("User Plex Source Connection") verbose_name = _("User Plex Source Connection")

View File

@ -7,11 +7,12 @@ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django_otp.models import Device from django_otp.models import Device
from duo_client.auth import Auth from duo_client.auth import Auth
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer, Serializer
from authentik import __version__ from authentik import __version__
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, Stage
from authentik.lib.models import SerializerModel
class AuthenticatorDuoStage(ConfigurableStage, Stage): class AuthenticatorDuoStage(ConfigurableStage, Stage):
@ -65,7 +66,7 @@ class AuthenticatorDuoStage(ConfigurableStage, Stage):
verbose_name_plural = _("Duo Authenticator Setup Stages") verbose_name_plural = _("Duo Authenticator Setup Stages")
class DuoDevice(Device): class DuoDevice(SerializerModel, Device):
"""Duo Device for a single user""" """Duo Device for a single user"""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
@ -73,9 +74,14 @@ class DuoDevice(Device):
# Connect to the stage to when validating access we know the API Credentials # Connect to the stage to when validating access we know the API Credentials
stage = models.ForeignKey(AuthenticatorDuoStage, on_delete=models.CASCADE) stage = models.ForeignKey(AuthenticatorDuoStage, on_delete=models.CASCADE)
duo_user_id = models.TextField() duo_user_id = models.TextField()
last_t = models.DateTimeField(auto_now=True) last_t = models.DateTimeField(auto_now=True)
@property
def serializer(self) -> Serializer:
from authentik.stages.authenticator_duo.api import DuoDeviceSerializer
return DuoDeviceSerializer
def __str__(self): def __str__(self):
return self.name or str(self.user) return self.name or str(self.user)

View File

@ -17,6 +17,7 @@ from twilio.rest import Client
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, Stage
from authentik.lib.models import SerializerModel
from authentik.lib.utils.errors import exception_to_string from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.http import get_http_session from authentik.lib.utils.http import get_http_session
@ -163,7 +164,7 @@ def hash_phone_number(phone_number: str) -> str:
return "hash:" + sha256(phone_number.encode()).hexdigest() return "hash:" + sha256(phone_number.encode()).hexdigest()
class SMSDevice(SideChannelDevice): class SMSDevice(SerializerModel, SideChannelDevice):
"""SMS Device""" """SMS Device"""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
@ -184,6 +185,12 @@ class SMSDevice(SideChannelDevice):
"""Check if the phone number is hashed""" """Check if the phone number is hashed"""
return self.phone_number.startswith("hash:") return self.phone_number.startswith("hash:")
@property
def serializer(self) -> BaseSerializer:
from authentik.stages.authenticator_sms.api import SMSDeviceSerializer
return SMSDeviceSerializer
def verify_token(self, token): def verify_token(self, token):
valid = super().verify_token(token) valid = super().verify_token(token)
if valid: if valid:

View File

@ -7,12 +7,13 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django_otp.models import Device from django_otp.models import Device
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer, Serializer
from webauthn.helpers.base64url_to_bytes import base64url_to_bytes from webauthn.helpers.base64url_to_bytes import base64url_to_bytes
from webauthn.helpers.structs import PublicKeyCredentialDescriptor from webauthn.helpers.structs import PublicKeyCredentialDescriptor
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, Stage
from authentik.lib.models import SerializerModel
class UserVerification(models.TextChoices): class UserVerification(models.TextChoices):
@ -113,7 +114,7 @@ class AuthenticateWebAuthnStage(ConfigurableStage, Stage):
verbose_name_plural = _("WebAuthn Authenticator Setup Stages") verbose_name_plural = _("WebAuthn Authenticator Setup Stages")
class WebAuthnDevice(Device): class WebAuthnDevice(SerializerModel, Device):
"""WebAuthn Device for a single user""" """WebAuthn Device for a single user"""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
@ -138,6 +139,12 @@ class WebAuthnDevice(Device):
self.last_t = now() self.last_t = now()
self.save() self.save()
@property
def serializer(self) -> Serializer:
from authentik.stages.authenticator_webauthn.api import WebAuthnDeviceSerializer
return WebAuthnDeviceSerializer
def __str__(self): def __str__(self):
return self.name or str(self.user) return self.name or str(self.user)

View File

@ -3,10 +3,11 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer, Serializer
from authentik.core.models import Application, ExpiringModel, User from authentik.core.models import Application, ExpiringModel, User
from authentik.flows.models import Stage from authentik.flows.models import Stage
from authentik.lib.models import SerializerModel
from authentik.lib.utils.time import timedelta_string_validator from authentik.lib.utils.time import timedelta_string_validator
@ -51,13 +52,19 @@ class ConsentStage(Stage):
verbose_name_plural = _("Consent Stages") verbose_name_plural = _("Consent Stages")
class UserConsent(ExpiringModel): class UserConsent(SerializerModel, ExpiringModel):
"""Consent given by a user for an application""" """Consent given by a user for an application"""
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
application = models.ForeignKey(Application, on_delete=models.CASCADE) application = models.ForeignKey(Application, on_delete=models.CASCADE)
permissions = models.TextField(default="") permissions = models.TextField(default="")
@property
def serializer(self) -> Serializer:
from authentik.stages.consent.api import UserConsentSerializer
return UserConsentSerializer
def __str__(self): def __str__(self):
return f"User Consent {self.application} by {self.user}" return f"User Consent {self.application} by {self.user}"

View File

@ -4,10 +4,11 @@ from uuid import uuid4
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer, Serializer
from authentik.core.models import ExpiringModel, User from authentik.core.models import ExpiringModel, User
from authentik.flows.models import Stage from authentik.flows.models import Stage
from authentik.lib.models import SerializerModel
class InvitationStage(Stage): class InvitationStage(Stage):
@ -47,7 +48,7 @@ class InvitationStage(Stage):
verbose_name_plural = _("Invitation Stages") verbose_name_plural = _("Invitation Stages")
class Invitation(ExpiringModel): class Invitation(SerializerModel, ExpiringModel):
"""Single-use invitation link""" """Single-use invitation link"""
invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -66,6 +67,12 @@ class Invitation(ExpiringModel):
help_text=_("Optional fixed data to enforce on user enrollment."), help_text=_("Optional fixed data to enforce on user enrollment."),
) )
@property
def serializer(self) -> Serializer:
from authentik.stages.consent.api import UserConsentSerializer
return UserConsentSerializer
def __str__(self): def __str__(self):
return f"Invitation {self.invite_uuid.hex} created by {self.created_by}" return f"Invitation {self.invite_uuid.hex} created by {self.created_by}"

View File

@ -1,40 +1,12 @@
# Generated by Django 3.2.3 on 2021-05-29 16:55 # Generated by Django 3.2.3 on 2021-05-29 16:55
from django.apps.registry import Apps
from django.db import migrations from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def create_default_tenant(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
Flow = apps.get_model("authentik_flows", "Flow")
Tenant = apps.get_model("authentik_tenants", "Tenant")
db_alias = schema_editor.connection.alias
default_authentication = (
Flow.objects.using(db_alias).filter(slug="default-authentication-flow").first()
)
default_invalidation = (
Flow.objects.using(db_alias).filter(slug="default-invalidation-flow").first()
)
tenant, _ = Tenant.objects.using(db_alias).update_or_create(
domain="authentik-default",
default=True,
defaults={
"flow_authentication": default_authentication,
"flow_invalidation": default_invalidation,
},
)
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("authentik_tenants", "0001_initial"), ("authentik_tenants", "0001_initial"),
("authentik_flows", "0008_default_flows"),
] ]
operations = [ operations = []
migrations.RunPython(create_default_tenant),
]

View File

@ -3,13 +3,15 @@ from uuid import uuid4
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.models import SerializerModel
from authentik.lib.utils.time import timedelta_string_validator from authentik.lib.utils.time import timedelta_string_validator
class Tenant(models.Model): class Tenant(SerializerModel):
"""Single tenant""" """Single tenant"""
tenant_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) tenant_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -62,9 +64,14 @@ class Tenant(models.Model):
on_delete=models.SET_DEFAULT, on_delete=models.SET_DEFAULT,
help_text=_(("Web Certificate used by the authentik Core webserver.")), help_text=_(("Web Certificate used by the authentik Core webserver.")),
) )
attributes = models.JSONField(default=dict, blank=True) attributes = models.JSONField(default=dict, blank=True)
@property
def serializer(self) -> Serializer:
from authentik.tenants.api import TenantSerializer
return TenantSerializer
@property @property
def default_locale(self) -> str: def default_locale(self) -> str:
"""Get default locale""" """Get default locale"""

View File

@ -0,0 +1,76 @@
entries:
- attrs:
compatibility_mode: false
designation: stage_configuration
layout: stacked
name: Change Password
policy_engine_mode: all
title: Change password
identifiers:
slug: default-password-change
model: authentik_flows.flow
id: flow
- attrs:
order: 300
placeholder: Password
placeholder_expression: false
required: true
sub_text: ''
type: password
identifiers:
field_key: password
label: Password
id: prompt-field-password
model: authentik_stages_prompt.prompt
- attrs:
order: 301
placeholder: Password (repeat)
placeholder_expression: false
required: true
sub_text: ''
type: password
identifiers:
field_key: password_repeat
label: Password (repeat)
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
- attrs:
fields:
- !KeyOf prompt-field-password
- !KeyOf prompt-field-password-repeat
meta_model_name: authentik_stages_prompt.promptstage
validation_policies: []
identifiers:
name: default-password-change-prompt
id: default-password-change-prompt
model: authentik_stages_prompt.promptstage
- attrs:
create_users_as_inactive: false
create_users_group: null
meta_model_name: authentik_stages_user_write.userwritestage
user_path_template: ''
identifiers:
name: default-password-change-write
id: default-password-change-write
model: authentik_stages_user_write.userwritestage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-password-change-prompt
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 1
stage: !KeyOf default-password-change-write
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,102 @@
entries:
- attrs:
cache_count: 1
compatibility_mode: false
designation: authentication
layout: stacked
name: Welcome to authentik!
policy_engine_mode: all
title: Welcome to authentik!
identifiers:
slug: default-authentication-flow
model: authentik_flows.flow
id: flow
- attrs:
backends:
- authentik.core.auth.InbuiltBackend
- authentik.sources.ldap.auth.LDAPBackend
- authentik.core.auth.TokenBackend
- authentik.core.auth.TokenBackend
configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]
failed_attempts_before_cancel: 5
meta_model_name: authentik_stages_password.passwordstage
identifiers:
name: default-authentication-password
id: default-authentication-password
model: authentik_stages_password.passwordstage
- attrs:
configuration_stages: []
device_classes:
- static
- totp
- webauthn
- duo
- sms
last_auth_threshold: seconds=0
meta_model_name: authentik_stages_authenticator_validate.authenticatorvalidatestage
not_configured_action: skip
identifiers:
name: default-authentication-mfa-validation
id: default-authentication-mfa-validation
model: authentik_stages_authenticator_validate.authenticatorvalidatestage
- attrs:
case_insensitive_matching: true
meta_model_name: authentik_stages_identification.identificationstage
show_matched_user: true
show_source_labels: false
sources: []
user_fields:
- email
- username
identifiers:
name: default-authentication-identification
id: default-authentication-identification
model: authentik_stages_identification.identificationstage
- attrs:
meta_model_name: authentik_stages_user_login.userloginstage
session_duration: seconds=0
identifiers:
name: default-authentication-login
id: default-authentication-login
model: authentik_stages_user_login.userloginstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 10
stage: !KeyOf default-authentication-identification
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 20
stage: !KeyOf default-authentication-password
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: any
re_evaluate_policies: false
identifiers:
order: 30
stage: !KeyOf default-authentication-mfa-validation
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 100
stage: !KeyOf default-authentication-login
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,30 @@
entries:
- attrs:
compatibility_mode: false
designation: invalidation
layout: stacked
name: Logout
policy_engine_mode: all
title: Default Invalidation Flow
identifiers:
pk: 46979d76-94d3-43b5-ad07-43e924c15d2c
slug: default-invalidation-flow
model: authentik_flows.flow
id: flow
- attrs:
meta_model_name: authentik_stages_user_logout.userlogoutstage
identifiers:
name: default-invalidation-logout
id: default-invalidation-logout
model: authentik_stages_user_logout.userlogoutstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-invalidation-logout
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,31 @@
entries:
- attrs:
compatibility_mode: false
designation: stage_configuration
layout: stacked
name: default-authenticator-static-setup
policy_engine_mode: any
title: Setup Static OTP Tokens
identifiers:
slug: default-authenticator-static-setup
model: authentik_flows.flow
id: flow
- attrs:
configure_flow: !KeyOf flow
meta_model_name: authentik_stages_authenticator_static.authenticatorstaticstage
token_count: 6
identifiers:
name: default-authenticator-static-setup
id: default-authenticator-static-setup
model: authentik_stages_authenticator_static.authenticatorstaticstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: any
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-authenticator-static-setup
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,31 @@
entries:
- attrs:
compatibility_mode: false
designation: stage_configuration
layout: stacked
name: default-authenticator-totp-setup
policy_engine_mode: any
title: Setup Two-Factor authentication
identifiers:
slug: default-authenticator-totp-setup
model: authentik_flows.flow
id: flow
- attrs:
configure_flow: !KeyOf flow
digits: 6
meta_model_name: authentik_stages_authenticator_totp.authenticatortotpstage
identifiers:
name: default-authenticator-totp-setup
id: default-authenticator-totp-setup
model: authentik_stages_authenticator_totp.authenticatortotpstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: any
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-authenticator-totp-setup
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,33 @@
entries:
- attrs:
compatibility_mode: false
designation: stage_configuration
layout: stacked
name: default-authenticator-webauthn-setup
policy_engine_mode: any
title: Setup WebAuthn
identifiers:
slug: default-authenticator-webauthn-setup
model: authentik_flows.flow
id: flow
- attrs:
authenticator_attachment: null
configure_flow: !KeyOf flow
meta_model_name: authentik_stages_authenticator_webauthn.authenticatewebauthnstage
resident_key_requirement: preferred
user_verification: preferred
identifiers:
name: default-authenticator-webauthn-setup
id: default-authenticator-webauthn-setup
model: authentik_stages_authenticator_webauthn.authenticatewebauthnstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: any
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-authenticator-webauthn-setup
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,31 @@
entries:
- attrs:
compatibility_mode: false
designation: authorization
layout: stacked
name: Authorize Application
policy_engine_mode: all
title: Redirecting to %(app)s
identifiers:
slug: default-provider-authorization-explicit-consent
model: authentik_flows.flow
id: flow
- attrs:
consent_expire_in: weeks=4
meta_model_name: authentik_stages_consent.consentstage
mode: always_require
identifiers:
name: default-provider-authorization-consent
id: default-provider-authorization-consent
model: authentik_stages_consent.consentstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-provider-authorization-consent
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,12 @@
entries:
- attrs:
compatibility_mode: false
designation: authorization
layout: stacked
name: Authorize Application
policy_engine_mode: all
title: Redirecting to %(app)s
identifiers:
slug: default-provider-authorization-implicit-consent
model: authentik_flows.flow
version: 1

View File

@ -0,0 +1,50 @@
entries:
- attrs:
compatibility_mode: false
designation: authentication
layout: stacked
name: Welcome to authentik!
policy_engine_mode: all
title: Welcome to authentik!
identifiers:
slug: default-source-authentication
model: authentik_flows.flow
id: flow
- attrs:
execution_logging: false
expression: |
# This policy ensures that this flow can only be used when the user
# is in a SSO Flow (meaning they come from an external IdP)
return ak_is_sso_flow
meta_model_name: authentik_policies_expression.expressionpolicy
identifiers:
name: default-source-authentication-if-sso
id: default-source-authentication-if-sso
model: authentik_policies_expression.expressionpolicy
- attrs:
meta_model_name: authentik_stages_user_login.userloginstage
session_duration: seconds=0
identifiers:
name: default-source-authentication-login
id: default-source-authentication-login
model: authentik_stages_user_login.userloginstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf default-source-authentication-login
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
enabled: true
negate: false
timeout: 30
identifiers:
order: 0
policy: !KeyOf default-source-authentication-if-sso
target: !KeyOf flow
model: authentik_policies.policybinding
version: 1

View File

@ -0,0 +1,121 @@
entries:
- attrs:
compatibility_mode: false
designation: enrollment
layout: stacked
name: Welcome to authentik! Please select a username.
policy_engine_mode: all
title: Welcome to authentik! Please select a username.
identifiers:
slug: default-source-enrollment
model: authentik_flows.flow
id: flow
- attrs:
order: 100
placeholder: Username
placeholder_expression: false
required: true
sub_text: ''
type: text
identifiers:
field_key: username
label: Username
id: prompt-field-username
model: authentik_stages_prompt.prompt
- attrs:
execution_logging: false
expression: |
# Check if we''ve not been given a username by the external IdP
# and trigger the enrollment flow
return ''username'' not in context.get(''prompt_data'', {})
meta_model_name: authentik_policies_expression.expressionpolicy
identifiers:
name: default-source-enrollment-if-username
id: default-source-enrollment-if-username
model: authentik_policies_expression.expressionpolicy
- attrs:
execution_logging: false
expression: |
# This policy ensures that this flow can only be used when the user
# is in a SSO Flow (meaning they come from an external IdP)
return ak_is_sso_flow
meta_model_name: authentik_policies_expression.expressionpolicy
identifiers:
name: default-source-enrollment-if-sso
id: default-source-enrollment-if-sso
model: authentik_policies_expression.expressionpolicy
- attrs:
meta_model_name: authentik_stages_user_login.userloginstage
session_duration: seconds=0
identifiers:
name: default-source-enrollment-login
id: default-source-enrollment-login
model: authentik_stages_user_login.userloginstage
- attrs:
fields:
- !KeyOf prompt-field-username
meta_model_name: authentik_stages_prompt.promptstage
validation_policies: []
identifiers:
name: default-source-enrollment-prompt
id: default-source-enrollment-prompt
model: authentik_stages_prompt.promptstage
- attrs:
create_users_as_inactive: false
create_users_group: null
meta_model_name: authentik_stages_user_write.userwritestage
user_path_template: ''
identifiers:
name: default-source-enrollment-write
id: default-source-enrollment-write
model: authentik_stages_user_write.userwritestage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: true
identifiers:
order: 0
stage: !KeyOf default-source-enrollment-prompt
target: !KeyOf flow
id: prompt-binding
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 1
stage: !KeyOf default-source-enrollment-write
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: all
re_evaluate_policies: false
identifiers:
order: 2
stage: !KeyOf default-source-enrollment-login
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
enabled: true
negate: false
timeout: 30
identifiers:
order: 0
policy: !KeyOf default-source-enrollment-if-sso
target: !KeyOf flow
model: authentik_policies.policybinding
- attrs:
enabled: true
negate: false
timeout: 30
identifiers:
order: 0
policy: !KeyOf default-source-enrollment-if-username
target: !KeyOf prompt-binding
model: authentik_policies.policybinding
version: 1

View File

@ -0,0 +1,12 @@
entries:
- attrs:
compatibility_mode: false
designation: stage_configuration
layout: stacked
name: Pre-Authentication
policy_engine_mode: any
title: ''
identifiers:
slug: default-source-pre-authentication
model: authentik_flows.flow
version: 1

View File

@ -0,0 +1,157 @@
entries:
- attrs:
compatibility_mode: false
designation: stage_configuration
layout: stacked
name: Update your info
policy_engine_mode: any
title: ''
identifiers:
slug: default-user-settings-flow
model: authentik_flows.flow
id: flow
- attrs:
order: 200
placeholder: |
try:
return user.username
except:
return ''
placeholder_expression: true
required: true
sub_text: ''
type: text
identifiers:
field_key: username
label: Username
id: prompt-field-username
model: authentik_stages_prompt.prompt
- attrs:
order: 201
placeholder: |
try:
return user.name
except:
return ''
placeholder_expression: true
required: true
sub_text: ''
type: text
identifiers:
field_key: name
label: Name
id: prompt-field-name
model: authentik_stages_prompt.prompt
- attrs:
order: 202
placeholder: |
try:
return user.email
except:
return ''
placeholder_expression: true
required: true
sub_text: ''
type: email
identifiers:
field_key: email
label: Email
id: prompt-field-email
model: authentik_stages_prompt.prompt
- attrs:
order: 203
placeholder: |
try:
return user.attributes.get("settings", {}).get("locale", "")
except:
return ''
placeholder_expression: true
required: true
sub_text: ''
type: ak-locale
identifiers:
field_key: attributes.settings.locale
label: Locale
id: prompt-field-locale
model: authentik_stages_prompt.prompt
- attrs:
execution_logging: false
expression: |
from authentik.lib.config import CONFIG
from authentik.core.models import (
USER_ATTRIBUTE_CHANGE_EMAIL,
USER_ATTRIBUTE_CHANGE_NAME,
USER_ATTRIBUTE_CHANGE_USERNAME
)
prompt_data = request.context.get("prompt_data")
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_EMAIL, CONFIG.y_bool("default_user_change_email", True)
):
if prompt_data.get("email") != request.user.email:
ak_message("Not allowed to change email address.")
return False
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_NAME, CONFIG.y_bool("default_user_change_name", True)
):
if prompt_data.get("name") != request.user.name:
ak_message("Not allowed to change name.")
return False
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_USERNAME, CONFIG.y_bool("default_user_change_username", True)
):
if prompt_data.get("username") != request.user.username:
ak_message("Not allowed to change username.")
return False
return True
meta_model_name: authentik_policies_expression.expressionpolicy
name: default-user-settings-authorization
identifiers:
name: default-user-settings-authorization
model: authentik_policies_expression.expressionpolicy
- attrs:
create_users_as_inactive: false
create_users_group: null
meta_model_name: authentik_stages_user_write.userwritestage
user_path_template: ''
identifiers:
name: default-user-settings-write
id: default-user-settings-write
model: authentik_stages_user_write.userwritestage
- attrs:
fields:
- !KeyOf prompt-field-username
- !KeyOf prompt-field-name
- !KeyOf prompt-field-email
- !KeyOf prompt-field-locale
meta_model_name: authentik_stages_prompt.promptstage
validation_policies:
- !KeyOf default-user-settings-authorization
identifiers:
name: default-user-settings
id: default-user-settings
model: authentik_stages_prompt.promptstage
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: any
re_evaluate_policies: false
identifiers:
order: 20
stage: !KeyOf default-user-settings
target: !KeyOf flow
model: authentik_flows.flowstagebinding
- attrs:
evaluate_on_plan: true
invalid_response_action: retry
policy_engine_mode: any
re_evaluate_policies: false
identifiers:
order: 100
stage: !KeyOf default-user-settings-write
target: !KeyOf flow
model: authentik_flows.flowstagebinding
version: 1

View File

@ -0,0 +1,9 @@
version: 1
entries:
- attrs:
flow_authentication: !Find [authentik_flows.flow, [slug, default-authentication-flow]]
flow_invalidation: !Find [authentik_flows.flow, [slug, default-invalidation-flow]]
identifiers:
domain: authentik-default
default: True
model: authentik_tenants.Tenant

View File

@ -1,119 +1,116 @@
version: 1 version: 1
entries: entries:
- identifiers: - identifiers:
pk: 773c6673-e4a2-423f-8d32-95b7b4a41cf3
slug: default-enrollment-flow slug: default-enrollment-flow
model: authentik_flows.flow model: authentik_flows.flow
id: flow
attrs: attrs:
name: Default enrollment Flow name: Default enrollment Flow
title: Welcome to authentik! title: Welcome to authentik!
designation: enrollment designation: enrollment
- identifiers: - identifiers:
pk: cb954fd4-65a5-4ad9-b1ee-180ee9559cf4
model: authentik_stages_prompt.prompt
attrs:
field_key: username field_key: username
label: Username label: Username
id: prompt-field-username
model: authentik_stages_prompt.prompt
attrs:
type: username type: username
required: true required: true
placeholder: Username placeholder: Username
order: 0 order: 0
- identifiers: - identifiers:
pk: 7db91ee8-4290-4e08-8d39-63f132402515
model: authentik_stages_prompt.prompt
attrs:
field_key: password field_key: password
label: Password label: Password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
type: password type: password
required: true required: true
placeholder: Password placeholder: Password
order: 0 order: 0
- identifiers: - identifiers:
pk: d30b5eb4-7787-4072-b1ba-65b46e928920
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat field_key: password_repeat
label: Password (repeat) label: Password (repeat)
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
type: password type: password
required: true required: true
placeholder: Password (repeat) placeholder: Password (repeat)
order: 1 order: 1
- identifiers: - identifiers:
pk: f78d977a-efa6-4cc2-9a0f-2621a9fd94d2
model: authentik_stages_prompt.prompt
attrs:
field_key: name field_key: name
label: Name label: Name
id: prompt-field-name
model: authentik_stages_prompt.prompt
attrs:
type: text type: text
required: true required: true
placeholder: Name placeholder: Name
order: 0 order: 0
- identifiers: - identifiers:
pk: 1ff91927-e33d-4615-95b0-c258e5f0df62
model: authentik_stages_prompt.prompt
attrs:
field_key: email field_key: email
label: Email label: Email
id: prompt-field-email
model: authentik_stages_prompt.prompt
attrs:
type: email type: email
required: true required: true
placeholder: Email placeholder: Email
order: 1 order: 1
- identifiers: - identifiers:
pk: 6c342b94-790d-425a-ae31-6196b6570722
name: default-enrollment-prompt-second name: default-enrollment-prompt-second
id: default-enrollment-prompt-second
model: authentik_stages_prompt.promptstage model: authentik_stages_prompt.promptstage
attrs: attrs:
fields: fields:
- f78d977a-efa6-4cc2-9a0f-2621a9fd94d2 - !KeyOf prompt-field-name
- 1ff91927-e33d-4615-95b0-c258e5f0df62 - !KeyOf prompt-field-email
- identifiers: - identifiers:
pk: 20375f30-7fa7-4562-8f6e-0f61889f2963
name: default-enrollment-prompt-first name: default-enrollment-prompt-first
id: default-enrollment-prompt-first
model: authentik_stages_prompt.promptstage model: authentik_stages_prompt.promptstage
attrs: attrs:
fields: fields:
- cb954fd4-65a5-4ad9-b1ee-180ee9559cf4 - !KeyOf prompt-field-username
- 7db91ee8-4290-4e08-8d39-63f132402515 - !KeyOf prompt-field-password
- d30b5eb4-7787-4072-b1ba-65b46e928920 - !KeyOf prompt-field-password-repeat
- identifiers: - identifiers:
pk: 77090897-eb3f-40db-81e6-b4074b1998c4 pk: !KeyOf default-enrollment-user-login
name: default-enrollment-user-login name: default-enrollment-user-login
id: default-enrollment-user-login
model: authentik_stages_user_login.userloginstage model: authentik_stages_user_login.userloginstage
attrs: attrs:
session_duration: seconds=0 session_duration: seconds=0
- identifiers: - identifiers:
pk: a4090add-f483-4ac6-8917-10b493ef843e
name: default-enrollment-user-write name: default-enrollment-user-write
id: default-enrollment-user-write
model: authentik_stages_user_write.userwritestage model: authentik_stages_user_write.userwritestage
attrs: {} attrs: {}
- identifiers: - identifiers:
pk: 34e1e7d5-8eed-4549-bc7a-305069ff7df0 target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-prompt-first
stage: 20375f30-7fa7-4562-8f6e-0f61889f2963
order: 10 order: 10
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: e40467a6-3052-488c-a1b5-1ad7a80fe7b3 target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-prompt-second
stage: 6c342b94-790d-425a-ae31-6196b6570722
order: 11 order: 11
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 76bc594e-2715-49ab-bd40-994abd9a7b70 target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-user-write
stage: a4090add-f483-4ac6-8917-10b493ef843e
order: 20 order: 20
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 2f324f6d-7646-4108-a6e2-e7f90985477f target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-user-login
stage: 77090897-eb3f-40db-81e6-b4074b1998c4
order: 100 order: 100
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:

View File

@ -1,66 +1,66 @@
version: 1 version: 1
entries: entries:
- identifiers: - identifiers:
pk: 773c6673-e4a2-423f-8d32-95b7b4a41cf3
slug: default-enrollment-flow slug: default-enrollment-flow
id: flow
model: authentik_flows.flow model: authentik_flows.flow
attrs: attrs:
name: Default enrollment Flow name: Default enrollment Flow
title: Welcome to authentik! title: Welcome to authentik!
designation: enrollment designation: enrollment
- identifiers: - identifiers:
pk: cb954fd4-65a5-4ad9-b1ee-180ee9559cf4
model: authentik_stages_prompt.prompt
attrs:
field_key: username field_key: username
label: Username label: Username
id: prompt-field-username
model: authentik_stages_prompt.prompt
attrs:
type: username type: username
required: true required: true
placeholder: Username placeholder: Username
order: 0 order: 0
- identifiers: - identifiers:
pk: 7db91ee8-4290-4e08-8d39-63f132402515
model: authentik_stages_prompt.prompt
attrs:
field_key: password field_key: password
label: Password label: Password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
type: password type: password
required: true required: true
placeholder: Password placeholder: Password
order: 0 order: 0
- identifiers: - identifiers:
pk: d30b5eb4-7787-4072-b1ba-65b46e928920
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat field_key: password_repeat
label: Password (repeat) label: Password (repeat)
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
type: password type: password
required: true required: true
placeholder: Password (repeat) placeholder: Password (repeat)
order: 1 order: 1
- identifiers: - identifiers:
pk: f78d977a-efa6-4cc2-9a0f-2621a9fd94d2
model: authentik_stages_prompt.prompt
attrs:
field_key: name field_key: name
label: Name label: Name
id: prompt-field-name
model: authentik_stages_prompt.prompt
attrs:
type: text type: text
required: true required: true
placeholder: Name placeholder: Name
order: 0 order: 0
- identifiers: - identifiers:
pk: 1ff91927-e33d-4615-95b0-c258e5f0df62
model: authentik_stages_prompt.prompt
attrs:
field_key: email field_key: email
label: Email label: Email
id: prompt-field-email
model: authentik_stages_prompt.prompt
attrs:
type: email type: email
required: true required: true
placeholder: Email placeholder: Email
order: 1 order: 1
- identifiers: - identifiers:
pk: 096e6282-6b30-4695-bd03-3b143eab5580
name: default-enrollment-email-verification name: default-enrollment-email-verification
id: default-enrollment-email-verification
model: authentik_stages_email.emailstage model: authentik_stages_email.emailstage
attrs: attrs:
use_global_settings: true use_global_settings: true
@ -76,70 +76,65 @@ entries:
template: email/account_confirmation.html template: email/account_confirmation.html
activate_user_on_success: true activate_user_on_success: true
- identifiers: - identifiers:
pk: 6c342b94-790d-425a-ae31-6196b6570722
name: default-enrollment-prompt-second name: default-enrollment-prompt-second
id: default-enrollment-prompt-second
model: authentik_stages_prompt.promptstage model: authentik_stages_prompt.promptstage
attrs: attrs:
fields: fields:
- f78d977a-efa6-4cc2-9a0f-2621a9fd94d2 - !KeyOf prompt-field-name
- 1ff91927-e33d-4615-95b0-c258e5f0df62 - !KeyOf prompt-field-email
- identifiers: - identifiers:
pk: 20375f30-7fa7-4562-8f6e-0f61889f2963
name: default-enrollment-prompt-first name: default-enrollment-prompt-first
id: default-enrollment-prompt-first
model: authentik_stages_prompt.promptstage model: authentik_stages_prompt.promptstage
attrs: attrs:
fields: fields:
- cb954fd4-65a5-4ad9-b1ee-180ee9559cf4 - !KeyOf prompt-field-username
- 7db91ee8-4290-4e08-8d39-63f132402515 - !KeyOf prompt-field-password
- d30b5eb4-7787-4072-b1ba-65b46e928920 - !KeyOf prompt-field-password-repeat
- identifiers: - identifiers:
pk: 77090897-eb3f-40db-81e6-b4074b1998c4
name: default-enrollment-user-login name: default-enrollment-user-login
id: default-enrollment-user-login
model: authentik_stages_user_login.userloginstage model: authentik_stages_user_login.userloginstage
attrs: attrs:
session_duration: seconds=0 session_duration: seconds=0
- identifiers: - identifiers:
pk: a4090add-f483-4ac6-8917-10b493ef843e
name: default-enrollment-user-write name: default-enrollment-user-write
id: default-enrollment-user-write
model: authentik_stages_user_write.userwritestage model: authentik_stages_user_write.userwritestage
attrs: attrs:
create_users_as_inactive: true create_users_as_inactive: true
- identifiers: - identifiers:
pk: 34e1e7d5-8eed-4549-bc7a-305069ff7df0 target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-prompt-first
stage: 20375f30-7fa7-4562-8f6e-0f61889f2963
order: 10 order: 10
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: e40467a6-3052-488c-a1b5-1ad7a80fe7b3 target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-prompt-second
stage: 6c342b94-790d-425a-ae31-6196b6570722
order: 11 order: 11
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 76bc594e-2715-49ab-bd40-994abd9a7b70 target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-user-write
stage: a4090add-f483-4ac6-8917-10b493ef843e
order: 20 order: 20
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 1db34a14-8985-4184-b5c9-254cd585d94f target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-email-verification
stage: 096e6282-6b30-4695-bd03-3b143eab5580
order: 30 order: 30
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 2f324f6d-7646-4108-a6e2-e7f90985477f target: !KeyOf flow
target: 773c6673-e4a2-423f-8d32-95b7b4a41cf3 stage: !KeyOf default-enrollment-user-login
stage: 77090897-eb3f-40db-81e6-b4074b1998c4
order: 40 order: 40
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:

View File

@ -2,29 +2,29 @@ version: 1
entries: entries:
- identifiers: - identifiers:
slug: default-authentication-flow slug: default-authentication-flow
pk: 563ece21-e9a4-47e5-a264-23ffd923e393
model: authentik_flows.flow model: authentik_flows.flow
id: flow
attrs: attrs:
name: Default Authentication Flow name: Default Authentication Flow
title: Welcome to authentik! title: Welcome to authentik!
designation: authentication designation: authentication
- identifiers: - identifiers:
pk: 7db93f1e-788b-4af6-8dc6-5cdeb59d8be7 name: test-not-app-password
id: test-not-app-password
model: authentik_policies_expression.expressionpolicy model: authentik_policies_expression.expressionpolicy
attrs: attrs:
name: test-not-app-password
execution_logging: false execution_logging: false
bound_to: 1 expression: |
expression: return context["auth_method"] != "app_password" return context["auth_method"] != "app_password"
- identifiers: - identifiers:
pk: 69d41125-3987-499b-8d74-ef27b54b88c8
name: default-authentication-login name: default-authentication-login
id: default-authentication-login
model: authentik_stages_user_login.userloginstage model: authentik_stages_user_login.userloginstage
attrs: attrs:
session_duration: seconds=0 session_duration: seconds=0
- identifiers: - identifiers:
pk: 5f594f27-0def-488d-9855-fe604eb13de5
name: default-authentication-identification name: default-authentication-identification
id: default-authentication-identification
model: authentik_stages_identification.identificationstage model: authentik_stages_identification.identificationstage
attrs: attrs:
user_fields: user_fields:
@ -34,13 +34,14 @@ entries:
enrollment_flow: null enrollment_flow: null
recovery_flow: null recovery_flow: null
- identifiers: - identifiers:
pk: 37f709c3-8817-45e8-9a93-80a925d293c2
name: default-authentication-flow-mfa name: default-authentication-flow-mfa
id: default-authentication-flow-mfa
model: authentik_stages_authenticator_validate.AuthenticatorValidateStage model: authentik_stages_authenticator_validate.AuthenticatorValidateStage
attrs: {} attrs: {}
- identifiers: - identifiers:
pk: d8affa62-500c-4c5c-a01f-5835e1ffdf40 pk: !KeyOf default-authentication-password
name: default-authentication-password name: default-authentication-password
id: default-authentication-password
model: authentik_stages_password.passwordstage model: authentik_stages_password.passwordstage
attrs: attrs:
backends: backends:
@ -48,44 +49,40 @@ entries:
- authentik.core.auth.TokenBackend - authentik.core.auth.TokenBackend
- authentik.sources.ldap.auth.LDAPBackend - authentik.sources.ldap.auth.LDAPBackend
- identifiers: - identifiers:
pk: a3056482-b692-4e3a-93f1-7351c6a351c7 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-identification
stage: 5f594f27-0def-488d-9855-fe604eb13de5
order: 10 order: 10
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 4e8538cf-3e18-4a68-82ae-6df6725fa2e6 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-password
stage: d8affa62-500c-4c5c-a01f-5835e1ffdf40
order: 20 order: 20
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 688aec6f-5622-42c6-83a5-d22072d7e798 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-flow-mfa
stage: 37f709c3-8817-45e8-9a93-80a925d293c2
order: 30 order: 30
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
id: flow-binding-mfa
attrs: attrs:
evaluate_on_plan: false evaluate_on_plan: false
re_evaluate_policies: true re_evaluate_policies: true
policy_engine_mode: any policy_engine_mode: any
invalid_response_action: retry invalid_response_action: retry
- identifiers: - identifiers:
pk: f3fede3a-a9b5-4232-9ec7-be7ff4194b27 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-login
stage: 69d41125-3987-499b-8d74-ef27b54b88c8
order: 100 order: 100
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 6e40ae4d-a4ed-4bd7-a784-27b1fe5859d2 policy: !KeyOf test-not-app-password
policy: 7db93f1e-788b-4af6-8dc6-5cdeb59d8be7 target: !KeyOf flow-binding-mfa
target: 688aec6f-5622-42c6-83a5-d22072d7e798
order: 0 order: 0
model: authentik_policies.policybinding model: authentik_policies.policybinding
attrs: attrs:

View File

@ -2,7 +2,7 @@ version: 1
entries: entries:
- identifiers: - identifiers:
slug: default-authentication-flow slug: default-authentication-flow
pk: 563ece21-e9a4-47e5-a264-23ffd923e393 id: flow
model: authentik_flows.flow model: authentik_flows.flow
attrs: attrs:
name: Default Authentication Flow name: Default Authentication Flow
@ -10,20 +10,20 @@ entries:
designation: authentication designation: authentication
- identifiers: - identifiers:
name: default-authentication-login name: default-authentication-login
pk: 69d41125-3987-499b-8d74-ef27b54b88c8 id: default-authentication-login
model: authentik_stages_user_login.userloginstage model: authentik_stages_user_login.userloginstage
attrs: attrs:
session_duration: seconds=0 session_duration: seconds=0
- identifiers: - identifiers:
name: default-authentication-flow-captcha name: default-authentication-flow-captcha
pk: a368cafc-1494-45e9-b75b-b5e7ac2bd3e4 id: default-authentication-flow-captcha
model: authentik_stages_captcha.captchastage model: authentik_stages_captcha.captchastage
attrs: attrs:
public_key: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI public_key: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
private_key: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe private_key: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
- identifiers: - identifiers:
name: default-authentication-identification name: default-authentication-identification
pk: 5f594f27-0def-488d-9855-fe604eb13de5 id: default-authentication-identification
model: authentik_stages_identification.identificationstage model: authentik_stages_identification.identificationstage
attrs: attrs:
user_fields: user_fields:
@ -34,7 +34,7 @@ entries:
recovery_flow: null recovery_flow: null
- identifiers: - identifiers:
name: default-authentication-password name: default-authentication-password
pk: d8affa62-500c-4c5c-a01f-5835e1ffdf40 id: default-authentication-password
model: authentik_stages_password.passwordstage model: authentik_stages_password.passwordstage
attrs: attrs:
backends: backends:
@ -42,50 +42,46 @@ entries:
- authentik.core.auth.TokenBackend - authentik.core.auth.TokenBackend
- authentik.sources.ldap.auth.LDAPBackend - authentik.sources.ldap.auth.LDAPBackend
- identifiers: - identifiers:
pk: a3056482-b692-4e3a-93f1-7351c6a351c7 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-identification
stage: 5f594f27-0def-488d-9855-fe604eb13de5
order: 10 order: 10
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 4e8538cf-3e18-4a68-82ae-6df6725fa2e6 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-password
stage: d8affa62-500c-4c5c-a01f-5835e1ffdf40
order: 20 order: 20
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 3bcd6af0-48a6-4e18-87f3-d251a1a58226 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-flow-captcha
stage: a368cafc-1494-45e9-b75b-b5e7ac2bd3e4
order: 30 order: 30
id: flow-binding-captcha
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
evaluate_on_plan: false evaluate_on_plan: false
re_evaluate_policies: true re_evaluate_policies: true
- identifiers: - identifiers:
pk: f3fede3a-a9b5-4232-9ec7-be7ff4194b27 target: !KeyOf flow
target: 563ece21-e9a4-47e5-a264-23ffd923e393 stage: !KeyOf default-authentication-login
stage: 69d41125-3987-499b-8d74-ef27b54b88c8
order: 100 order: 100
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
re_evaluate_policies: false re_evaluate_policies: false
- identifiers: - identifiers:
pk: 688c9890-47ad-4327-a9e5-380e88d34be5 name: default-authentication-flow-conditional-captcha
id: default-authentication-flow-conditional-captcha
model: authentik_policies_reputation.reputationpolicy model: authentik_policies_reputation.reputationpolicy
attrs: attrs:
name: default-authentication-flow-conditional-captcha
check_ip: true check_ip: true
check_username: true check_username: true
threshold: -5 threshold: -5
- identifiers: - identifiers:
pk: 02e4d220-3448-44db-822e-c5255cf7c250 policy: !KeyOf default-authentication-flow-conditional-captcha
policy: 688c9890-47ad-4327-a9e5-380e88d34be5 target: !KeyOf flow-binding-captcha
target: 3bcd6af0-48a6-4e18-87f3-d251a1a58226
order: 0 order: 0
model: authentik_policies.policybinding model: authentik_policies.policybinding
attrs: attrs:

View File

@ -1,8 +1,8 @@
version: 1 version: 1
entries: entries:
- identifiers: - identifiers:
pk: a5993183-89c0-43d2-a7f4-ddffb17baba7
slug: default-recovery-flow slug: default-recovery-flow
id: flow
model: authentik_flows.flow model: authentik_flows.flow
attrs: attrs:
name: Default recovery flow name: Default recovery flow
@ -13,11 +13,11 @@ entries:
compatibility_mode: false compatibility_mode: false
layout: stacked layout: stacked
- identifiers: - identifiers:
pk: 7db91ee8-4290-4e08-8d39-63f132402515
model: authentik_stages_prompt.prompt
attrs:
field_key: password field_key: password
label: Password label: Password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
type: password type: password
required: true required: true
placeholder: Password placeholder: Password
@ -25,11 +25,11 @@ entries:
sub_text: "" sub_text: ""
placeholder_expression: false placeholder_expression: false
- identifiers: - identifiers:
pk: d30b5eb4-7787-4072-b1ba-65b46e928920
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat field_key: password_repeat
label: Password (repeat) label: Password (repeat)
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
type: password type: password
required: true required: true
placeholder: Password (repeat) placeholder: Password (repeat)
@ -37,24 +37,16 @@ entries:
sub_text: "" sub_text: ""
placeholder_expression: false placeholder_expression: false
- identifiers: - identifiers:
pk: 1c5709ae-1b3e-413a-a117-260ab509bf5c name: default-recovery-skip-if-restored
id: default-recovery-skip-if-restored
model: authentik_policies_expression.expressionpolicy model: authentik_policies_expression.expressionpolicy
attrs: attrs:
name: default-recovery-skip-if-restored
execution_logging: false execution_logging: false
bound_to: 2 expression: |
expression: return request.context.get('is_restored', False) return request.context.get('is_restored', False)
- identifiers: - identifiers:
pk: 1c5709ae-1b3e-413a-a117-260ab509bf5c
model: authentik_policies_expression.expressionpolicy
attrs:
name: default-recovery-skip-if-restored
execution_logging: false
bound_to: 2
expression: return request.context.get('is_restored', False)
- identifiers:
pk: 4ac5719f-32c0-441c-8a7e-33c5ea0db7da
name: default-recovery-email name: default-recovery-email
id: default-recovery-email
model: authentik_stages_email.emailstage model: authentik_stages_email.emailstage
attrs: attrs:
use_global_settings: true use_global_settings: true
@ -70,16 +62,16 @@ entries:
template: email/password_reset.html template: email/password_reset.html
activate_user_on_success: true activate_user_on_success: true
- identifiers: - identifiers:
pk: 68b25ad5-318a-496e-95a7-cf4d94247f0d
name: default-recovery-user-write name: default-recovery-user-write
id: default-recovery-user-write
model: authentik_stages_user_write.userwritestage model: authentik_stages_user_write.userwritestage
attrs: attrs:
create_users_as_inactive: false create_users_as_inactive: false
create_users_group: null create_users_group: null
user_path_template: "" user_path_template: ""
- identifiers: - identifiers:
pk: 94843ef6-28fe-4939-bd61-cd46bb34f1de
name: default-recovery-identification name: default-recovery-identification
id: default-recovery-identification
model: authentik_stages_identification.identificationstage model: authentik_stages_identification.identificationstage
attrs: attrs:
user_fields: user_fields:
@ -94,37 +86,37 @@ entries:
sources: [] sources: []
show_source_labels: false show_source_labels: false
- identifiers: - identifiers:
pk: e74230b2-82bc-4843-8b18-2c3a66a62d57
name: default-recovery-user-login name: default-recovery-user-login
id: default-recovery-user-login
model: authentik_stages_user_login.userloginstage model: authentik_stages_user_login.userloginstage
attrs: attrs:
session_duration: seconds=0 session_duration: seconds=0
- identifiers: - identifiers:
pk: fa2d8d65-1809-4dcc-bdc0-56266e0f7971
name: Change your password name: Change your password
name: stages-prompt-password
model: authentik_stages_prompt.promptstage model: authentik_stages_prompt.promptstage
attrs: attrs:
fields: fields:
- 7db91ee8-4290-4e08-8d39-63f132402515 - !KeyOf prompt-field-password
- d30b5eb4-7787-4072-b1ba-65b46e928920 - !KeyOf prompt-field-password-repeat
validation_policies: [] validation_policies: []
- identifiers: - identifiers:
pk: 7af7558e-2196-4b9f-a08e-d38420b7cfbb target: !KeyOf flow
target: a5993183-89c0-43d2-a7f4-ddffb17baba7 stage: !KeyOf default-recovery-identification
stage: 94843ef6-28fe-4939-bd61-cd46bb34f1de
order: 10 order: 10
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
id: flow-binding-identification
attrs: attrs:
evaluate_on_plan: true evaluate_on_plan: true
re_evaluate_policies: true re_evaluate_policies: true
policy_engine_mode: any policy_engine_mode: any
invalid_response_action: retry invalid_response_action: retry
- identifiers: - identifiers:
pk: 29446fd6-dd93-4e92-9830-2d81debad5ae target: !KeyOf flow
target: a5993183-89c0-43d2-a7f4-ddffb17baba7 stage: !KeyOf default-recovery-email
stage: 4ac5719f-32c0-441c-8a7e-33c5ea0db7da
order: 20 order: 20
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
id: flow-binding-email
attrs: attrs:
evaluate_on_plan: true evaluate_on_plan: true
re_evaluate_policies: true re_evaluate_policies: true
@ -132,8 +124,8 @@ entries:
invalid_response_action: retry invalid_response_action: retry
- identifiers: - identifiers:
pk: 1219d06e-2c06-4c5b-a162-78e3959c6cf0 pk: 1219d06e-2c06-4c5b-a162-78e3959c6cf0
target: a5993183-89c0-43d2-a7f4-ddffb17baba7 target: !KeyOf flow
stage: fa2d8d65-1809-4dcc-bdc0-56266e0f7971 stage: !KeyOf stages-prompt-password
order: 30 order: 30
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
@ -142,9 +134,8 @@ entries:
policy_engine_mode: any policy_engine_mode: any
invalid_response_action: retry invalid_response_action: retry
- identifiers: - identifiers:
pk: 66de86ba-0707-46a0-8475-ff2e260d6935 target: !KeyOf flow
target: a5993183-89c0-43d2-a7f4-ddffb17baba7 stage: !KeyOf default-recovery-user-write
stage: 68b25ad5-318a-496e-95a7-cf4d94247f0d
order: 40 order: 40
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
@ -153,9 +144,8 @@ entries:
policy_engine_mode: any policy_engine_mode: any
invalid_response_action: retry invalid_response_action: retry
- identifiers: - identifiers:
pk: 9cec2334-d4a2-4895-a2b2-bc5ae4e9639a target: !KeyOf flow
target: a5993183-89c0-43d2-a7f4-ddffb17baba7 stage: !KeyOf default-recovery-user-login
stage: e74230b2-82bc-4843-8b18-2c3a66a62d57
order: 100 order: 100
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:
@ -164,9 +154,8 @@ entries:
policy_engine_mode: any policy_engine_mode: any
invalid_response_action: retry invalid_response_action: retry
- identifiers: - identifiers:
pk: 95aad215-8729-4177-953d-41ffbe86239e policy: !KeyOf default-recovery-skip-if-restored
policy: 1c5709ae-1b3e-413a-a117-260ab509bf5c target: !KeyOf flow-binding-identification
target: 7af7558e-2196-4b9f-a08e-d38420b7cfbb
order: 0 order: 0
model: authentik_policies.policybinding model: authentik_policies.policybinding
attrs: attrs:
@ -174,9 +163,8 @@ entries:
enabled: true enabled: true
timeout: 30 timeout: 30
- identifiers: - identifiers:
pk: a5454cbc-d2e4-403a-84af-6af999990b12 policy: !KeyOf default-recovery-skip-if-restored
policy: 1c5709ae-1b3e-413a-a117-260ab509bf5c target: !KeyOf flow-binding-email
target: 29446fd6-dd93-4e92-9830-2d81debad5ae
order: 0 order: 0
model: authentik_policies.policybinding model: authentik_policies.policybinding
attrs: attrs:

View File

@ -1,22 +1,21 @@
version: 1 version: 1
entries: entries:
- identifiers: - identifiers:
pk: 59a576ce-2f23-4a63-b63a-d18dc7e550f5
slug: default-unenrollment-flow slug: default-unenrollment-flow
model: authentik_flows.flow model: authentik_flows.flow
id: flow
attrs: attrs:
name: Default unenrollment flow name: Default unenrollment flow
title: Delete your account title: Delete your account
designation: unenrollment designation: unenrollment
- identifiers: - identifiers:
pk: c62ac2a4-2735-4a0f-abd0-8523d68c1209
name: default-unenrollment-user-delete name: default-unenrollment-user-delete
id: default-unenrollment-user-delete
model: authentik_stages_user_delete.userdeletestage model: authentik_stages_user_delete.userdeletestage
attrs: {} attrs: {}
- identifiers: - identifiers:
pk: eb9aff2b-b95d-40b3-ad08-233aa77bbcf3 target: !KeyOf flow
target: 59a576ce-2f23-4a63-b63a-d18dc7e550f5 stage: !KeyOf default-unenrollment-user-delete
stage: c62ac2a4-2735-4a0f-abd0-8523d68c1209
order: 10 order: 10
model: authentik_flows.flowstagebinding model: authentik_flows.flowstagebinding
attrs: attrs:

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-28 19:11+0000\n" "POT-Creation-Date: 2022-07-31 14:22+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -31,6 +31,18 @@ msgstr ""
msgid "Validation Error" msgid "Validation Error"
msgstr "" msgstr ""
#: authentik/blueprints/models.py:18
msgid "Managed by authentik"
msgstr ""
#: authentik/blueprints/models.py:69
msgid "Blueprint Instance"
msgstr ""
#: authentik/blueprints/models.py:70
msgid "Blueprint Instances"
msgstr ""
#: authentik/core/api/providers.py:89 #: authentik/core/api/providers.py:89
msgid "SAML Provider from Metadata" msgid "SAML Provider from Metadata"
msgstr "" msgstr ""
@ -55,95 +67,95 @@ msgstr ""
msgid "Users added to this group will be superusers." msgid "Users added to this group will be superusers."
msgstr "" msgstr ""
#: authentik/core/models.py:146 #: authentik/core/models.py:152
msgid "User's display name." msgid "User's display name."
msgstr "" msgstr ""
#: authentik/core/models.py:239 authentik/providers/oauth2/models.py:321 #: authentik/core/models.py:251 authentik/providers/oauth2/models.py:322
msgid "User" msgid "User"
msgstr "" msgstr ""
#: authentik/core/models.py:240 #: authentik/core/models.py:252
msgid "Users" msgid "Users"
msgstr "" msgstr ""
#: authentik/core/models.py:251 #: authentik/core/models.py:263
msgid "Flow used when authorizing this provider." msgid "Flow used when authorizing this provider."
msgstr "" msgstr ""
#: authentik/core/models.py:284 #: authentik/core/models.py:296
msgid "Application's display Name." msgid "Application's display Name."
msgstr "" msgstr ""
#: authentik/core/models.py:285 #: authentik/core/models.py:297
msgid "Internal application name, used in URLs." msgid "Internal application name, used in URLs."
msgstr "" msgstr ""
#: authentik/core/models.py:297 #: authentik/core/models.py:309
msgid "Open launch URL in a new browser tab or window." msgid "Open launch URL in a new browser tab or window."
msgstr "" msgstr ""
#: authentik/core/models.py:356 #: authentik/core/models.py:374
msgid "Application" msgid "Application"
msgstr "" msgstr ""
#: authentik/core/models.py:357 #: authentik/core/models.py:375
msgid "Applications" msgid "Applications"
msgstr "" msgstr ""
#: authentik/core/models.py:363 #: authentik/core/models.py:381
msgid "Use the source-specific identifier" msgid "Use the source-specific identifier"
msgstr "" msgstr ""
#: authentik/core/models.py:371 #: authentik/core/models.py:389
msgid "" msgid ""
"Use the user's email address, but deny enrollment when the email address " "Use the user's email address, but deny enrollment when the email address "
"already exists." "already exists."
msgstr "" msgstr ""
#: authentik/core/models.py:380 #: authentik/core/models.py:398
msgid "" msgid ""
"Use the user's username, but deny enrollment when the username already " "Use the user's username, but deny enrollment when the username already "
"exists." "exists."
msgstr "" msgstr ""
#: authentik/core/models.py:387 #: authentik/core/models.py:405
msgid "Source's display Name." msgid "Source's display Name."
msgstr "" msgstr ""
#: authentik/core/models.py:388 #: authentik/core/models.py:406
msgid "Internal source name, used in URLs." msgid "Internal source name, used in URLs."
msgstr "" msgstr ""
#: authentik/core/models.py:401 #: authentik/core/models.py:419
msgid "Flow to use when authenticating existing users." msgid "Flow to use when authenticating existing users."
msgstr "" msgstr ""
#: authentik/core/models.py:410 #: authentik/core/models.py:428
msgid "Flow to use when enrolling new users." msgid "Flow to use when enrolling new users."
msgstr "" msgstr ""
#: authentik/core/models.py:560 #: authentik/core/models.py:589
msgid "Token" msgid "Token"
msgstr "" msgstr ""
#: authentik/core/models.py:561 #: authentik/core/models.py:590
msgid "Tokens" msgid "Tokens"
msgstr "" msgstr ""
#: authentik/core/models.py:604 #: authentik/core/models.py:633
msgid "Property Mapping" msgid "Property Mapping"
msgstr "" msgstr ""
#: authentik/core/models.py:605 #: authentik/core/models.py:634
msgid "Property Mappings" msgid "Property Mappings"
msgstr "" msgstr ""
#: authentik/core/models.py:641 #: authentik/core/models.py:670
msgid "Authenticated Session" msgid "Authenticated Session"
msgstr "" msgstr ""
#: authentik/core/models.py:642 #: authentik/core/models.py:671
msgid "Authenticated Sessions" msgid "Authenticated Sessions"
msgstr "" msgstr ""
@ -166,12 +178,12 @@ msgstr ""
msgid "Go to home" msgid "Go to home"
msgstr "" msgstr ""
#: authentik/core/templates/if/admin.html:18
#: authentik/core/templates/if/admin.html:24 #: authentik/core/templates/if/admin.html:24
#: authentik/core/templates/if/flow.html:37 #: authentik/core/templates/if/admin.html:30
#: authentik/core/templates/if/flow.html:43 #: authentik/core/templates/if/flow.html:38
#: authentik/core/templates/if/user.html:18 #: authentik/core/templates/if/flow.html:44
#: authentik/core/templates/if/user.html:24 #: authentik/core/templates/if/user.html:24
#: authentik/core/templates/if/user.html:30
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -227,21 +239,21 @@ msgstr ""
msgid "Subject-alt name" msgid "Subject-alt name"
msgstr "" msgstr ""
#: authentik/crypto/models.py:34 #: authentik/crypto/models.py:35
msgid "PEM-encoded Certificate data" msgid "PEM-encoded Certificate data"
msgstr "" msgstr ""
#: authentik/crypto/models.py:37 #: authentik/crypto/models.py:38
msgid "" msgid ""
"Optional Private Key. If this is set, you can use this keypair for " "Optional Private Key. If this is set, you can use this keypair for "
"encryption." "encryption."
msgstr "" msgstr ""
#: authentik/crypto/models.py:100 #: authentik/crypto/models.py:107
msgid "Certificate-Key Pair" msgid "Certificate-Key Pair"
msgstr "" msgstr ""
#: authentik/crypto/models.py:101 #: authentik/crypto/models.py:108
msgid "Certificate-Key Pairs" msgid "Certificate-Key Pairs"
msgstr "" msgstr ""
@ -250,89 +262,89 @@ msgstr ""
msgid "Successfully imported %(count)d files." msgid "Successfully imported %(count)d files."
msgstr "" msgstr ""
#: authentik/events/models.py:288 #: authentik/events/models.py:294
msgid "Event" msgid "Event"
msgstr "" msgstr ""
#: authentik/events/models.py:289 #: authentik/events/models.py:295
msgid "Events" msgid "Events"
msgstr "" msgstr ""
#: authentik/events/models.py:295 #: authentik/events/models.py:301
msgid "authentik inbuilt notifications" msgid "authentik inbuilt notifications"
msgstr "" msgstr ""
#: authentik/events/models.py:296 #: authentik/events/models.py:302
msgid "Generic Webhook" msgid "Generic Webhook"
msgstr "" msgstr ""
#: authentik/events/models.py:297 #: authentik/events/models.py:303
msgid "Slack Webhook (Slack/Discord)" msgid "Slack Webhook (Slack/Discord)"
msgstr "" msgstr ""
#: authentik/events/models.py:298 #: authentik/events/models.py:304
msgid "Email" msgid "Email"
msgstr "" msgstr ""
#: authentik/events/models.py:316 #: authentik/events/models.py:322
msgid "" msgid ""
"Only send notification once, for example when sending a webhook into a chat " "Only send notification once, for example when sending a webhook into a chat "
"channel." "channel."
msgstr "" msgstr ""
#: authentik/events/models.py:374 #: authentik/events/models.py:380
msgid "Severity" msgid "Severity"
msgstr "" msgstr ""
#: authentik/events/models.py:379 #: authentik/events/models.py:385
msgid "Dispatched for user" msgid "Dispatched for user"
msgstr "" msgstr ""
#: authentik/events/models.py:456 #: authentik/events/models.py:468
msgid "Notification Transport" msgid "Notification Transport"
msgstr "" msgstr ""
#: authentik/events/models.py:457 #: authentik/events/models.py:469
msgid "Notification Transports" msgid "Notification Transports"
msgstr "" msgstr ""
#: authentik/events/models.py:463 #: authentik/events/models.py:475
msgid "Notice" msgid "Notice"
msgstr "" msgstr ""
#: authentik/events/models.py:464 #: authentik/events/models.py:476
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: authentik/events/models.py:465 #: authentik/events/models.py:477
msgid "Alert" msgid "Alert"
msgstr "" msgstr ""
#: authentik/events/models.py:485 #: authentik/events/models.py:503
msgid "Notification" msgid "Notification"
msgstr "" msgstr ""
#: authentik/events/models.py:486 #: authentik/events/models.py:504
msgid "Notifications" msgid "Notifications"
msgstr "" msgstr ""
#: authentik/events/models.py:506 #: authentik/events/models.py:524
msgid "Controls which severity level the created notifications will have." msgid "Controls which severity level the created notifications will have."
msgstr "" msgstr ""
#: authentik/events/models.py:526 #: authentik/events/models.py:550
msgid "Notification Rule" msgid "Notification Rule"
msgstr "" msgstr ""
#: authentik/events/models.py:527 #: authentik/events/models.py:551
msgid "Notification Rules" msgid "Notification Rules"
msgstr "" msgstr ""
#: authentik/events/models.py:548 #: authentik/events/models.py:572
msgid "Notification Webhook Mapping" msgid "Notification Webhook Mapping"
msgstr "" msgstr ""
#: authentik/events/models.py:549 #: authentik/events/models.py:573
msgid "Notification Webhook Mappings" msgid "Notification Webhook Mappings"
msgstr "" msgstr ""
@ -430,10 +442,6 @@ msgstr ""
msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'." msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'."
msgstr "" msgstr ""
#: authentik/managed/models.py:12
msgid "Managed by authentik"
msgstr ""
#: authentik/outposts/api/service_connections.py:132 #: authentik/outposts/api/service_connections.py:132
msgid "" msgid ""
"You can only use an empty kubeconfig when connecting to a local cluster." "You can only use an empty kubeconfig when connecting to a local cluster."
@ -443,33 +451,33 @@ msgstr ""
msgid "Invalid kubeconfig" msgid "Invalid kubeconfig"
msgstr "" msgstr ""
#: authentik/outposts/models.py:154 #: authentik/outposts/models.py:155
msgid "Outpost Service-Connection" msgid "Outpost Service-Connection"
msgstr "" msgstr ""
#: authentik/outposts/models.py:155 #: authentik/outposts/models.py:156
msgid "Outpost Service-Connections" msgid "Outpost Service-Connections"
msgstr "" msgstr ""
#: authentik/outposts/models.py:191 #: authentik/outposts/models.py:192
msgid "" msgid ""
"Certificate/Key used for authentication. Can be left empty for no " "Certificate/Key used for authentication. Can be left empty for no "
"authentication." "authentication."
msgstr "" msgstr ""
#: authentik/outposts/models.py:204 #: authentik/outposts/models.py:211
msgid "Docker Service-Connection" msgid "Docker Service-Connection"
msgstr "" msgstr ""
#: authentik/outposts/models.py:205 #: authentik/outposts/models.py:212
msgid "Docker Service-Connections" msgid "Docker Service-Connections"
msgstr "" msgstr ""
#: authentik/outposts/models.py:230 #: authentik/outposts/models.py:243
msgid "Kubernetes Service-Connection" msgid "Kubernetes Service-Connection"
msgstr "" msgstr ""
#: authentik/outposts/models.py:231 #: authentik/outposts/models.py:244
msgid "Kubernetes Service-Connections" msgid "Kubernetes Service-Connections"
msgstr "" msgstr ""
@ -666,184 +674,184 @@ msgstr ""
msgid "LDAP Providers" msgid "LDAP Providers"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:37 #: authentik/providers/oauth2/models.py:38
msgid "Confidential" msgid "Confidential"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:38 #: authentik/providers/oauth2/models.py:39
msgid "Public" msgid "Public"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:60 #: authentik/providers/oauth2/models.py:61
msgid "Based on the Hashed User ID" msgid "Based on the Hashed User ID"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:61 #: authentik/providers/oauth2/models.py:62
msgid "Based on the username" msgid "Based on the username"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:64 #: authentik/providers/oauth2/models.py:65
msgid "Based on the User's Email. This is recommended over the UPN method." msgid "Based on the User's Email. This is recommended over the UPN method."
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:80 #: authentik/providers/oauth2/models.py:81
msgid "Same identifier is used for all providers" msgid "Same identifier is used for all providers"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:82 #: authentik/providers/oauth2/models.py:83
msgid "Each provider has a different issuer, based on the application slug." msgid "Each provider has a different issuer, based on the application slug."
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:89 #: authentik/providers/oauth2/models.py:90
msgid "code (Authorization Code Flow)" msgid "code (Authorization Code Flow)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:90 #: authentik/providers/oauth2/models.py:91
msgid "id_token (Implicit Flow)" msgid "id_token (Implicit Flow)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:91 #: authentik/providers/oauth2/models.py:92
msgid "id_token token (Implicit Flow)" msgid "id_token token (Implicit Flow)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:92 #: authentik/providers/oauth2/models.py:93
msgid "code token (Hybrid Flow)" msgid "code token (Hybrid Flow)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:93 #: authentik/providers/oauth2/models.py:94
msgid "code id_token (Hybrid Flow)" msgid "code id_token (Hybrid Flow)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:94 #: authentik/providers/oauth2/models.py:95
msgid "code id_token token (Hybrid Flow)" msgid "code id_token token (Hybrid Flow)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:100 #: authentik/providers/oauth2/models.py:101
msgid "HS256 (Symmetric Encryption)" msgid "HS256 (Symmetric Encryption)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:101 #: authentik/providers/oauth2/models.py:102
msgid "RS256 (Asymmetric Encryption)" msgid "RS256 (Asymmetric Encryption)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:102 #: authentik/providers/oauth2/models.py:103
msgid "ES256 (Asymmetric Encryption)" msgid "ES256 (Asymmetric Encryption)"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:108 #: authentik/providers/oauth2/models.py:109
msgid "Scope used by the client" msgid "Scope used by the client"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:134 #: authentik/providers/oauth2/models.py:135
msgid "Scope Mapping" msgid "Scope Mapping"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:135 #: authentik/providers/oauth2/models.py:136
msgid "Scope Mappings" msgid "Scope Mappings"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:145 #: authentik/providers/oauth2/models.py:146
msgid "Client Type" msgid "Client Type"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:147 #: authentik/providers/oauth2/models.py:148
msgid "" msgid ""
"Confidential clients are capable of maintaining the confidentiality of their " "Confidential clients are capable of maintaining the confidentiality of their "
"credentials. Public clients are incapable" "credentials. Public clients are incapable"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:154 #: authentik/providers/oauth2/models.py:155
msgid "Client ID" msgid "Client ID"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:160 #: authentik/providers/oauth2/models.py:161
msgid "Client Secret" msgid "Client Secret"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:166 #: authentik/providers/oauth2/models.py:167
msgid "Redirect URIs" msgid "Redirect URIs"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:167 #: authentik/providers/oauth2/models.py:168
msgid "Enter each URI on a new line." msgid "Enter each URI on a new line."
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:172 #: authentik/providers/oauth2/models.py:173
msgid "Include claims in id_token" msgid "Include claims in id_token"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:220 #: authentik/providers/oauth2/models.py:221
msgid "Signing Key" msgid "Signing Key"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:224 #: authentik/providers/oauth2/models.py:225
msgid "" msgid ""
"Key used to sign the tokens. Only required when JWT Algorithm is set to " "Key used to sign the tokens. Only required when JWT Algorithm is set to "
"RS256." "RS256."
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:231 #: authentik/providers/oauth2/models.py:232
msgid "" msgid ""
"Any JWT signed by the JWK of the selected source can be used to authenticate." "Any JWT signed by the JWK of the selected source can be used to authenticate."
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:313 #: authentik/providers/oauth2/models.py:314
msgid "OAuth2/OpenID Provider" msgid "OAuth2/OpenID Provider"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:314 #: authentik/providers/oauth2/models.py:315
msgid "OAuth2/OpenID Providers" msgid "OAuth2/OpenID Providers"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:322 #: authentik/providers/oauth2/models.py:323
msgid "Scopes" msgid "Scopes"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:341 #: authentik/providers/oauth2/models.py:342
msgid "Code" msgid "Code"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:342 #: authentik/providers/oauth2/models.py:343
msgid "Nonce" msgid "Nonce"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:343 #: authentik/providers/oauth2/models.py:344
msgid "Is Authentication?" msgid "Is Authentication?"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:344 #: authentik/providers/oauth2/models.py:345
msgid "Code Challenge" msgid "Code Challenge"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:346 #: authentik/providers/oauth2/models.py:347
msgid "Code Challenge Method" msgid "Code Challenge Method"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:360 #: authentik/providers/oauth2/models.py:367
msgid "Authorization Code" msgid "Authorization Code"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:361 #: authentik/providers/oauth2/models.py:368
msgid "Authorization Codes" msgid "Authorization Codes"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:404 #: authentik/providers/oauth2/models.py:411
msgid "Access Token" msgid "Access Token"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:405 #: authentik/providers/oauth2/models.py:412
msgid "Refresh Token" msgid "Refresh Token"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:406 #: authentik/providers/oauth2/models.py:413
msgid "ID Token" msgid "ID Token"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:409 #: authentik/providers/oauth2/models.py:422
msgid "OAuth2 Token" msgid "OAuth2 Token"
msgstr "" msgstr ""
#: authentik/providers/oauth2/models.py:410 #: authentik/providers/oauth2/models.py:423
msgid "OAuth2 Tokens" msgid "OAuth2 Tokens"
msgstr "" msgstr ""
@ -870,42 +878,42 @@ msgstr ""
msgid "authentik API Access on behalf of your user" msgid "authentik API Access on behalf of your user"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:47 #: authentik/providers/proxy/models.py:54
msgid "Validate SSL Certificates of upstream servers" msgid "Validate SSL Certificates of upstream servers"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:48 #: authentik/providers/proxy/models.py:55
msgid "Internal host SSL Validation" msgid "Internal host SSL Validation"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:54 #: authentik/providers/proxy/models.py:61
msgid "" msgid ""
"Enable support for forwardAuth in traefik and nginx auth_request. Exclusive " "Enable support for forwardAuth in traefik and nginx auth_request. Exclusive "
"with internal_host." "with internal_host."
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:72 #: authentik/providers/proxy/models.py:79
msgid "Set HTTP-Basic Authentication" msgid "Set HTTP-Basic Authentication"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:74 #: authentik/providers/proxy/models.py:81
msgid "" msgid ""
"Set a custom HTTP-Basic Authentication header based on values from authentik." "Set a custom HTTP-Basic Authentication header based on values from authentik."
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:79 #: authentik/providers/proxy/models.py:86
msgid "HTTP-Basic Username Key" msgid "HTTP-Basic Username Key"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:89 #: authentik/providers/proxy/models.py:96
msgid "HTTP-Basic Password Key" msgid "HTTP-Basic Password Key"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:144 #: authentik/providers/proxy/models.py:151
msgid "Proxy Provider" msgid "Proxy Provider"
msgstr "" msgstr ""
#: authentik/providers/proxy/models.py:145 #: authentik/providers/proxy/models.py:152
msgid "Proxy Providers" msgid "Proxy Providers"
msgstr "" msgstr ""
@ -1213,11 +1221,11 @@ msgstr ""
msgid "Okta OAuth Sources" msgid "Okta OAuth Sources"
msgstr "" msgstr ""
#: authentik/sources/oauth/models.py:220 #: authentik/sources/oauth/models.py:228
msgid "User OAuth Source Connection" msgid "User OAuth Source Connection"
msgstr "" msgstr ""
#: authentik/sources/oauth/models.py:221 #: authentik/sources/oauth/models.py:229
msgid "User OAuth Source Connections" msgid "User OAuth Source Connections"
msgstr "" msgstr ""
@ -1245,11 +1253,11 @@ msgstr ""
msgid "Plex Sources" msgid "Plex Sources"
msgstr "" msgstr ""
#: authentik/sources/plex/models.py:104 #: authentik/sources/plex/models.py:110
msgid "User Plex Source Connection" msgid "User Plex Source Connection"
msgstr "" msgstr ""
#: authentik/sources/plex/models.py:105 #: authentik/sources/plex/models.py:111
msgid "User Plex Source Connections" msgid "User Plex Source Connections"
msgstr "" msgstr ""
@ -1322,42 +1330,42 @@ msgstr ""
msgid "SAML Sources" msgid "SAML Sources"
msgstr "" msgstr ""
#: authentik/stages/authenticator_duo/models.py:64 #: authentik/stages/authenticator_duo/models.py:65
msgid "Duo Authenticator Setup Stage" msgid "Duo Authenticator Setup Stage"
msgstr "" msgstr ""
#: authentik/stages/authenticator_duo/models.py:65 #: authentik/stages/authenticator_duo/models.py:66
msgid "Duo Authenticator Setup Stages" msgid "Duo Authenticator Setup Stages"
msgstr "" msgstr ""
#: authentik/stages/authenticator_duo/models.py:84 #: authentik/stages/authenticator_duo/models.py:90
msgid "Duo Device" msgid "Duo Device"
msgstr "" msgstr ""
#: authentik/stages/authenticator_duo/models.py:85 #: authentik/stages/authenticator_duo/models.py:91
msgid "Duo Devices" msgid "Duo Devices"
msgstr "" msgstr ""
#: authentik/stages/authenticator_sms/models.py:53 #: authentik/stages/authenticator_sms/models.py:56
msgid "" msgid ""
"When enabled, the Phone number is only used during enrollment to verify the " "When enabled, the Phone number is only used during enrollment to verify the "
"users authenticity. Only a hash of the phone number is saved to ensure it is " "users authenticity. Only a hash of the phone number is saved to ensure it is "
"not re-used in the future." "not re-used in the future."
msgstr "" msgstr ""
#: authentik/stages/authenticator_sms/models.py:167 #: authentik/stages/authenticator_sms/models.py:158
msgid "SMS Authenticator Setup Stage" msgid "SMS Authenticator Setup Stage"
msgstr "" msgstr ""
#: authentik/stages/authenticator_sms/models.py:168 #: authentik/stages/authenticator_sms/models.py:159
msgid "SMS Authenticator Setup Stages" msgid "SMS Authenticator Setup Stages"
msgstr "" msgstr ""
#: authentik/stages/authenticator_sms/models.py:207 #: authentik/stages/authenticator_sms/models.py:204
msgid "SMS Device" msgid "SMS Device"
msgstr "" msgstr ""
#: authentik/stages/authenticator_sms/models.py:208 #: authentik/stages/authenticator_sms/models.py:205
msgid "SMS Devices" msgid "SMS Devices"
msgstr "" msgstr ""
@ -1431,19 +1439,19 @@ msgstr ""
msgid "Authenticator Validation Stages" msgid "Authenticator Validation Stages"
msgstr "" msgstr ""
#: authentik/stages/authenticator_webauthn/models.py:112 #: authentik/stages/authenticator_webauthn/models.py:113
msgid "WebAuthn Authenticator Setup Stage" msgid "WebAuthn Authenticator Setup Stage"
msgstr "" msgstr ""
#: authentik/stages/authenticator_webauthn/models.py:113 #: authentik/stages/authenticator_webauthn/models.py:114
msgid "WebAuthn Authenticator Setup Stages" msgid "WebAuthn Authenticator Setup Stages"
msgstr "" msgstr ""
#: authentik/stages/authenticator_webauthn/models.py:146 #: authentik/stages/authenticator_webauthn/models.py:153
msgid "WebAuthn Device" msgid "WebAuthn Device"
msgstr "" msgstr ""
#: authentik/stages/authenticator_webauthn/models.py:147 #: authentik/stages/authenticator_webauthn/models.py:154
msgid "WebAuthn Devices" msgid "WebAuthn Devices"
msgstr "" msgstr ""
@ -1465,19 +1473,19 @@ msgstr ""
msgid "Captcha Stages" msgid "Captcha Stages"
msgstr "" msgstr ""
#: authentik/stages/consent/models.py:50 #: authentik/stages/consent/models.py:51
msgid "Consent Stage" msgid "Consent Stage"
msgstr "" msgstr ""
#: authentik/stages/consent/models.py:51 #: authentik/stages/consent/models.py:52
msgid "Consent Stages" msgid "Consent Stages"
msgstr "" msgstr ""
#: authentik/stages/consent/models.py:67 #: authentik/stages/consent/models.py:74
msgid "User Consent" msgid "User Consent"
msgstr "" msgstr ""
#: authentik/stages/consent/models.py:68 #: authentik/stages/consent/models.py:75
msgid "User Consents" msgid "User Consents"
msgstr "" msgstr ""
@ -1645,27 +1653,27 @@ msgstr ""
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
#: authentik/stages/invitation/models.py:46 #: authentik/stages/invitation/models.py:47
msgid "Invitation Stage" msgid "Invitation Stage"
msgstr "" msgstr ""
#: authentik/stages/invitation/models.py:47 #: authentik/stages/invitation/models.py:48
msgid "Invitation Stages" msgid "Invitation Stages"
msgstr "" msgstr ""
#: authentik/stages/invitation/models.py:59 #: authentik/stages/invitation/models.py:60
msgid "When enabled, the invitation will be deleted after usage." msgid "When enabled, the invitation will be deleted after usage."
msgstr "" msgstr ""
#: authentik/stages/invitation/models.py:66 #: authentik/stages/invitation/models.py:67
msgid "Optional fixed data to enforce on user enrollment." msgid "Optional fixed data to enforce on user enrollment."
msgstr "" msgstr ""
#: authentik/stages/invitation/models.py:74 #: authentik/stages/invitation/models.py:81
msgid "Invitation" msgid "Invitation"
msgstr "" msgstr ""
#: authentik/stages/invitation/models.py:75 #: authentik/stages/invitation/models.py:82
msgid "Invitations" msgid "Invitations"
msgstr "" msgstr ""
@ -1817,16 +1825,16 @@ msgstr ""
msgid "No Pending data." msgid "No Pending data."
msgstr "" msgstr ""
#: authentik/tenants/models.py:18 #: authentik/tenants/models.py:20
msgid "" msgid ""
"Domain that activates this tenant. Can be a superset, i.e. `a.b` for `aa.b` " "Domain that activates this tenant. Can be a superset, i.e. `a.b` for `aa.b` "
"and `ba.b`" "and `ba.b`"
msgstr "" msgstr ""
#: authentik/tenants/models.py:80 #: authentik/tenants/models.py:87
msgid "Tenant" msgid "Tenant"
msgstr "" msgstr ""
#: authentik/tenants/models.py:81 #: authentik/tenants/models.py:88
msgid "Tenants" msgid "Tenants"
msgstr "" msgstr ""

View File

@ -5869,7 +5869,7 @@ paths:
/flows/instances/{slug}/export/: /flows/instances/{slug}/export/:
get: get:
operationId: flows_instances_export_retrieve operationId: flows_instances_export_retrieve
description: Export flow to .akflow file description: Export flow to .yaml file
parameters: parameters:
- in: path - in: path
name: slug name: slug
@ -6013,7 +6013,7 @@ paths:
/flows/instances/import_flow/: /flows/instances/import_flow/:
post: post:
operationId: flows_instances_import_flow_create operationId: flows_instances_import_flow_create
description: Import flow from .akflow file description: Import flow from .yaml file
tags: tags:
- flows - flows
requestBody: requestBody:
@ -6030,6 +6030,215 @@ paths:
description: Bad request description: Bad request
'403': '403':
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
/managed/blueprints/:
get:
operationId: managed_blueprints_list
description: Blueprint instances
parameters:
- in: query
name: name
schema:
type: string
- name: ordering
required: false
in: query
description: Which field to use when ordering the results.
schema:
type: string
- name: page
required: false
in: query
description: A page number within the paginated result set.
schema:
type: integer
- name: page_size
required: false
in: query
description: Number of results to return per page.
schema:
type: integer
- in: query
name: path
schema:
type: string
- name: search
required: false
in: query
description: A search term.
schema:
type: string
tags:
- managed
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedBlueprintInstanceList'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
post:
operationId: managed_blueprints_create
description: Blueprint instances
tags:
- managed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstanceRequest'
required: true
security:
- authentik: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/managed/blueprints/{instance_uuid}/:
get:
operationId: managed_blueprints_retrieve
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
put:
operationId: managed_blueprints_update
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstanceRequest'
required: true
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: managed_blueprints_partial_update
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedBlueprintInstanceRequest'
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: managed_blueprints_destroy
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
security:
- authentik: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/managed/blueprints/available/:
get:
operationId: managed_blueprints_available_list
description: Get blueprints
tags:
- managed
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
type: string
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/oauth2/authorization_codes/: /oauth2/authorization_codes/:
get: get:
operationId: oauth2_authorization_codes_list operationId: oauth2_authorization_codes_list
@ -8145,12 +8354,12 @@ paths:
enum: enum:
- authentik.admin - authentik.admin
- authentik.api - authentik.api
- authentik.blueprints
- authentik.core - authentik.core
- authentik.crypto - authentik.crypto
- authentik.events - authentik.events
- authentik.flows - authentik.flows
- authentik.lib - authentik.lib
- authentik.managed
- authentik.outposts - authentik.outposts
- authentik.policies - authentik.policies
- authentik.policies.dummy - authentik.policies.dummy
@ -19607,7 +19816,7 @@ components:
- authentik.stages.user_logout - authentik.stages.user_logout
- authentik.stages.user_write - authentik.stages.user_write
- authentik.tenants - authentik.tenants
- authentik.managed - authentik.blueprints
- authentik.core - authentik.core
type: string type: string
AppleChallengeResponseRequest: AppleChallengeResponseRequest:
@ -20653,6 +20862,60 @@ components:
- POST - POST
- POST_AUTO - POST_AUTO
type: string type: string
BlueprintInstance:
type: object
description: Info about a single blueprint instance file
properties:
name:
type: string
path:
type: string
context:
type: object
additionalProperties: {}
last_applied:
type: string
format: date-time
readOnly: true
status:
$ref: '#/components/schemas/BlueprintInstanceStatusEnum'
enabled:
type: boolean
required:
- context
- last_applied
- name
- path
- status
BlueprintInstanceRequest:
type: object
description: Info about a single blueprint instance file
properties:
name:
type: string
minLength: 1
path:
type: string
minLength: 1
context:
type: object
additionalProperties: {}
status:
$ref: '#/components/schemas/BlueprintInstanceStatusEnum'
enabled:
type: boolean
required:
- context
- name
- path
- status
BlueprintInstanceStatusEnum:
enum:
- successful
- warning
- error
- unknown
type: string
Cache: Cache:
type: object type: object
description: Generic cache stats for an object description: Generic cache stats for an object
@ -24565,6 +24828,41 @@ components:
required: required:
- pagination - pagination
- results - results
PaginatedBlueprintInstanceList:
type: object
properties:
pagination:
type: object
properties:
next:
type: number
previous:
type: number
count:
type: number
current:
type: number
total_pages:
type: number
start_index:
type: number
end_index:
type: number
required:
- next
- previous
- count
- current
- total_pages
- start_index
- end_index
results:
type: array
items:
$ref: '#/components/schemas/BlueprintInstance'
required:
- pagination
- results
PaginatedCaptchaStageList: PaginatedCaptchaStageList:
type: object type: object
properties: properties:
@ -27472,6 +27770,23 @@ components:
minLength: 1 minLength: 1
description: If any of the user's device has been used within this threshold, description: If any of the user's device has been used within this threshold,
this stage will be skipped this stage will be skipped
PatchedBlueprintInstanceRequest:
type: object
description: Info about a single blueprint instance file
properties:
name:
type: string
minLength: 1
path:
type: string
minLength: 1
context:
type: object
additionalProperties: {}
status:
$ref: '#/components/schemas/BlueprintInstanceStatusEnum'
enabled:
type: boolean
PatchedCaptchaStageRequest: PatchedCaptchaStageRequest:
type: object type: object
description: CaptchaStage Serializer description: CaptchaStage Serializer
@ -31291,13 +31606,6 @@ components:
maxLength: 16 maxLength: 16
required: required:
- token - token
StatusEnum:
enum:
- SUCCESSFUL
- WARNING
- ERROR
- UNKNOWN
type: string
SubModeEnum: SubModeEnum:
enum: enum:
- hashed_user_id - hashed_user_id
@ -31406,7 +31714,7 @@ components:
type: string type: string
format: date-time format: date-time
status: status:
$ref: '#/components/schemas/StatusEnum' $ref: '#/components/schemas/TaskStatusEnum'
messages: messages:
type: array type: array
items: {} items: {}
@ -31416,6 +31724,13 @@ components:
- task_description - task_description
- task_finish_timestamp - task_finish_timestamp
- task_name - task_name
TaskStatusEnum:
enum:
- SUCCESSFUL
- WARNING
- ERROR
- UNKNOWN
type: string
Tenant: Tenant:
type: object type: object
description: Tenant Serializer description: Tenant Serializer

Some files were not shown because too many files have changed in this diff Show More