diff --git a/authentik/admin/tasks.py b/authentik/admin/tasks.py index 5834b9ff2e..e16dea091e 100644 --- a/authentik/admin/tasks.py +++ b/authentik/admin/tasks.py @@ -33,10 +33,10 @@ def _set_prom_info(): ) -@actor +@actor(description=_("Update latest version info")) def update_latest_version(): - """Update latest version info""" self: Task = CurrentTask.get_task() + raise RuntimeError("whatever") if CONFIG.get_bool("disable_update_check"): cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT) self.info("Version check disabled.") diff --git a/authentik/blueprints/v1/tasks.py b/authentik/blueprints/v1/tasks.py index cfa21e5017..2b1340984c 100644 --- a/authentik/blueprints/v1/tasks.py +++ b/authentik/blueprints/v1/tasks.py @@ -1,5 +1,7 @@ """v1 blueprints tasks""" +from django.utils.translation import gettext_lazy as _ + from dataclasses import asdict, dataclass, field from hashlib import sha512 from pathlib import Path @@ -106,10 +108,10 @@ class BlueprintEventHandler(FileSystemEventHandler): @actor( + description=_("Find blueprints as `blueprints_find` does, but return a safe dict"), throws=(DatabaseError, ProgrammingError, InternalError), ) def blueprints_find_dict(): - """Find blueprints as `blueprints_find` does, but return a safe dict""" blueprints = [] for blueprint in blueprints_find(): blueprints.append(sanitize_dict(asdict(blueprint))) @@ -145,9 +147,11 @@ def blueprints_find() -> list[BlueprintFile]: return blueprints -@actor(throws=(DatabaseError, ProgrammingError, InternalError)) +@actor( + description=_("Find blueprints and check if they need to be created in the database"), + throws=(DatabaseError, ProgrammingError, InternalError), +) def blueprints_discovery(path: str | None = None): - """Find blueprints and check if they need to be created in the database""" self: Task = CurrentTask.get_task() count = 0 for blueprint in blueprints_find(): @@ -185,9 +189,8 @@ def check_blueprint_v1_file(blueprint: BlueprintFile): apply_blueprint.send_with_options(args=(instance.pk,), rel_obj=instance) -@actor +@actor(description=_("Apply single blueprint")) def apply_blueprint(instance_pk: UUID): - """Apply single blueprint""" self: Task = CurrentTask.get_task() self.set_uid(str(instance_pk)) instance: BlueprintInstance | None = None @@ -237,9 +240,8 @@ def apply_blueprint(instance_pk: UUID): instance.save() -@actor +@actor(description=_("Remove blueprints which couldn't be fetched")) def clear_failed_blueprints(): - """Remove blueprints which couldn't be fetched""" # Exclude OCI blueprints as those might be temporarily unavailable for blueprint in BlueprintInstance.objects.exclude(path__startswith=OCI_PREFIX): try: diff --git a/authentik/core/tasks.py b/authentik/core/tasks.py index ed43b8dbd6..8f2ed520a7 100644 --- a/authentik/core/tasks.py +++ b/authentik/core/tasks.py @@ -1,5 +1,7 @@ """authentik core tasks""" +from django.utils.translation import gettext_lazy as _ + from datetime import datetime, timedelta from django.utils.timezone import now @@ -18,9 +20,8 @@ from authentik.tasks.models import Task LOGGER = get_logger() -@actor +@actor(description=_("Remove expired objects")) def clean_expired_models(): - """Remove expired objects""" self: Task = CurrentTask.get_task() for cls in ExpiringModel.__subclasses__(): cls: ExpiringModel @@ -34,9 +35,8 @@ def clean_expired_models(): self.info(f"Expired {amount} {cls._meta.verbose_name_plural}") -@actor +@actor(description=_("Remove temporary users created by SAML Sources")) def clean_temporary_users(): - """Remove temporary users created by SAML Sources""" self: Task = CurrentTask.get_task() _now = datetime.now() deleted_users = 0 diff --git a/authentik/crypto/tasks.py b/authentik/crypto/tasks.py index 84be597b22..92fb7e33df 100644 --- a/authentik/crypto/tasks.py +++ b/authentik/crypto/tasks.py @@ -1,5 +1,7 @@ """Crypto tasks""" +from django.utils.translation import gettext_lazy as _ + from glob import glob from pathlib import Path @@ -35,9 +37,8 @@ def ensure_certificate_valid(body: str): return body -@actor +@actor(description=_("Discover, import and update certificates from the filesystem")) def certificate_discovery(): - """Discover, import and update certificates from the filesystem""" self: Task = CurrentTask.get_task() certs = {} private_keys = {} diff --git a/authentik/enterprise/policies/unique_password/tasks.py b/authentik/enterprise/policies/unique_password/tasks.py index e0ac67cd6f..45bb71aa84 100644 --- a/authentik/enterprise/policies/unique_password/tasks.py +++ b/authentik/enterprise/policies/unique_password/tasks.py @@ -2,6 +2,7 @@ from django.db.models.aggregates import Count from django_dramatiq_postgres.middleware import CurrentTask from dramatiq.actor import actor from structlog import get_logger +from django.utils.translation import gettext_lazy as _ from authentik.enterprise.policies.unique_password.models import ( UniquePasswordPolicy, @@ -12,11 +13,12 @@ from authentik.tasks.models import Task LOGGER = get_logger() -@actor +@actor( + description=_( + "Check if any UniquePasswordPolicy exists, and if not, purge the password history table." + ) +) def check_and_purge_password_history(): - """Check if any UniquePasswordPolicy exists, and if not, purge the password history table. - This is run on a schedule instead of being triggered by policy binding deletion. - """ self: Task = CurrentTask.get_task() if not UniquePasswordPolicy.objects.exists(): @@ -28,10 +30,8 @@ def check_and_purge_password_history(): self.info("Not purging password histories, a unique password policy exists") -@actor +@actor(description=_("Remove user password history that are too old.")) def trim_password_histories(): - self: Task = CurrentTask.get_task() - """Removes rows from UserPasswordHistory older than the `n` most recent entries. @@ -39,6 +39,8 @@ def trim_password_histories(): UniquePasswordPolicy policies. """ + self: Task = CurrentTask.get_task() + # No policy, we'll let the cleanup above do its thing if not UniquePasswordPolicy.objects.exists(): return diff --git a/authentik/enterprise/providers/google_workspace/tasks.py b/authentik/enterprise/providers/google_workspace/tasks.py index 48bd31ed4f..30afaa3c15 100644 --- a/authentik/enterprise/providers/google_workspace/tasks.py +++ b/authentik/enterprise/providers/google_workspace/tasks.py @@ -4,26 +4,27 @@ from dramatiq.actor import actor from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider from authentik.lib.sync.outgoing.tasks import SyncTasks +from django.utils.translation import gettext_lazy as _ sync_tasks = SyncTasks(GoogleWorkspaceProvider) -@actor +@actor(description=_("Sync Google Workspace provider objects.")) def google_workspace_sync_objects(*args, **kwargs): return sync_tasks.sync_objects(*args, **kwargs) -@actor +@actor(description=_("Full sync for Google Workspace provider.")) def google_workspace_sync(provider_pk: int, *args, **kwargs): """Run full sync for Google Workspace provider""" return sync_tasks.sync(provider_pk, google_workspace_sync_objects) -@actor +@actor(description=_("Sync a direct object (user, group) for Google Workspace provider.")) def google_workspace_sync_direct(*args, **kwargs): return sync_tasks.sync_signal_direct(*args, **kwargs) -@actor +@actor(description=_("Sync a related object (memberships) for Google Workspace provider.")) def google_workspace_sync_m2m(*args, **kwargs): return sync_tasks.sync_signal_m2m(*args, **kwargs) diff --git a/authentik/enterprise/providers/microsoft_entra/tasks.py b/authentik/enterprise/providers/microsoft_entra/tasks.py index 2e4cbbf379..140a942226 100644 --- a/authentik/enterprise/providers/microsoft_entra/tasks.py +++ b/authentik/enterprise/providers/microsoft_entra/tasks.py @@ -4,26 +4,27 @@ from dramatiq.actor import actor from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider from authentik.lib.sync.outgoing.tasks import SyncTasks +from django.utils.translation import gettext_lazy as _ sync_tasks = SyncTasks(MicrosoftEntraProvider) -@actor +@actor(description=_("Sync Microsoft Entra provider objects.")) def microsoft_entra_sync_objects(*args, **kwargs): return sync_tasks.sync_objects(*args, **kwargs) -@actor +@actor(description=_("Full sync for Microsoft Entra provider.")) def microsoft_entra_sync(provider_pk: int, *args, **kwargs): """Run full sync for Microsoft Entra provider""" return sync_tasks.sync(provider_pk, microsoft_entra_sync_objects) -@actor +@actor(description=_("Sync a direct object (user, group) for Microsoft Entra provider.")) def microsoft_entra_sync_direct(*args, **kwargs): return sync_tasks.sync_signal_direct(*args, **kwargs) -@actor +@actor(description=_("Sync a related object (memberships) for Microsoft Entra provider.")) def microsoft_entra_sync_m2m(*args, **kwargs): return sync_tasks.sync_signal_m2m(*args, **kwargs) diff --git a/authentik/enterprise/providers/ssf/tasks.py b/authentik/enterprise/providers/ssf/tasks.py index d12a8ba36b..efedac9b94 100644 --- a/authentik/enterprise/providers/ssf/tasks.py +++ b/authentik/enterprise/providers/ssf/tasks.py @@ -1,4 +1,5 @@ from typing import Any +from django.utils.translation import gettext_lazy as _ from uuid import UUID from django.http import HttpRequest @@ -61,7 +62,7 @@ def _check_app_access(stream: Stream, event_data: dict) -> bool: return engine.passing -@actor +@actor(description=_("Send an SSF event")) def _send_ssf_event(stream_uuid: UUID, event_data: dict[str, Any]): self: Task = CurrentTask.get_task() diff --git a/authentik/enterprise/tasks.py b/authentik/enterprise/tasks.py index 43b19ac047..c429959671 100644 --- a/authentik/enterprise/tasks.py +++ b/authentik/enterprise/tasks.py @@ -1,11 +1,12 @@ """Enterprise tasks""" +from django.utils.translation import gettext_lazy as _ + from dramatiq.actor import actor from authentik.enterprise.license import LicenseKey -@actor +@actor(description=_("Update enterprise license status.")) def enterprise_update_usage(): - """Update enterprise license status""" LicenseKey.get_total().record_usage() diff --git a/authentik/events/tasks.py b/authentik/events/tasks.py index b80d99dbae..b960633950 100644 --- a/authentik/events/tasks.py +++ b/authentik/events/tasks.py @@ -2,6 +2,7 @@ from uuid import UUID +from django.utils.translation import gettext_lazy as _ from django.db.models.query_utils import Q from django_dramatiq_postgres.middleware import CurrentTask from dramatiq.actor import actor @@ -22,7 +23,7 @@ from authentik.tasks.models import Task LOGGER = get_logger() -@actor +@actor(description=_("Check if policies attached to NotificationRule match event")) def event_trigger_handler(event_uuid: UUID, trigger_name: str): """Check if policies attached to NotificationRule match event""" event: Event = Event.objects.filter(event_uuid=event_uuid).first() @@ -79,7 +80,7 @@ def event_trigger_handler(event_uuid: UUID, trigger_name: str): break -@actor +@actor(description=_("Send notification")) def notification_transport(transport_pk: int, event_pk: str, user_pk: int, trigger_pk: str): """Send notification over specified transport""" event = Event.objects.filter(pk=event_pk).first() @@ -100,7 +101,7 @@ def notification_transport(transport_pk: int, event_pk: str, user_pk: int, trigg transport.send(notification) -@actor +@actor(description=_("Cleanup events for GDPR compliance")) def gdpr_cleanup(user_pk: int): """cleanup events from gdpr_compliance""" events = Event.objects.filter(user__pk=user_pk) diff --git a/authentik/outposts/tasks.py b/authentik/outposts/tasks.py index 17ef324edf..b86186ad37 100644 --- a/authentik/outposts/tasks.py +++ b/authentik/outposts/tasks.py @@ -1,5 +1,7 @@ """outpost tasks""" +from django.utils.translation import gettext_lazy as _ + from hashlib import sha256 from os import R_OK, access from pathlib import Path @@ -82,7 +84,7 @@ def controller_for_outpost(outpost: Outpost) -> type[BaseController] | None: return None -@actor +@actor(description=_("Update cached state of a service connection")) def outpost_service_connection_monitor(connection_pk: Any): """Update cached state of a service connection""" connection: OutpostServiceConnection = ( @@ -107,7 +109,7 @@ def outpost_service_connection_monitor(connection_pk: Any): cache.set(connection.state_key, state, timeout=None) -@actor +@actor(description=_("Create/update/monitor/delete the deployment of an Outpost")) def outpost_controller(outpost_pk: str, action: str = "up", from_cache: bool = False): """Create/update/monitor/delete the deployment of an Outpost""" self: Task = CurrentTask.get_task() @@ -140,7 +142,7 @@ def outpost_controller(outpost_pk: str, action: str = "up", from_cache: bool = F self.info(log) -@actor +@actor(description=_("Ensure that all Outposts have valid Service Accounts and Tokens")) def outpost_token_ensurer(): """ Periodically ensure that all Outposts have valid Service Accounts and Tokens @@ -153,7 +155,7 @@ def outpost_token_ensurer(): self.info(f"Successfully checked {len(all_outposts)} Outposts.") -@actor +@actor(description=_("If an Outpost is saved, ensure that token is created/updated")) def outpost_post_save(model_class: str, model_pk: Any): """If an Outpost is saved, Ensure that token is created/updated @@ -225,7 +227,7 @@ def _outpost_single_update(outpost: Outpost, layer=None): async_to_sync(layer.group_send)(group, {"type": "event.update"}) -@actor +@actor(description=_("Checks the local environment and create Service connections.")) def outpost_connection_discovery(): """Checks the local environment and create Service connections.""" self: Task = CurrentTask.get_task() @@ -266,9 +268,8 @@ def outpost_connection_discovery(): ) -@actor +@actor(description=_("Terminate session on all outposts")) def outpost_session_end(session_id: str): - """Update outpost instances connected to a single outpost""" layer = get_channel_layer() hashed_session_id = hash_session_key(session_id) for outpost in Outpost.objects.all(): diff --git a/authentik/providers/proxy/tasks.py b/authentik/providers/proxy/tasks.py index f742be2e48..6624c50999 100644 --- a/authentik/providers/proxy/tasks.py +++ b/authentik/providers/proxy/tasks.py @@ -7,11 +7,11 @@ from dramatiq.actor import actor from authentik.outposts.consumer import OUTPOST_GROUP from authentik.outposts.models import Outpost, OutpostType from authentik.providers.oauth2.id_token import hash_session_key +from django.utils.translation import gettext_lazy as _ -@actor +@actor(description=_("Terminate session on Proxy outpost")) def proxy_on_logout(session_id: str): - """Update outpost instances connected to a single outpost""" layer = get_channel_layer() hashed_session_id = hash_session_key(session_id) for outpost in Outpost.objects.filter(type=OutpostType.PROXY): diff --git a/authentik/providers/scim/tasks.py b/authentik/providers/scim/tasks.py index 3df69c3b2a..063f801fed 100644 --- a/authentik/providers/scim/tasks.py +++ b/authentik/providers/scim/tasks.py @@ -4,26 +4,27 @@ from dramatiq.actor import actor from authentik.lib.sync.outgoing.tasks import SyncTasks from authentik.providers.scim.models import SCIMProvider +from django.utils.translation import gettext_lazy as _ sync_tasks = SyncTasks(SCIMProvider) -@actor +@actor(description=_("Sync SCIM provider objects.")) def scim_sync_objects(*args, **kwargs): return sync_tasks.sync_objects(*args, **kwargs) -@actor +@actor(description=_("Full sync for SCIM provider.")) def scim_sync(provider_pk: int, *args, **kwargs): """Run full sync for SCIM provider""" return sync_tasks.sync(provider_pk, scim_sync_objects) -@actor +@actor(description=_("Sync a direct object (user, group) for SCIM provider.")) def scim_sync_direct(*args, **kwargs): return sync_tasks.sync_signal_direct(*args, **kwargs) -@actor +@actor(description=_("Sync a related object (memberships) for SCIM provider.")) def scim_sync_m2m(*args, **kwargs): return sync_tasks.sync_signal_m2m(*args, **kwargs) diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 4657edfab8..90ec641d78 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -360,8 +360,8 @@ DRAMATIQ = { "task_model": "authentik.tasks.models.Task", "task_purge_interval": timedelta_from_string( CONFIG.get("worker.task_purge_interval") - ).total_seconds, - "task_expiration": timedelta_from_string(CONFIG.get("worker.task_expiration")).total_seconds, + ).total_seconds(), + "task_expiration": timedelta_from_string(CONFIG.get("worker.task_expiration")).total_seconds(), "autodiscovery": { "enabled": True, "setup_module": "authentik.tasks.setup", @@ -372,7 +372,7 @@ DRAMATIQ = { "threads": CONFIG.get_int("worker.threads", 1), "consumer_listen_timeout": timedelta_from_string( CONFIG.get("worker.consumer_listen_timeout") - ), + ).total_seconds(), }, "scheduler_class": "authentik.tasks.schedules.scheduler.Scheduler", "schedule_model": "authentik.tasks.schedules.models.Schedule", @@ -400,6 +400,7 @@ DRAMATIQ = { ("authentik.tasks.middleware.TenantMiddleware", {}), ("authentik.tasks.middleware.RelObjMiddleware", {}), ("authentik.tasks.middleware.LoggingMiddleware", {}), + ("authentik.tasks.middleware.DescriptionMiddleware", {}), ), "test": TEST, } diff --git a/authentik/sources/kerberos/tasks.py b/authentik/sources/kerberos/tasks.py index 698bb05ac3..8d177a0901 100644 --- a/authentik/sources/kerberos/tasks.py +++ b/authentik/sources/kerberos/tasks.py @@ -5,6 +5,7 @@ from django_dramatiq_postgres.middleware import CurrentTask from dramatiq.actor import actor from structlog.stdlib import get_logger +from django.utils.translation import gettext_lazy as _ from authentik.lib.config import CONFIG from authentik.lib.sync.outgoing.exceptions import StopSync from authentik.lib.utils.errors import exception_to_string @@ -16,7 +17,7 @@ LOGGER = get_logger() CACHE_KEY_STATUS = "goauthentik.io/sources/kerberos/status/" -@actor +@actor(description=_("Check connectivity for Kerberos sources")) def kerberos_connectivity_check(pk: str): """Check connectivity for Kerberos Sources""" # 2 hour timeout, this task should run every hour @@ -28,9 +29,11 @@ def kerberos_connectivity_check(pk: str): cache.set(CACHE_KEY_STATUS + source.slug, status, timeout=timeout) -@actor(time_limit=(60 * 60 * CONFIG.get_int("sources.kerberos.task_timeout_hours")) * 2.5 * 1000) +@actor( + time_limit=(60 * 60 * CONFIG.get_int("sources.kerberos.task_timeout_hours")) * 2.5 * 1000, + description=_("Sync Kerberos source"), +) def kerberos_sync(pk: str): - """Sync a single source""" self: Task = CurrentTask.get_task() source: KerberosSource = KerberosSource.objects.filter(enabled=True, pk=pk).first() if not source: diff --git a/authentik/sources/ldap/tasks.py b/authentik/sources/ldap/tasks.py index 627cb8796e..b08f46fd7d 100644 --- a/authentik/sources/ldap/tasks.py +++ b/authentik/sources/ldap/tasks.py @@ -9,6 +9,7 @@ from dramatiq.composition import group from dramatiq.message import Message from ldap3.core.exceptions import LDAPException from structlog.stdlib import get_logger +from django.utils.translation import gettext_lazy as _ from authentik.lib.config import CONFIG from authentik.lib.sync.outgoing.exceptions import StopSync @@ -33,7 +34,7 @@ CACHE_KEY_PREFIX = "goauthentik.io/sources/ldap/page/" CACHE_KEY_STATUS = "goauthentik.io/sources/ldap/status/" -@actor +@actor(description=_("Check connectivity for LDAP sources")) def ldap_connectivity_check(pk: str | None = None): """Check connectivity for LDAP Sources""" timeout = 60 * 60 * 2 @@ -48,8 +49,8 @@ def ldap_connectivity_check(pk: str | None = None): # We take the configured hours timeout time by 3.5 as we run user and # group in parallel and then membership, then deletions, so 3x is to cover the serial tasks, # and 0.5x on top of that to give some more leeway - time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000) - * 3.5, + time_limit=(60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000) * 3.5, + description=_("Sync LDAP source"), ) def ldap_sync(source_pk: str): """Sync a single source""" @@ -116,7 +117,10 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) -> return messages -@actor(time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000) +@actor( + time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours") * 1000, + description=_("Sync page for LDAP source"), +) def ldap_sync_page(source_pk: str, sync_class: str, page_cache_key: str): """Synchronization of an LDAP Source""" self: Task = CurrentTask.get_task() diff --git a/authentik/sources/oauth/tasks.py b/authentik/sources/oauth/tasks.py index 4c233470c2..8459b93840 100644 --- a/authentik/sources/oauth/tasks.py +++ b/authentik/sources/oauth/tasks.py @@ -6,6 +6,7 @@ from django_dramatiq_postgres.middleware import CurrentTask from dramatiq.actor import actor from requests import RequestException from structlog.stdlib import get_logger +from django.utils.translation import gettext_lazy as _ from authentik.lib.utils.http import get_http_session from authentik.sources.oauth.models import OAuthSource @@ -14,9 +15,12 @@ from authentik.tasks.models import Task LOGGER = get_logger() -@actor +@actor( + description=_( + "Update OAuth sources' config from well_known, and JWKS info from the configured URL" + ) +) def update_well_known_jwks(): - """Update OAuth sources' config from well_known, and JWKS info from the configured URL""" self: Task = CurrentTask.get_task() session = get_http_session() for source in OAuthSource.objects.all().exclude(oidc_well_known_url=""): diff --git a/authentik/sources/plex/tasks.py b/authentik/sources/plex/tasks.py index 11bb34e57e..0ed425ec89 100644 --- a/authentik/sources/plex/tasks.py +++ b/authentik/sources/plex/tasks.py @@ -9,9 +9,10 @@ from authentik.lib.utils.errors import exception_to_string from authentik.sources.plex.models import PlexSource from authentik.sources.plex.plex import PlexAuth from authentik.tasks.models import Task +from django.utils.translation import gettext_lazy as _ -@actor +@actor(description=_("Check the validity of a Plex source")) def check_plex_token(source_pk: str): """Check the validity of a Plex source.""" self: Task = CurrentTask.get_task() diff --git a/authentik/stages/authenticator_webauthn/tasks.py b/authentik/stages/authenticator_webauthn/tasks.py index ddc2ec91fd..f62a568e21 100644 --- a/authentik/stages/authenticator_webauthn/tasks.py +++ b/authentik/stages/authenticator_webauthn/tasks.py @@ -9,6 +9,7 @@ from django.db.transaction import atomic from django_dramatiq_postgres.middleware import CurrentTask from dramatiq.actor import actor from fido2.mds3 import filter_revoked, parse_blob +from django.utils.translation import gettext_lazy as _ from authentik.stages.authenticator_webauthn.models import ( UNKNOWN_DEVICE_TYPE_AAGUID, @@ -29,7 +30,7 @@ def mds_ca() -> bytes: return _raw_root.read() -@actor +@actor(description=_("Background task to import FIDO Alliance MDS blob and AAGUIDs into database")) def webauthn_mds_import(force=False): """Background task to import FIDO Alliance MDS blob and AAGUIDs into database""" self: Task = CurrentTask.get_task() diff --git a/authentik/stages/email/tasks.py b/authentik/stages/email/tasks.py index 2bfd831748..cf8473d4ae 100644 --- a/authentik/stages/email/tasks.py +++ b/authentik/stages/email/tasks.py @@ -6,6 +6,7 @@ from typing import Any from django.core.mail import EmailMultiAlternatives from django.core.mail.utils import DNS_NAME from django.utils.text import slugify +from django.utils.translation import gettext_lazy as _ from django_dramatiq_postgres.middleware import CurrentTask from dramatiq.actor import actor from dramatiq.composition import group @@ -48,7 +49,7 @@ def get_email_body(email: EmailMultiAlternatives) -> str: return email.body -@actor +@actor(description=_("Send email")) def send_mail( message: dict[Any, Any], stage_class_path: str | None = None, diff --git a/authentik/tasks/middleware.py b/authentik/tasks/middleware.py index de58a3fed7..0d4ed99854 100644 --- a/authentik/tasks/middleware.py +++ b/authentik/tasks/middleware.py @@ -72,3 +72,9 @@ class LoggingMiddleware(Middleware): message=f"Task {task.actor_name} encountered an error: " "{exception_to_string(exception)}", ).save() + + +class DescriptionMiddleware(Middleware): + @property + def actor_options(self): + return {"description"} diff --git a/authentik/tasks/schedules/api.py b/authentik/tasks/schedules/api.py index 233b3c9c3b..8c5658e8e7 100644 --- a/authentik/tasks/schedules/api.py +++ b/authentik/tasks/schedules/api.py @@ -52,9 +52,9 @@ class ScheduleSerializer(ModelSerializer): actor: Actor = get_broker().get_actor(instance.actor_name) except ActorNotFound: return "FIXME this shouldn't happen" - if not actor.fn.__doc__: + if "description" not in actor.options: return "no doc" - return actor.fn.__doc__.strip() + return actor.options["description"] class ScheduleFilter(FilterSet):