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 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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user