Corp/backend/guests/tasks.py

148 lines
4.8 KiB
Python

import logging
from celery import shared_task
from django.db.models import Q
from django.utils import timezone
from .constants import GuestVisitEventType, GuestVisitStatus
from .models import Guest, GuestVisit
from .services import (
GuestLdapService,
GuestVisitWorkflow,
get_guest_admin_users,
notify_guest_visit,
record_guest_event,
)
logger = logging.getLogger(__name__)
@shared_task(ignore_result=True)
def activate_due_guest_visits():
now = timezone.now()
visits = GuestVisit.objects.filter(
status=GuestVisitStatus.APPROVED,
unlimited=False,
access_starts_at__lte=now,
).filter(Q(access_expires_at__isnull=True) | Q(access_expires_at__gt=now))
for visit in visits.select_related("guest", "inviter"):
try:
GuestLdapService().sync_guest_for_visit(visit)
except Exception as error:
logger.exception(
"Guest visit activation failed",
extra={
"event": "guest.visit.activation_failed",
"guest_visit_id": visit.pk,
"guest_id": visit.guest_id,
"error_type": type(error).__name__,
},
)
@shared_task(ignore_result=True)
def expire_guest_visits():
now = timezone.now()
visits = GuestVisit.objects.filter(
status=GuestVisitStatus.APPROVED,
unlimited=False,
access_expires_at__lte=now,
)
for visit in visits.select_related("guest", "inviter"):
try:
GuestVisitWorkflow.expire(visit)
except Exception as error:
logger.exception(
"Guest visit expiration failed",
extra={
"event": "guest.visit.expiration_failed",
"guest_visit_id": visit.pk,
"guest_id": visit.guest_id,
"error_type": type(error).__name__,
},
)
@shared_task(ignore_result=True)
def detect_inactive_inviters():
visits = GuestVisit.objects.filter(
status=GuestVisitStatus.APPROVED,
inviter_inactive=False,
).select_related("guest", "inviter")
admins = list(get_guest_admin_users())
for visit in visits:
inviter = visit.inviter
if inviter.is_active and getattr(inviter, "is_actually_active", True):
continue
reason = "Приглашающий неактивен, гостевой доступ отозван автоматически."
old_status = visit.status
visit.inviter_inactive = True
visit.status = GuestVisitStatus.REVOKED
visit.revoked_at = timezone.now()
visit.revoke_reason = reason
visit.save(
update_fields=[
"inviter_inactive",
"status",
"revoked_at",
"revoke_reason",
"updated_at",
]
)
record_guest_event(
visit,
GuestVisitEventType.INVITER_INACTIVE_DETECTED,
from_status=old_status,
to_status=GuestVisitStatus.REVOKED,
comment=reason,
metadata={"inviter_id": inviter.id},
)
record_guest_event(
visit,
GuestVisitEventType.REVOKED,
from_status=old_status,
to_status=GuestVisitStatus.REVOKED,
comment=reason,
metadata={"reason": "inviter_inactive", "inviter_id": inviter.id},
)
notify_guest_visit(
visit,
"guest_inviter_inactive",
admins,
title="Приглашающий гостя неактивен",
message=reason,
)
try:
GuestLdapService().sync_guest_for_visit(visit)
except Exception as error:
logger.exception(
"Guest LDAP sync failed after inactive inviter revocation",
extra={
"event": "guest.ldap_sync.after_revocation_failed",
"guest_visit_id": visit.pk,
"guest_id": visit.guest_id,
"inviter_id": inviter.id,
"error_type": type(error).__name__,
},
)
def execute_guest_queue_operation(operation: str, payload: dict) -> None:
guest_id = payload.get("guest_id") or payload.get("object_pk")
guest = Guest.objects.get(pk=guest_id)
service = GuestLdapService()
if operation in {"guest_sync", "guest_disable"}:
service.sync_guest(
guest,
enqueue_on_error=False,
raise_on_error=True,
)
elif operation == "guest_delete":
service.disable_guest(
guest,
enqueue_on_error=False,
raise_on_error=True,
)
else:
raise ValueError(f"Unknown guest LDAP operation: {operation}")