blueprints: add meta model to apply blueprint within blueprint for dependencies (#3486)
* add meta model to apply blueprint within blueprint for dependencies Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix tests Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * use custom registry Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix again Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * move ManagedAppConfig to apps.py Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * rename manager to registry Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * ci: use full tag in comment Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
58e3ca28be
commit
54ba3e9616
|
@ -83,8 +83,6 @@ runs:
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/goauthentik/dev-server
|
repository: ghcr.io/goauthentik/dev-server
|
||||||
tag: ${{ inputs.tag }}
|
tag: ${{ inputs.tag }}
|
||||||
# pullPolicy: Always to ensure you always get the latest version
|
|
||||||
pullPolicy: Always
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Afterwards, run the upgrade commands from the latest release notes.
|
Afterwards, run the upgrade commands from the latest release notes.
|
||||||
|
|
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
|
@ -240,4 +240,4 @@ jobs:
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: ./.github/actions/comment-pr-instructions
|
uses: ./.github/actions/comment-pr-instructions
|
||||||
with:
|
with:
|
||||||
tag: gh-${{ steps.ev.outputs.branchNameContainer }}
|
tag: gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.sha }}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""authentik admin app config"""
|
"""authentik admin app config"""
|
||||||
from prometheus_client import Gauge, Info
|
from prometheus_client import Gauge, Info
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
PROM_INFO = Info("authentik_version", "Currently running authentik version")
|
PROM_INFO = Info("authentik_version", "Currently running authentik version")
|
||||||
GAUGE_WORKERS = Gauge("authentik_admin_workers", "Currently connected workers")
|
GAUGE_WORKERS = Gauge("authentik_admin_workers", "Currently connected workers")
|
||||||
|
|
|
@ -1,6 +1,46 @@
|
||||||
"""authentik Blueprints app"""
|
"""authentik Blueprints app"""
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from importlib import import_module
|
||||||
|
from inspect import ismethod
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.db import DatabaseError, InternalError, ProgrammingError
|
||||||
|
from structlog.stdlib import BoundLogger, get_logger
|
||||||
|
|
||||||
|
|
||||||
|
class ManagedAppConfig(AppConfig):
|
||||||
|
"""Basic reconciliation logic for apps"""
|
||||||
|
|
||||||
|
_logger: BoundLogger
|
||||||
|
|
||||||
|
def __init__(self, app_name: str, *args, **kwargs) -> None:
|
||||||
|
super().__init__(app_name, *args, **kwargs)
|
||||||
|
self._logger = get_logger().bind(app_name=app_name)
|
||||||
|
|
||||||
|
def ready(self) -> None:
|
||||||
|
self.reconcile()
|
||||||
|
return super().ready()
|
||||||
|
|
||||||
|
def import_module(self, path: str):
|
||||||
|
"""Load module"""
|
||||||
|
import_module(path)
|
||||||
|
|
||||||
|
def reconcile(self) -> None:
|
||||||
|
"""reconcile ourselves"""
|
||||||
|
prefix = "reconcile_"
|
||||||
|
for meth_name in dir(self):
|
||||||
|
meth = getattr(self, meth_name)
|
||||||
|
if not ismethod(meth):
|
||||||
|
continue
|
||||||
|
if not meth_name.startswith(prefix):
|
||||||
|
continue
|
||||||
|
name = meth_name.replace(prefix, "")
|
||||||
|
try:
|
||||||
|
self._logger.debug("Starting reconciler", name=name)
|
||||||
|
meth()
|
||||||
|
self._logger.debug("Successfully reconciled", name=name)
|
||||||
|
except (DatabaseError, ProgrammingError, InternalError) as exc:
|
||||||
|
self._logger.debug("Failed to run reconcile", name=name, exc=exc)
|
||||||
|
|
||||||
|
|
||||||
class AuthentikBlueprintsConfig(ManagedAppConfig):
|
class AuthentikBlueprintsConfig(ManagedAppConfig):
|
||||||
|
@ -15,6 +55,10 @@ class AuthentikBlueprintsConfig(ManagedAppConfig):
|
||||||
"""Load v1 tasks"""
|
"""Load v1 tasks"""
|
||||||
self.import_module("authentik.blueprints.v1.tasks")
|
self.import_module("authentik.blueprints.v1.tasks")
|
||||||
|
|
||||||
|
def reconcile_load_blueprints_v1_meta(self):
|
||||||
|
"""Load v1 meta models"""
|
||||||
|
self.import_module("authentik.blueprints.v1.meta.apply_blueprint")
|
||||||
|
|
||||||
def reconcile_blueprints_discover(self):
|
def reconcile_blueprints_discover(self):
|
||||||
"""Run blueprint discovery"""
|
"""Run blueprint discovery"""
|
||||||
from authentik.blueprints.v1.tasks import blueprints_discover
|
from authentik.blueprints.v1.tasks import blueprints_discover
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
from json import dumps, loads
|
from json import dumps, loads
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.apps import apps
|
|
||||||
from django.core.management.base import BaseCommand, no_translations
|
from django.core.management.base import BaseCommand, no_translations
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.blueprints.v1.importer import is_model_allowed
|
from authentik.blueprints.v1.importer import is_model_allowed
|
||||||
|
from authentik.blueprints.v1.meta.registry import registry
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
@ -29,10 +29,11 @@ class Command(BaseCommand):
|
||||||
def set_model_allowed(self):
|
def set_model_allowed(self):
|
||||||
"""Set model enum"""
|
"""Set model enum"""
|
||||||
model_names = []
|
model_names = []
|
||||||
for model in apps.get_models():
|
for model in registry.get_models():
|
||||||
if not is_model_allowed(model):
|
if not is_model_allowed(model):
|
||||||
continue
|
continue
|
||||||
if SerializerModel not in model.__mro__:
|
if SerializerModel not in model.__mro__:
|
||||||
continue
|
continue
|
||||||
model_names.append(f"{model._meta.app_label}.{model._meta.model_name}")
|
model_names.append(f"{model._meta.app_label}.{model._meta.model_name}")
|
||||||
|
model_names.sort()
|
||||||
self.schema["properties"]["entries"]["items"]["properties"]["model"]["enum"] = model_names
|
self.schema["properties"]["entries"]["items"]["properties"]["model"]["enum"] = model_names
|
||||||
|
|
|
@ -41,8 +41,7 @@
|
||||||
"$id": "#entry",
|
"$id": "#entry",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"model",
|
"model"
|
||||||
"identifiers"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"model": {
|
"model": {
|
||||||
|
@ -67,6 +66,7 @@
|
||||||
},
|
},
|
||||||
"identifiers": {
|
"identifiers": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"default": {},
|
||||||
"properties": {
|
"properties": {
|
||||||
"pk": {
|
"pk": {
|
||||||
"description": "Commonly available field, may not exist on all models",
|
"description": "Commonly available field, may not exist on all models",
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
"""Managed objects manager"""
|
|
||||||
from importlib import import_module
|
|
||||||
from inspect import ismethod
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
|
||||||
from django.db import DatabaseError, InternalError, ProgrammingError
|
|
||||||
from structlog.stdlib import BoundLogger, get_logger
|
|
||||||
|
|
||||||
LOGGER = get_logger()
|
|
||||||
|
|
||||||
|
|
||||||
class ManagedAppConfig(AppConfig):
|
|
||||||
"""Basic reconciliation logic for apps"""
|
|
||||||
|
|
||||||
_logger: BoundLogger
|
|
||||||
|
|
||||||
def __init__(self, app_name: str, *args, **kwargs) -> None:
|
|
||||||
super().__init__(app_name, *args, **kwargs)
|
|
||||||
self._logger = get_logger().bind(app_name=app_name)
|
|
||||||
|
|
||||||
def ready(self) -> None:
|
|
||||||
self.reconcile()
|
|
||||||
return super().ready()
|
|
||||||
|
|
||||||
def import_module(self, path: str):
|
|
||||||
"""Load module"""
|
|
||||||
import_module(path)
|
|
||||||
|
|
||||||
def reconcile(self) -> None:
|
|
||||||
"""reconcile ourselves"""
|
|
||||||
prefix = "reconcile_"
|
|
||||||
for meth_name in dir(self):
|
|
||||||
meth = getattr(self, meth_name)
|
|
||||||
if not ismethod(meth):
|
|
||||||
continue
|
|
||||||
if not meth_name.startswith(prefix):
|
|
||||||
continue
|
|
||||||
name = meth_name.replace(prefix, "")
|
|
||||||
try:
|
|
||||||
self._logger.debug("Starting reconciler", name=name)
|
|
||||||
meth()
|
|
||||||
self._logger.debug("Successfully reconciled", name=name)
|
|
||||||
except (DatabaseError, ProgrammingError, InternalError) as exc:
|
|
||||||
self._logger.debug("Failed to run reconcile", name=name, exc=exc)
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""Managed Object models"""
|
"""blueprint models"""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Callable
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
from authentik.blueprints.models import BlueprintInstance
|
from authentik.blueprints.models import BlueprintInstance
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ class BlueprintEntryState:
|
||||||
class BlueprintEntry:
|
class BlueprintEntry:
|
||||||
"""Single entry of a blueprint"""
|
"""Single entry of a blueprint"""
|
||||||
|
|
||||||
identifiers: dict[str, Any]
|
|
||||||
model: str
|
model: str
|
||||||
|
identifiers: dict[str, Any] = field(default_factory=dict)
|
||||||
attrs: Optional[dict[str, Any]] = field(default_factory=dict)
|
attrs: Optional[dict[str, Any]] = field(default_factory=dict)
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
|
|
|
@ -6,7 +6,6 @@ from typing import Any, Optional
|
||||||
from dacite import from_dict
|
from dacite import from_dict
|
||||||
from dacite.exceptions import DaciteError
|
from dacite.exceptions import DaciteError
|
||||||
from deepmerge import always_merger
|
from deepmerge import always_merger
|
||||||
from django.apps import apps
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from django.db.models.query_utils import Q
|
from django.db.models.query_utils import Q
|
||||||
|
@ -25,6 +24,7 @@ from authentik.blueprints.v1.common import (
|
||||||
BlueprintLoader,
|
BlueprintLoader,
|
||||||
EntryInvalidError,
|
EntryInvalidError,
|
||||||
)
|
)
|
||||||
|
from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry
|
||||||
from authentik.core.models import (
|
from authentik.core.models import (
|
||||||
AuthenticatedSession,
|
AuthenticatedSession,
|
||||||
PropertyMapping,
|
PropertyMapping,
|
||||||
|
@ -138,10 +138,17 @@ class Importer:
|
||||||
def _validate_single(self, entry: BlueprintEntry) -> 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] = registry.get_model(model_app_label, model_name)
|
||||||
# Don't use isinstance since we don't want to check for inheritance
|
# Don't use isinstance since we don't want to check for inheritance
|
||||||
if not is_model_allowed(model):
|
if not is_model_allowed(model):
|
||||||
raise EntryInvalidError(f"Model {model} not allowed")
|
raise EntryInvalidError(f"Model {model} not allowed")
|
||||||
|
if issubclass(model, BaseMetaModel):
|
||||||
|
serializer = model.serializer()(data=entry.get_attrs(self.__import))
|
||||||
|
try:
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
except ValidationError as exc:
|
||||||
|
raise EntryInvalidError(f"Serializer errors {serializer.errors}") from exc
|
||||||
|
return serializer
|
||||||
if entry.identifiers == {}:
|
if entry.identifiers == {}:
|
||||||
raise EntryInvalidError("No identifiers")
|
raise EntryInvalidError("No identifiers")
|
||||||
|
|
||||||
|
@ -158,7 +165,7 @@ class Importer:
|
||||||
existing_models = model.objects.filter(self.__query_from_identifier(updated_identifiers))
|
existing_models = model.objects.filter(self.__query_from_identifier(updated_identifiers))
|
||||||
|
|
||||||
serializer_kwargs = {}
|
serializer_kwargs = {}
|
||||||
if existing_models.exists():
|
if not isinstance(model(), BaseMetaModel) and existing_models.exists():
|
||||||
model_instance = existing_models.first()
|
model_instance = existing_models.first()
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
"initialise serializer with instance",
|
"initialise serializer with instance",
|
||||||
|
@ -207,7 +214,7 @@ class Importer:
|
||||||
for entry in self.__import.entries:
|
for entry in self.__import.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: type[SerializerModel] = registry.get_model(model_app_label, model_name)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
"app or model does not exist", app=model_app_label, model=model_name
|
"app or model does not exist", app=model_app_label, model=model_name
|
||||||
|
@ -224,7 +231,7 @@ class Importer:
|
||||||
if "pk" in entry.identifiers:
|
if "pk" in entry.identifiers:
|
||||||
self.__pk_map[entry.identifiers["pk"]] = model.pk
|
self.__pk_map[entry.identifiers["pk"]] = model.pk
|
||||||
entry._state = BlueprintEntryState(model)
|
entry._state = BlueprintEntryState(model)
|
||||||
self.logger.debug("updated model", model=model, pk=model.pk)
|
self.logger.debug("updated model", model=model)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validate(self) -> tuple[bool, list[EventDict]]:
|
def validate(self) -> tuple[bool, list[EventDict]]:
|
||||||
|
@ -243,6 +250,6 @@ class Importer:
|
||||||
if not successful:
|
if not successful:
|
||||||
self.logger.debug("Blueprint validation failed")
|
self.logger.debug("Blueprint validation failed")
|
||||||
for log in logs:
|
for log in logs:
|
||||||
self.logger.debug(**log)
|
getattr(self.logger, log.get("log_level"))(**log)
|
||||||
self.__import = orig_import
|
self.__import = orig_import
|
||||||
return successful, logs
|
return successful, logs
|
||||||
|
|
0
authentik/blueprints/v1/meta/__init__.py
Normal file
0
authentik/blueprints/v1/meta/__init__.py
Normal file
46
authentik/blueprints/v1/meta/apply_blueprint.py
Normal file
46
authentik/blueprints/v1/meta/apply_blueprint.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
"""Apply Blueprint meta model"""
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
from rest_framework.fields import BooleanField, JSONField
|
||||||
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
|
from authentik.blueprints.v1.meta.registry import BaseMetaModel, MetaResult, registry
|
||||||
|
from authentik.core.api.utils import PassiveSerializer, is_dict
|
||||||
|
|
||||||
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
class ApplyBlueprintMetaSerializer(PassiveSerializer):
|
||||||
|
"""Serializer for meta apply blueprint model"""
|
||||||
|
|
||||||
|
identifiers = JSONField(validators=[is_dict])
|
||||||
|
required = BooleanField(default=True)
|
||||||
|
|
||||||
|
def create(self, validated_data: dict) -> MetaResult:
|
||||||
|
from authentik.blueprints.models import BlueprintInstance
|
||||||
|
from authentik.blueprints.v1.tasks import apply_blueprint
|
||||||
|
|
||||||
|
identifiers = validated_data["identifiers"]
|
||||||
|
required = validated_data["required"]
|
||||||
|
instance = BlueprintInstance.objects.filter(**identifiers).first()
|
||||||
|
if not instance:
|
||||||
|
if required:
|
||||||
|
raise ValidationError("Required blueprint does not exist")
|
||||||
|
LOGGER.info("Blueprint does not exist, but not required")
|
||||||
|
return MetaResult()
|
||||||
|
LOGGER.debug("Applying blueprint from meta model", blueprint=instance)
|
||||||
|
# pylint: disable=no-value-for-parameter
|
||||||
|
apply_blueprint(str(instance.pk))
|
||||||
|
return MetaResult()
|
||||||
|
|
||||||
|
|
||||||
|
@registry.register("metaapplyblueprint")
|
||||||
|
class MetaApplyBlueprint(BaseMetaModel):
|
||||||
|
"""Meta model to apply another blueprint"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def serializer() -> ApplyBlueprintMetaSerializer:
|
||||||
|
return ApplyBlueprintMetaSerializer
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
abstract = True
|
63
authentik/blueprints/v1/meta/registry.py
Normal file
63
authentik/blueprints/v1/meta/registry.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"""Base models"""
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db.models import Model
|
||||||
|
from rest_framework.serializers import Serializer
|
||||||
|
|
||||||
|
|
||||||
|
class BaseMetaModel(Model):
|
||||||
|
"""Base models"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def serializer() -> Serializer:
|
||||||
|
"""Serializer similar to SerializerModel, but as a static method since
|
||||||
|
this is an abstract model"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class MetaResult:
|
||||||
|
"""Result returned by Meta Models' serializers. Empty class but we can't return none as
|
||||||
|
the framework doesn't allow that"""
|
||||||
|
|
||||||
|
|
||||||
|
class MetaModelRegistry:
|
||||||
|
"""Registry for pseudo meta models"""
|
||||||
|
|
||||||
|
models: dict[str, BaseMetaModel]
|
||||||
|
virtual_prefix: str
|
||||||
|
|
||||||
|
def __init__(self, prefix: str) -> None:
|
||||||
|
self.models = {}
|
||||||
|
self.virtual_prefix = prefix
|
||||||
|
|
||||||
|
def register(self, model_id: str):
|
||||||
|
"""Register model class under `model_id`"""
|
||||||
|
|
||||||
|
def inner_wrapper(cls):
|
||||||
|
self.models[model_id] = cls
|
||||||
|
return cls
|
||||||
|
|
||||||
|
return inner_wrapper
|
||||||
|
|
||||||
|
def get_models(self):
|
||||||
|
"""Wrapper for django's `get_models` to list all models"""
|
||||||
|
models = apps.get_models()
|
||||||
|
for _, value in self.models.items():
|
||||||
|
models.append(value)
|
||||||
|
return models
|
||||||
|
|
||||||
|
def get_model(self, app_label: str, model_id: str) -> Optional[type[Model]]:
|
||||||
|
"""Get model checks if any virtual models are registered, and falls back
|
||||||
|
to actual django models"""
|
||||||
|
if app_label == self.virtual_prefix:
|
||||||
|
if model_id in self.models:
|
||||||
|
return self.models[model_id]
|
||||||
|
return apps.get_model(app_label, model_id)
|
||||||
|
|
||||||
|
|
||||||
|
registry = MetaModelRegistry("authentik_blueprints")
|
|
@ -1,7 +1,7 @@
|
||||||
"""authentik core app config"""
|
"""authentik core app config"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
|
|
||||||
class AuthentikCoreConfig(ManagedAppConfig):
|
class AuthentikCoreConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""authentik events app"""
|
"""authentik events app"""
|
||||||
from prometheus_client import Gauge
|
from prometheus_client import Gauge
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
GAUGE_TASKS = Gauge(
|
GAUGE_TASKS = Gauge(
|
||||||
"authentik_system_tasks",
|
"authentik_system_tasks",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""authentik flows app config"""
|
"""authentik flows app config"""
|
||||||
from prometheus_client import Gauge, Histogram
|
from prometheus_client import Gauge, Histogram
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
from authentik.lib.utils.reflection import all_subclasses
|
from authentik.lib.utils.reflection import all_subclasses
|
||||||
|
|
||||||
GAUGE_FLOWS_CACHED = Gauge(
|
GAUGE_FLOWS_CACHED = Gauge(
|
||||||
|
|
|
@ -20,9 +20,8 @@ def model_tester_factory(test_model: type[Stage]) -> Callable:
|
||||||
try:
|
try:
|
||||||
model_class = None
|
model_class = None
|
||||||
if test_model._meta.abstract:
|
if test_model._meta.abstract:
|
||||||
model_class = test_model.__bases__[0]()
|
return
|
||||||
else:
|
model_class = test_model()
|
||||||
model_class = test_model()
|
|
||||||
self.assertTrue(issubclass(model_class.serializer, BaseSerializer))
|
self.assertTrue(issubclass(model_class.serializer, BaseSerializer))
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from prometheus_client import Gauge
|
from prometheus_client import Gauge
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""authentik policies app config"""
|
"""authentik policies app config"""
|
||||||
from prometheus_client import Gauge, Histogram
|
from prometheus_client import Gauge, Histogram
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
GAUGE_POLICIES_CACHED = Gauge(
|
GAUGE_POLICIES_CACHED = Gauge(
|
||||||
"authentik_policies_cached",
|
"authentik_policies_cached",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""Authentik reputation_policy app config"""
|
"""Authentik reputation_policy app config"""
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
|
|
||||||
class AuthentikPolicyReputationConfig(ManagedAppConfig):
|
class AuthentikPolicyReputationConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""authentik Proxy app"""
|
"""authentik Proxy app"""
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
|
|
||||||
class AuthentikProviderProxyConfig(ManagedAppConfig):
|
class AuthentikProviderProxyConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""authentik ldap source config"""
|
"""authentik ldap source config"""
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
|
|
||||||
class AuthentikSourceLDAPConfig(ManagedAppConfig):
|
class AuthentikSourceLDAPConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -15,7 +15,7 @@ from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
from authentik.lib.utils.http import get_http_session
|
from authentik.lib.utils.http import get_http_session
|
||||||
from authentik.sources.oauth.models import OAuthSource
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
|
|
||||||
|
|
||||||
class SourceTypeSerializer(PassiveSerializer):
|
class SourceTypeSerializer(PassiveSerializer):
|
||||||
|
@ -33,7 +33,7 @@ class SourceTypeSerializer(PassiveSerializer):
|
||||||
class OAuthSourceSerializer(SourceSerializer):
|
class OAuthSourceSerializer(SourceSerializer):
|
||||||
"""OAuth Source Serializer"""
|
"""OAuth Source Serializer"""
|
||||||
|
|
||||||
provider_type = ChoiceField(choices=MANAGER.get_name_tuple())
|
provider_type = ChoiceField(choices=registry.get_name_tuple())
|
||||||
callback_url = SerializerMethodField()
|
callback_url = SerializerMethodField()
|
||||||
|
|
||||||
def get_callback_url(self, instance: OAuthSource) -> str:
|
def get_callback_url(self, instance: OAuthSource) -> str:
|
||||||
|
@ -81,7 +81,7 @@ class OAuthSourceSerializer(SourceSerializer):
|
||||||
config = jwks_config.json()
|
config = jwks_config.json()
|
||||||
attrs["oidc_jwks"] = config
|
attrs["oidc_jwks"] = config
|
||||||
|
|
||||||
provider_type = MANAGER.find_type(attrs.get("provider_type", ""))
|
provider_type = registry.find_type(attrs.get("provider_type", ""))
|
||||||
for url in [
|
for url in [
|
||||||
"authorization_url",
|
"authorization_url",
|
||||||
"access_token_url",
|
"access_token_url",
|
||||||
|
@ -153,10 +153,10 @@ class OAuthSourceViewSet(UsedByMixin, ModelViewSet):
|
||||||
If <name> isn't found, returns the default type."""
|
If <name> isn't found, returns the default type."""
|
||||||
data = []
|
data = []
|
||||||
if "name" in request.query_params:
|
if "name" in request.query_params:
|
||||||
source_type = MANAGER.find_type(request.query_params.get("name"))
|
source_type = registry.find_type(request.query_params.get("name"))
|
||||||
if source_type.__class__ != SourceType:
|
if source_type.__class__ != SourceType:
|
||||||
data.append(SourceTypeSerializer(source_type).data)
|
data.append(SourceTypeSerializer(source_type).data)
|
||||||
else:
|
else:
|
||||||
for source_type in MANAGER.get():
|
for source_type in registry.get():
|
||||||
data.append(SourceTypeSerializer(source_type).data)
|
data.append(SourceTypeSerializer(source_type).data)
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""authentik oauth_client config"""
|
"""authentik oauth_client config"""
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from authentik.core.models import Source, UserSourceConnection
|
||||||
from authentik.core.types import UILoginButton, UserSettingSerializer
|
from authentik.core.types import UILoginButton, UserSettingSerializer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from authentik.sources.oauth.types.manager import SourceType
|
from authentik.sources.oauth.types.registry import SourceType
|
||||||
|
|
||||||
|
|
||||||
class OAuthSource(Source):
|
class OAuthSource(Source):
|
||||||
|
@ -57,9 +57,9 @@ class OAuthSource(Source):
|
||||||
@property
|
@property
|
||||||
def type(self) -> type["SourceType"]:
|
def type(self) -> type["SourceType"]:
|
||||||
"""Return the provider instance for this source"""
|
"""Return the provider instance for this source"""
|
||||||
from authentik.sources.oauth.types.manager import MANAGER
|
from authentik.sources.oauth.types.registry import registry
|
||||||
|
|
||||||
return MANAGER.find_type(self.provider_type)
|
return registry.find_type(self.provider_type)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def component(self) -> str:
|
def component(self) -> str:
|
||||||
|
|
|
@ -11,7 +11,7 @@ from structlog.stdlib import get_logger
|
||||||
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
||||||
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
||||||
from authentik.sources.oauth.models import OAuthSource
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class AppleOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class AppleType(SourceType):
|
class AppleType(SourceType):
|
||||||
"""Apple Type definition"""
|
"""Apple Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class AzureADOAuthCallback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class AzureADType(SourceType):
|
class AzureADType(SourceType):
|
||||||
"""Azure AD Type definition"""
|
"""Azure AD Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Discord OAuth Views"""
|
"""Discord OAuth Views"""
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class DiscordOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class DiscordType(SourceType):
|
class DiscordType(SourceType):
|
||||||
"""Discord Type definition"""
|
"""Discord Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any, Optional
|
||||||
from facebook import GraphAPI
|
from facebook import GraphAPI
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class FacebookOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class FacebookType(SourceType):
|
class FacebookType(SourceType):
|
||||||
"""Facebook Type definition"""
|
"""Facebook Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any, Optional
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class GitHubOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class GitHubType(SourceType):
|
class GitHubType(SourceType):
|
||||||
"""GitHub Type definition"""
|
"""GitHub Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Google OAuth Views"""
|
"""Google OAuth Views"""
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class GoogleOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class GoogleType(SourceType):
|
class GoogleType(SourceType):
|
||||||
"""Google Type definition"""
|
"""Google Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from requests.exceptions import RequestException
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class MailcowOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class MailcowType(SourceType):
|
class MailcowType(SourceType):
|
||||||
"""Mailcow Type definition"""
|
"""Mailcow Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
||||||
from authentik.sources.oauth.models import OAuthSource
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class OpenIDConnectOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class OpenIDConnectType(SourceType):
|
class OpenIDConnectType(SourceType):
|
||||||
"""OpenIDConnect Type definition"""
|
"""OpenIDConnect Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
||||||
from authentik.sources.oauth.models import OAuthSource
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class OktaOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class OktaType(SourceType):
|
class OktaType(SourceType):
|
||||||
"""Okta Type definition"""
|
"""Okta Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class RedditOAuth2Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class RedditType(SourceType):
|
class RedditType(SourceType):
|
||||||
"""Reddit Type definition"""
|
"""Reddit Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -55,13 +55,13 @@ class SourceType:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SourceTypeManager:
|
class SourceTypeRegistry:
|
||||||
"""Manager to hold all Source types."""
|
"""Registry to hold all Source types."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.__sources: list[type[SourceType]] = []
|
self.__sources: list[type[SourceType]] = []
|
||||||
|
|
||||||
def type(self):
|
def register(self):
|
||||||
"""Class decorator to register classes inline."""
|
"""Class decorator to register classes inline."""
|
||||||
|
|
||||||
def inner_wrapper(cls):
|
def inner_wrapper(cls):
|
||||||
|
@ -103,4 +103,4 @@ class SourceTypeManager:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
|
||||||
MANAGER = SourceTypeManager()
|
registry = SourceTypeRegistry()
|
|
@ -6,7 +6,7 @@ from authentik.sources.oauth.clients.oauth2 import (
|
||||||
SESSION_KEY_OAUTH_PKCE,
|
SESSION_KEY_OAUTH_PKCE,
|
||||||
UserprofileHeaderAuthClient,
|
UserprofileHeaderAuthClient,
|
||||||
)
|
)
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
from authentik.sources.oauth.views.redirect import OAuthRedirect
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class TwitterOAuthCallback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class TwitterType(SourceType):
|
class TwitterType(SourceType):
|
||||||
"""Twitter Type definition"""
|
"""Twitter Type definition"""
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from authentik.sources.oauth.types.manager import RequestKind
|
from authentik.sources.oauth.types.registry import RequestKind
|
||||||
from authentik.sources.oauth.views.dispatcher import DispatcherView
|
from authentik.sources.oauth.views.dispatcher import DispatcherView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.sources.oauth.models import OAuthSource
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, RequestKind
|
from authentik.sources.oauth.types.registry import RequestKind, registry
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -20,6 +20,6 @@ class DispatcherView(View):
|
||||||
def dispatch(self, *args, source_slug: str, **kwargs):
|
def dispatch(self, *args, source_slug: str, **kwargs):
|
||||||
"""Find Source by slug and forward request"""
|
"""Find Source by slug and forward request"""
|
||||||
source = get_object_or_404(OAuthSource, slug=source_slug)
|
source = get_object_or_404(OAuthSource, slug=source_slug)
|
||||||
view = MANAGER.find(source.provider_type, kind=RequestKind(self.kind))
|
view = registry.find(source.provider_type, kind=RequestKind(self.kind))
|
||||||
LOGGER.debug("dispatching OAuth2 request to", view=view, kind=self.kind)
|
LOGGER.debug("dispatching OAuth2 request to", view=view, kind=self.kind)
|
||||||
return view.as_view()(*args, source_slug=source_slug, **kwargs)
|
return view.as_view()(*args, source_slug=source_slug, **kwargs)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""Authentik SAML app config"""
|
"""Authentik SAML app config"""
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
|
|
||||||
class AuthentikSourceSAMLConfig(ManagedAppConfig):
|
class AuthentikSourceSAMLConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""Authenticator Static stage"""
|
"""Authenticator Static stage"""
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
|
|
||||||
class AuthentikStageAuthenticatorStaticConfig(ManagedAppConfig):
|
class AuthentikStageAuthenticatorStaticConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.template.exceptions import TemplateDoesNotExist
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.blueprints.manager import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ version: 1
|
||||||
metadata:
|
metadata:
|
||||||
name: Default - Authentication flow
|
name: Default - Authentication flow
|
||||||
entries:
|
entries:
|
||||||
|
- model: authentik_blueprints.metaapplyblueprint
|
||||||
|
attrs:
|
||||||
|
identifiers:
|
||||||
|
name: Default - Password change flow
|
||||||
|
required: false
|
||||||
- attrs:
|
- attrs:
|
||||||
designation: authentication
|
designation: authentication
|
||||||
name: Welcome to authentik!
|
name: Welcome to authentik!
|
||||||
|
|
|
@ -2,6 +2,21 @@ metadata:
|
||||||
name: Default - Tenant
|
name: Default - Tenant
|
||||||
version: 1
|
version: 1
|
||||||
entries:
|
entries:
|
||||||
|
- model: authentik_blueprints.metaapplyblueprint
|
||||||
|
attrs:
|
||||||
|
identifiers:
|
||||||
|
name: Default - Authentication flow
|
||||||
|
required: false
|
||||||
|
- model: authentik_blueprints.metaapplyblueprint
|
||||||
|
attrs:
|
||||||
|
identifiers:
|
||||||
|
name: Default - Invalidation flow
|
||||||
|
required: false
|
||||||
|
- model: authentik_blueprints.metaapplyblueprint
|
||||||
|
attrs:
|
||||||
|
identifiers:
|
||||||
|
name: Default - User settings flow
|
||||||
|
required: false
|
||||||
- attrs:
|
- attrs:
|
||||||
flow_authentication: !Find [authentik_flows.flow, [slug, default-authentication-flow]]
|
flow_authentication: !Find [authentik_flows.flow, [slug, default-authentication-flow]]
|
||||||
flow_invalidation: !Find [authentik_flows.flow, [slug, default-invalidation-flow]]
|
flow_invalidation: !Find [authentik_flows.flow, [slug, default-invalidation-flow]]
|
||||||
|
|
|
@ -41,18 +41,24 @@
|
||||||
"$id": "#entry",
|
"$id": "#entry",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"model",
|
"model"
|
||||||
"identifiers"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"model": {
|
"model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
"authentik_blueprints.blueprintinstance",
|
||||||
|
"authentik_blueprints.metaapplyblueprint",
|
||||||
|
"authentik_blueprints.metaapplyblueprint",
|
||||||
|
"authentik_core.application",
|
||||||
|
"authentik_core.group",
|
||||||
|
"authentik_core.token",
|
||||||
|
"authentik_core.user",
|
||||||
"authentik_crypto.certificatekeypair",
|
"authentik_crypto.certificatekeypair",
|
||||||
"authentik_events.event",
|
"authentik_events.event",
|
||||||
"authentik_events.notificationtransport",
|
|
||||||
"authentik_events.notification",
|
"authentik_events.notification",
|
||||||
"authentik_events.notificationrule",
|
"authentik_events.notificationrule",
|
||||||
|
"authentik_events.notificationtransport",
|
||||||
"authentik_events.notificationwebhookmapping",
|
"authentik_events.notificationwebhookmapping",
|
||||||
"authentik_flows.flow",
|
"authentik_flows.flow",
|
||||||
"authentik_flows.flowstagebinding",
|
"authentik_flows.flowstagebinding",
|
||||||
|
@ -60,25 +66,25 @@
|
||||||
"authentik_outposts.dockerserviceconnection",
|
"authentik_outposts.dockerserviceconnection",
|
||||||
"authentik_outposts.kubernetesserviceconnection",
|
"authentik_outposts.kubernetesserviceconnection",
|
||||||
"authentik_outposts.outpost",
|
"authentik_outposts.outpost",
|
||||||
|
"authentik_policies.policybinding",
|
||||||
"authentik_policies_dummy.dummypolicy",
|
"authentik_policies_dummy.dummypolicy",
|
||||||
"authentik_policies_event_matcher.eventmatcherpolicy",
|
"authentik_policies_event_matcher.eventmatcherpolicy",
|
||||||
"authentik_policies_expiry.passwordexpirypolicy",
|
"authentik_policies_expiry.passwordexpirypolicy",
|
||||||
"authentik_policies_expression.expressionpolicy",
|
"authentik_policies_expression.expressionpolicy",
|
||||||
"authentik_policies_hibp.haveibeenpwendpolicy",
|
"authentik_policies_hibp.haveibeenpwendpolicy",
|
||||||
"authentik_policies_password.passwordpolicy",
|
"authentik_policies_password.passwordpolicy",
|
||||||
"authentik_policies_reputation.reputationpolicy",
|
|
||||||
"authentik_policies_reputation.reputation",
|
"authentik_policies_reputation.reputation",
|
||||||
"authentik_policies.policybinding",
|
"authentik_policies_reputation.reputationpolicy",
|
||||||
"authentik_providers_ldap.ldapprovider",
|
"authentik_providers_ldap.ldapprovider",
|
||||||
"authentik_providers_oauth2.scopemapping",
|
|
||||||
"authentik_providers_oauth2.oauth2provider",
|
|
||||||
"authentik_providers_oauth2.authorizationcode",
|
"authentik_providers_oauth2.authorizationcode",
|
||||||
|
"authentik_providers_oauth2.oauth2provider",
|
||||||
"authentik_providers_oauth2.refreshtoken",
|
"authentik_providers_oauth2.refreshtoken",
|
||||||
|
"authentik_providers_oauth2.scopemapping",
|
||||||
"authentik_providers_proxy.proxyprovider",
|
"authentik_providers_proxy.proxyprovider",
|
||||||
"authentik_providers_saml.samlprovider",
|
|
||||||
"authentik_providers_saml.samlpropertymapping",
|
"authentik_providers_saml.samlpropertymapping",
|
||||||
"authentik_sources_ldap.ldapsource",
|
"authentik_providers_saml.samlprovider",
|
||||||
"authentik_sources_ldap.ldappropertymapping",
|
"authentik_sources_ldap.ldappropertymapping",
|
||||||
|
"authentik_sources_ldap.ldapsource",
|
||||||
"authentik_sources_oauth.oauthsource",
|
"authentik_sources_oauth.oauthsource",
|
||||||
"authentik_sources_oauth.useroauthsourceconnection",
|
"authentik_sources_oauth.useroauthsourceconnection",
|
||||||
"authentik_sources_plex.plexsource",
|
"authentik_sources_plex.plexsource",
|
||||||
|
@ -100,8 +106,8 @@
|
||||||
"authentik_stages_dummy.dummystage",
|
"authentik_stages_dummy.dummystage",
|
||||||
"authentik_stages_email.emailstage",
|
"authentik_stages_email.emailstage",
|
||||||
"authentik_stages_identification.identificationstage",
|
"authentik_stages_identification.identificationstage",
|
||||||
"authentik_stages_invitation.invitationstage",
|
|
||||||
"authentik_stages_invitation.invitation",
|
"authentik_stages_invitation.invitation",
|
||||||
|
"authentik_stages_invitation.invitationstage",
|
||||||
"authentik_stages_password.passwordstage",
|
"authentik_stages_password.passwordstage",
|
||||||
"authentik_stages_prompt.prompt",
|
"authentik_stages_prompt.prompt",
|
||||||
"authentik_stages_prompt.promptstage",
|
"authentik_stages_prompt.promptstage",
|
||||||
|
@ -109,12 +115,7 @@
|
||||||
"authentik_stages_user_login.userloginstage",
|
"authentik_stages_user_login.userloginstage",
|
||||||
"authentik_stages_user_logout.userlogoutstage",
|
"authentik_stages_user_logout.userlogoutstage",
|
||||||
"authentik_stages_user_write.userwritestage",
|
"authentik_stages_user_write.userwritestage",
|
||||||
"authentik_tenants.tenant",
|
"authentik_tenants.tenant"
|
||||||
"authentik_blueprints.blueprintinstance",
|
|
||||||
"authentik_core.group",
|
|
||||||
"authentik_core.user",
|
|
||||||
"authentik_core.application",
|
|
||||||
"authentik_core.token"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
|
@ -133,6 +134,7 @@
|
||||||
},
|
},
|
||||||
"identifiers": {
|
"identifiers": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"default": {},
|
||||||
"properties": {
|
"properties": {
|
||||||
"pk": {
|
"pk": {
|
||||||
"description": "Commonly available field, may not exist on all models",
|
"description": "Commonly available field, may not exist on all models",
|
||||||
|
|
|
@ -18,7 +18,7 @@ from authentik.core.models import User
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.lib.generators import generate_id, generate_key
|
from authentik.lib.generators import generate_id, generate_key
|
||||||
from authentik.sources.oauth.models import OAuthSource
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
from authentik.sources.oauth.types.manager import MANAGER, SourceType
|
from authentik.sources.oauth.types.registry import SourceType, registry
|
||||||
from authentik.sources.oauth.views.callback import OAuthCallback
|
from authentik.sources.oauth.views.callback import OAuthCallback
|
||||||
from authentik.stages.identification.models import IdentificationStage
|
from authentik.stages.identification.models import IdentificationStage
|
||||||
from tests.e2e.utils import SeleniumTestCase, retry
|
from tests.e2e.utils import SeleniumTestCase, retry
|
||||||
|
@ -43,7 +43,7 @@ class OAUth1Callback(OAuthCallback):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MANAGER.type()
|
@registry.register()
|
||||||
class OAUth1Type(SourceType):
|
class OAUth1Type(SourceType):
|
||||||
"""OAuth1 Type definition"""
|
"""OAuth1 Type definition"""
|
||||||
|
|
||||||
|
|
Reference in a new issue