blueprints: use reconcile decorator instead of relying on function name prefix (#8483)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
|
||||
from importlib import import_module
|
||||
from inspect import ismethod
|
||||
from typing import Callable
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db import DatabaseError, InternalError, ProgrammingError
|
||||
@ -13,8 +14,8 @@ class ManagedAppConfig(AppConfig):
|
||||
|
||||
logger: BoundLogger
|
||||
|
||||
RECONCILE_GLOBAL_PREFIX: str = "reconcile_global_"
|
||||
RECONCILE_TENANT_PREFIX: str = "reconcile_tenant_"
|
||||
RECONCILE_GLOBAL_CATEGORY: str = "global"
|
||||
RECONCILE_TENANT_CATEGORY: str = "tenant"
|
||||
|
||||
def __init__(self, app_name: str, *args, **kwargs) -> None:
|
||||
super().__init__(app_name, *args, **kwargs)
|
||||
@ -22,8 +23,8 @@ class ManagedAppConfig(AppConfig):
|
||||
|
||||
def ready(self) -> None:
|
||||
self.import_related()
|
||||
self.reconcile_global()
|
||||
self.reconcile_tenant()
|
||||
self._reconcile_global()
|
||||
self._reconcile_tenant()
|
||||
return super().ready()
|
||||
|
||||
def import_related(self):
|
||||
@ -51,7 +52,8 @@ class ManagedAppConfig(AppConfig):
|
||||
meth = getattr(self, meth_name)
|
||||
if not ismethod(meth):
|
||||
continue
|
||||
if not meth_name.startswith(prefix):
|
||||
category = getattr(meth, "_authentik_managed_reconcile", None)
|
||||
if category != prefix:
|
||||
continue
|
||||
name = meth_name.replace(prefix, "")
|
||||
try:
|
||||
@ -61,7 +63,19 @@ class ManagedAppConfig(AppConfig):
|
||||
except (DatabaseError, ProgrammingError, InternalError) as exc:
|
||||
self.logger.warning("Failed to run reconcile", name=name, exc=exc)
|
||||
|
||||
def reconcile_tenant(self) -> None:
|
||||
@staticmethod
|
||||
def reconcile_tenant(func: Callable):
|
||||
"""Mark a function to be called on startup (for each tenant)"""
|
||||
setattr(func, "_authentik_managed_reconcile", ManagedAppConfig.RECONCILE_TENANT_CATEGORY)
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def reconcile_global(func: Callable):
|
||||
"""Mark a function to be called on startup (globally)"""
|
||||
setattr(func, "_authentik_managed_reconcile", ManagedAppConfig.RECONCILE_GLOBAL_CATEGORY)
|
||||
return func
|
||||
|
||||
def _reconcile_tenant(self) -> None:
|
||||
"""reconcile ourselves for tenanted methods"""
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
@ -72,9 +86,9 @@ class ManagedAppConfig(AppConfig):
|
||||
return
|
||||
for tenant in tenants:
|
||||
with tenant:
|
||||
self._reconcile(self.RECONCILE_TENANT_PREFIX)
|
||||
self._reconcile(self.RECONCILE_TENANT_CATEGORY)
|
||||
|
||||
def reconcile_global(self) -> None:
|
||||
def _reconcile_global(self) -> None:
|
||||
"""
|
||||
reconcile ourselves for global methods.
|
||||
Used for signals, tasks, etc. Database queries should not be made in here.
|
||||
@ -82,7 +96,7 @@ class ManagedAppConfig(AppConfig):
|
||||
from django_tenants.utils import get_public_schema_name, schema_context
|
||||
|
||||
with schema_context(get_public_schema_name()):
|
||||
self._reconcile(self.RECONCILE_GLOBAL_PREFIX)
|
||||
self._reconcile(self.RECONCILE_GLOBAL_CATEGORY)
|
||||
|
||||
|
||||
class AuthentikBlueprintsConfig(ManagedAppConfig):
|
||||
@ -93,11 +107,13 @@ class AuthentikBlueprintsConfig(ManagedAppConfig):
|
||||
verbose_name = "authentik Blueprints"
|
||||
default = True
|
||||
|
||||
def reconcile_global_load_blueprints_v1_tasks(self):
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def load_blueprints_v1_tasks(self):
|
||||
"""Load v1 tasks"""
|
||||
self.import_module("authentik.blueprints.v1.tasks")
|
||||
|
||||
def reconcile_tenant_blueprints_discovery(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def blueprints_discovery(self):
|
||||
"""Run blueprint discovery"""
|
||||
from authentik.blueprints.v1.tasks import blueprints_discovery, clear_failed_blueprints
|
||||
|
||||
|
||||
@ -14,14 +14,16 @@ class AuthentikCoreConfig(ManagedAppConfig):
|
||||
mountpoint = ""
|
||||
default = True
|
||||
|
||||
def reconcile_global_debug_worker_hook(self):
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def debug_worker_hook(self):
|
||||
"""Dispatch startup tasks inline when debugging"""
|
||||
if settings.DEBUG:
|
||||
from authentik.root.celery import worker_ready_hook
|
||||
|
||||
worker_ready_hook()
|
||||
|
||||
def reconcile_tenant_source_inbuilt(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def source_inbuilt(self):
|
||||
"""Reconcile inbuilt source"""
|
||||
from authentik.core.models import Source
|
||||
|
||||
|
||||
@ -36,7 +36,8 @@ class AuthentikCryptoConfig(ManagedAppConfig):
|
||||
},
|
||||
)
|
||||
|
||||
def reconcile_tenant_managed_jwt_cert(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def managed_jwt_cert(self):
|
||||
"""Ensure managed JWT certificate"""
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
|
||||
@ -49,7 +50,8 @@ class AuthentikCryptoConfig(ManagedAppConfig):
|
||||
):
|
||||
self._create_update_cert()
|
||||
|
||||
def reconcile_tenant_self_signed(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def self_signed(self):
|
||||
"""Create self-signed keypair"""
|
||||
from authentik.crypto.builder import CertificateBuilder
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
|
||||
@ -13,7 +13,8 @@ class AuthentikEnterpriseAuditConfig(EnterpriseConfig):
|
||||
verbose_name = "authentik Enterprise.Audit"
|
||||
default = True
|
||||
|
||||
def reconcile_global_install_middleware(self):
|
||||
@EnterpriseConfig.reconcile_global
|
||||
def install_middleware(self):
|
||||
"""Install enterprise audit middleware"""
|
||||
orig_import = "authentik.events.middleware.AuditMiddleware"
|
||||
new_import = "authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware"
|
||||
|
||||
@ -35,7 +35,8 @@ class AuthentikEventsConfig(ManagedAppConfig):
|
||||
verbose_name = "authentik Events"
|
||||
default = True
|
||||
|
||||
def reconcile_global_check_deprecations(self):
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def check_deprecations(self):
|
||||
"""Check for config deprecations"""
|
||||
from authentik.events.models import Event, EventAction
|
||||
|
||||
@ -56,7 +57,8 @@ class AuthentikEventsConfig(ManagedAppConfig):
|
||||
message=msg,
|
||||
).save()
|
||||
|
||||
def reconcile_tenant_prefill_tasks(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def prefill_tasks(self):
|
||||
"""Prefill tasks"""
|
||||
from authentik.events.models import SystemTask
|
||||
from authentik.events.system_tasks import _prefill_tasks
|
||||
@ -67,7 +69,8 @@ class AuthentikEventsConfig(ManagedAppConfig):
|
||||
task.save()
|
||||
self.logger.debug("prefilled task", task_name=task.name)
|
||||
|
||||
def reconcile_tenant_run_scheduled_tasks(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def run_scheduled_tasks(self):
|
||||
"""Run schedule tasks which are behind schedule (only applies
|
||||
to tasks of which we keep metrics)"""
|
||||
from authentik.events.models import TaskStatus
|
||||
|
||||
@ -31,7 +31,8 @@ class AuthentikFlowsConfig(ManagedAppConfig):
|
||||
verbose_name = "authentik Flows"
|
||||
default = True
|
||||
|
||||
def reconcile_global_load_stages(self):
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def load_stages(self):
|
||||
"""Ensure all stages are loaded"""
|
||||
from authentik.flows.models import Stage
|
||||
|
||||
|
||||
@ -30,7 +30,8 @@ class AuthentikOutpostConfig(ManagedAppConfig):
|
||||
verbose_name = "authentik Outpost"
|
||||
default = True
|
||||
|
||||
def reconcile_tenant_embedded_outpost(self):
|
||||
@ManagedAppConfig.reconcile_tenant
|
||||
def embedded_outpost(self):
|
||||
"""Ensure embedded outpost"""
|
||||
from authentik.outposts.models import (
|
||||
DockerServiceConnection,
|
||||
|
||||
@ -32,7 +32,8 @@ class AuthentikSourceOAuthConfig(ManagedAppConfig):
|
||||
mountpoint = "source/oauth/"
|
||||
default = True
|
||||
|
||||
def reconcile_global_sources_loaded(self):
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def load_source_types(self):
|
||||
"""Load source_types from config file"""
|
||||
for source_type in AUTHENTIK_SOURCES_OAUTH_TYPES:
|
||||
try:
|
||||
|
||||
@ -28,7 +28,8 @@ class AuthentikTenantsConfig(ManagedAppConfig):
|
||||
verbose_name = "authentik Tenants"
|
||||
default = True
|
||||
|
||||
def reconcile_global_default_tenant(self):
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def default_tenant(self):
|
||||
"""Make sure default tenant exists, especially after a migration"""
|
||||
post_migrate.connect(ensure_default_tenant)
|
||||
ensure_default_tenant()
|
||||
|
||||
@ -136,7 +136,7 @@ def tenant_needs_sync(sender, tenant, **kwargs):
|
||||
with tenant:
|
||||
for app in apps.get_app_configs():
|
||||
if isinstance(app, ManagedAppConfig):
|
||||
app._reconcile(ManagedAppConfig.RECONCILE_TENANT_PREFIX)
|
||||
app._reconcile(ManagedAppConfig.RECONCILE_TENANT_CATEGORY)
|
||||
|
||||
tenant.ready = True
|
||||
tenant.save()
|
||||
|
||||
Reference in New Issue
Block a user