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:
Jens L
2024-02-23 15:12:34 +01:00
committed by GitHub
parent 965ddcb564
commit 341d866c00
10 changed files with 52 additions and 24 deletions

View File

@ -2,6 +2,7 @@
from importlib import import_module from importlib import import_module
from inspect import ismethod from inspect import ismethod
from typing import Callable
from django.apps import AppConfig from django.apps import AppConfig
from django.db import DatabaseError, InternalError, ProgrammingError from django.db import DatabaseError, InternalError, ProgrammingError
@ -13,8 +14,8 @@ class ManagedAppConfig(AppConfig):
logger: BoundLogger logger: BoundLogger
RECONCILE_GLOBAL_PREFIX: str = "reconcile_global_" RECONCILE_GLOBAL_CATEGORY: str = "global"
RECONCILE_TENANT_PREFIX: str = "reconcile_tenant_" RECONCILE_TENANT_CATEGORY: str = "tenant"
def __init__(self, app_name: str, *args, **kwargs) -> None: def __init__(self, app_name: str, *args, **kwargs) -> None:
super().__init__(app_name, *args, **kwargs) super().__init__(app_name, *args, **kwargs)
@ -22,8 +23,8 @@ class ManagedAppConfig(AppConfig):
def ready(self) -> None: def ready(self) -> None:
self.import_related() self.import_related()
self.reconcile_global() self._reconcile_global()
self.reconcile_tenant() self._reconcile_tenant()
return super().ready() return super().ready()
def import_related(self): def import_related(self):
@ -51,7 +52,8 @@ class ManagedAppConfig(AppConfig):
meth = getattr(self, meth_name) meth = getattr(self, meth_name)
if not ismethod(meth): if not ismethod(meth):
continue continue
if not meth_name.startswith(prefix): category = getattr(meth, "_authentik_managed_reconcile", None)
if category != prefix:
continue continue
name = meth_name.replace(prefix, "") name = meth_name.replace(prefix, "")
try: try:
@ -61,7 +63,19 @@ class ManagedAppConfig(AppConfig):
except (DatabaseError, ProgrammingError, InternalError) as exc: except (DatabaseError, ProgrammingError, InternalError) as exc:
self.logger.warning("Failed to run reconcile", name=name, exc=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""" """reconcile ourselves for tenanted methods"""
from authentik.tenants.models import Tenant from authentik.tenants.models import Tenant
@ -72,9 +86,9 @@ class ManagedAppConfig(AppConfig):
return return
for tenant in tenants: for tenant in tenants:
with tenant: 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. reconcile ourselves for global methods.
Used for signals, tasks, etc. Database queries should not be made in here. 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 from django_tenants.utils import get_public_schema_name, schema_context
with schema_context(get_public_schema_name()): with schema_context(get_public_schema_name()):
self._reconcile(self.RECONCILE_GLOBAL_PREFIX) self._reconcile(self.RECONCILE_GLOBAL_CATEGORY)
class AuthentikBlueprintsConfig(ManagedAppConfig): class AuthentikBlueprintsConfig(ManagedAppConfig):
@ -93,11 +107,13 @@ class AuthentikBlueprintsConfig(ManagedAppConfig):
verbose_name = "authentik Blueprints" verbose_name = "authentik Blueprints"
default = True default = True
def reconcile_global_load_blueprints_v1_tasks(self): @ManagedAppConfig.reconcile_global
def load_blueprints_v1_tasks(self):
"""Load v1 tasks""" """Load v1 tasks"""
self.import_module("authentik.blueprints.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""" """Run blueprint discovery"""
from authentik.blueprints.v1.tasks import blueprints_discovery, clear_failed_blueprints from authentik.blueprints.v1.tasks import blueprints_discovery, clear_failed_blueprints

View File

@ -14,14 +14,16 @@ class AuthentikCoreConfig(ManagedAppConfig):
mountpoint = "" mountpoint = ""
default = True default = True
def reconcile_global_debug_worker_hook(self): @ManagedAppConfig.reconcile_global
def debug_worker_hook(self):
"""Dispatch startup tasks inline when debugging""" """Dispatch startup tasks inline when debugging"""
if settings.DEBUG: if settings.DEBUG:
from authentik.root.celery import worker_ready_hook from authentik.root.celery import worker_ready_hook
worker_ready_hook() worker_ready_hook()
def reconcile_tenant_source_inbuilt(self): @ManagedAppConfig.reconcile_tenant
def source_inbuilt(self):
"""Reconcile inbuilt source""" """Reconcile inbuilt source"""
from authentik.core.models import Source from authentik.core.models import Source

View File

@ -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""" """Ensure managed JWT certificate"""
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
@ -49,7 +50,8 @@ class AuthentikCryptoConfig(ManagedAppConfig):
): ):
self._create_update_cert() self._create_update_cert()
def reconcile_tenant_self_signed(self): @ManagedAppConfig.reconcile_tenant
def self_signed(self):
"""Create self-signed keypair""" """Create self-signed keypair"""
from authentik.crypto.builder import CertificateBuilder from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair

View File

@ -13,7 +13,8 @@ class AuthentikEnterpriseAuditConfig(EnterpriseConfig):
verbose_name = "authentik Enterprise.Audit" verbose_name = "authentik Enterprise.Audit"
default = True default = True
def reconcile_global_install_middleware(self): @EnterpriseConfig.reconcile_global
def install_middleware(self):
"""Install enterprise audit middleware""" """Install enterprise audit middleware"""
orig_import = "authentik.events.middleware.AuditMiddleware" orig_import = "authentik.events.middleware.AuditMiddleware"
new_import = "authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware" new_import = "authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware"

View File

@ -35,7 +35,8 @@ class AuthentikEventsConfig(ManagedAppConfig):
verbose_name = "authentik Events" verbose_name = "authentik Events"
default = True default = True
def reconcile_global_check_deprecations(self): @ManagedAppConfig.reconcile_global
def check_deprecations(self):
"""Check for config deprecations""" """Check for config deprecations"""
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
@ -56,7 +57,8 @@ class AuthentikEventsConfig(ManagedAppConfig):
message=msg, message=msg,
).save() ).save()
def reconcile_tenant_prefill_tasks(self): @ManagedAppConfig.reconcile_tenant
def prefill_tasks(self):
"""Prefill tasks""" """Prefill tasks"""
from authentik.events.models import SystemTask from authentik.events.models import SystemTask
from authentik.events.system_tasks import _prefill_tasks from authentik.events.system_tasks import _prefill_tasks
@ -67,7 +69,8 @@ class AuthentikEventsConfig(ManagedAppConfig):
task.save() task.save()
self.logger.debug("prefilled task", task_name=task.name) 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 """Run schedule tasks which are behind schedule (only applies
to tasks of which we keep metrics)""" to tasks of which we keep metrics)"""
from authentik.events.models import TaskStatus from authentik.events.models import TaskStatus

View File

@ -31,7 +31,8 @@ class AuthentikFlowsConfig(ManagedAppConfig):
verbose_name = "authentik Flows" verbose_name = "authentik Flows"
default = True default = True
def reconcile_global_load_stages(self): @ManagedAppConfig.reconcile_global
def load_stages(self):
"""Ensure all stages are loaded""" """Ensure all stages are loaded"""
from authentik.flows.models import Stage from authentik.flows.models import Stage

View File

@ -30,7 +30,8 @@ class AuthentikOutpostConfig(ManagedAppConfig):
verbose_name = "authentik Outpost" verbose_name = "authentik Outpost"
default = True default = True
def reconcile_tenant_embedded_outpost(self): @ManagedAppConfig.reconcile_tenant
def embedded_outpost(self):
"""Ensure embedded outpost""" """Ensure embedded outpost"""
from authentik.outposts.models import ( from authentik.outposts.models import (
DockerServiceConnection, DockerServiceConnection,

View File

@ -32,7 +32,8 @@ class AuthentikSourceOAuthConfig(ManagedAppConfig):
mountpoint = "source/oauth/" mountpoint = "source/oauth/"
default = True default = True
def reconcile_global_sources_loaded(self): @ManagedAppConfig.reconcile_global
def load_source_types(self):
"""Load source_types from config file""" """Load source_types from config file"""
for source_type in AUTHENTIK_SOURCES_OAUTH_TYPES: for source_type in AUTHENTIK_SOURCES_OAUTH_TYPES:
try: try:

View File

@ -28,7 +28,8 @@ class AuthentikTenantsConfig(ManagedAppConfig):
verbose_name = "authentik Tenants" verbose_name = "authentik Tenants"
default = True default = True
def reconcile_global_default_tenant(self): @ManagedAppConfig.reconcile_global
def default_tenant(self):
"""Make sure default tenant exists, especially after a migration""" """Make sure default tenant exists, especially after a migration"""
post_migrate.connect(ensure_default_tenant) post_migrate.connect(ensure_default_tenant)
ensure_default_tenant() ensure_default_tenant()

View File

@ -136,7 +136,7 @@ def tenant_needs_sync(sender, tenant, **kwargs):
with tenant: with tenant:
for app in apps.get_app_configs(): for app in apps.get_app_configs():
if isinstance(app, ManagedAppConfig): if isinstance(app, ManagedAppConfig):
app._reconcile(ManagedAppConfig.RECONCILE_TENANT_PREFIX) app._reconcile(ManagedAppConfig.RECONCILE_TENANT_CATEGORY)
tenant.ready = True tenant.ready = True
tenant.save() tenant.save()