migrate outpost to soft-delete
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		| @ -3,11 +3,14 @@ | |||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.db.models.signals import post_delete, pre_delete | from django.dispatch import Signal | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
| from model_utils.managers import InheritanceManager | from model_utils.managers import InheritanceManager | ||||||
| from rest_framework.serializers import BaseSerializer | from rest_framework.serializers import BaseSerializer | ||||||
|  |  | ||||||
|  | pre_soft_delete = Signal() | ||||||
|  | post_soft_delete = Signal() | ||||||
|  |  | ||||||
|  |  | ||||||
| class SerializerModel(models.Model): | class SerializerModel(models.Model): | ||||||
|     """Base Abstract Model which has a serializer""" |     """Base Abstract Model which has a serializer""" | ||||||
| @ -83,7 +86,7 @@ class SoftDeleteModel(models.Model): | |||||||
|         return self.deleted_at is not None |         return self.deleted_at is not None | ||||||
|  |  | ||||||
|     def delete(self, using: Any = ..., keep_parents: bool = ...) -> tuple[int, dict[str, int]]: |     def delete(self, using: Any = ..., keep_parents: bool = ...) -> tuple[int, dict[str, int]]: | ||||||
|         pre_delete.send(sender=self.__class__, instance=self) |         pre_soft_delete.send(sender=self.__class__, instance=self) | ||||||
|         now = timezone.now() |         now = timezone.now() | ||||||
|         self.deleted_at = now |         self.deleted_at = now | ||||||
|         self.save( |         self.save( | ||||||
| @ -91,7 +94,7 @@ class SoftDeleteModel(models.Model): | |||||||
|                 "deleted_at", |                 "deleted_at", | ||||||
|             ] |             ] | ||||||
|         ) |         ) | ||||||
|         post_delete.send(sender=self.__class__, instance=self) |         post_soft_delete.send(sender=self.__class__, instance=self) | ||||||
|         return tuple() |         return tuple() | ||||||
|  |  | ||||||
|     def force_delete(self, using: Any = ...): |     def force_delete(self, using: Any = ...): | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								authentik/outposts/migrations/0022_outpost_deleted_at.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								authentik/outposts/migrations/0022_outpost_deleted_at.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | # Generated by Django 5.0.4 on 2024-04-23 21:00 | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("authentik_outposts", "0021_alter_outpost_type"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name="outpost", | ||||||
|  |             name="deleted_at", | ||||||
|  |             field=models.DateTimeField(blank=True, null=True), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -33,7 +33,7 @@ from authentik.core.models import ( | |||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
| from authentik.events.models import Event, EventAction | from authentik.events.models import Event, EventAction | ||||||
| from authentik.lib.config import CONFIG | from authentik.lib.config import CONFIG | ||||||
| from authentik.lib.models import InheritanceForeignKey, SerializerModel | from authentik.lib.models import InheritanceForeignKey, SerializerModel, SoftDeleteModel | ||||||
| from authentik.lib.sentry import SentryIgnoredException | from authentik.lib.sentry import SentryIgnoredException | ||||||
| from authentik.lib.utils.errors import exception_to_string | from authentik.lib.utils.errors import exception_to_string | ||||||
| from authentik.outposts.controllers.k8s.utils import get_namespace | from authentik.outposts.controllers.k8s.utils import get_namespace | ||||||
| @ -131,7 +131,7 @@ class OutpostServiceConnection(models.Model): | |||||||
|         verbose_name = _("Outpost Service-Connection") |         verbose_name = _("Outpost Service-Connection") | ||||||
|         verbose_name_plural = _("Outpost Service-Connections") |         verbose_name_plural = _("Outpost Service-Connections") | ||||||
|  |  | ||||||
|     def __str__(self) -> __version__: |     def __str__(self): | ||||||
|         return f"Outpost service connection {self.name}" |         return f"Outpost service connection {self.name}" | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @ -241,7 +241,7 @@ class KubernetesServiceConnection(SerializerModel, OutpostServiceConnection): | |||||||
|         return "ak-service-connection-kubernetes-form" |         return "ak-service-connection-kubernetes-form" | ||||||
|  |  | ||||||
|  |  | ||||||
| class Outpost(SerializerModel, ManagedModel): | class Outpost(SoftDeleteModel, SerializerModel, ManagedModel): | ||||||
|     """Outpost instance which manages a service user and token""" |     """Outpost instance which manages a service user and token""" | ||||||
|  |  | ||||||
|     uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True) |     uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True) | ||||||
|  | |||||||
| @ -2,13 +2,14 @@ | |||||||
|  |  | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.db.models import Model | from django.db.models import Model | ||||||
| from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save | from django.db.models.signals import m2m_changed, post_save, pre_save | ||||||
| from django.dispatch import receiver | from django.dispatch import receiver | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.brands.models import Brand | from authentik.brands.models import Brand | ||||||
| from authentik.core.models import Provider | from authentik.core.models import Provider | ||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
|  | from authentik.lib.models import post_soft_delete | ||||||
| from authentik.lib.utils.reflection import class_to_path | from authentik.lib.utils.reflection import class_to_path | ||||||
| from authentik.outposts.models import Outpost, OutpostServiceConnection | from authentik.outposts.models import Outpost, OutpostServiceConnection | ||||||
| from authentik.outposts.tasks import CACHE_KEY_OUTPOST_DOWN, outpost_controller, outpost_post_save | from authentik.outposts.tasks import CACHE_KEY_OUTPOST_DOWN, outpost_controller, outpost_post_save | ||||||
| @ -67,9 +68,7 @@ def post_save_update(sender, instance: Model, created: bool, **_): | |||||||
|     outpost_post_save.delay(class_to_path(instance.__class__), instance.pk) |     outpost_post_save.delay(class_to_path(instance.__class__), instance.pk) | ||||||
|  |  | ||||||
|  |  | ||||||
| @receiver(pre_delete, sender=Outpost) | @receiver(post_soft_delete, sender=Outpost) | ||||||
| def pre_delete_cleanup(sender, instance: Outpost, **_): | def outpost_cleanup(sender, instance: Outpost, **_): | ||||||
|     """Ensure that Outpost's user is deleted (which will delete the token through cascade)""" |     """Ensure that Outpost's user is deleted (which will delete the token through cascade)""" | ||||||
|     instance.user.delete() |     outpost_controller.delay(instance.pk.hex, action="down") | ||||||
|     cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, instance) |  | ||||||
|     outpost_controller.delay(instance.pk.hex, action="down", from_cache=True) |  | ||||||
|  | |||||||
| @ -129,17 +129,10 @@ def outpost_controller_all(): | |||||||
|  |  | ||||||
|  |  | ||||||
| @CELERY_APP.task(bind=True, base=SystemTask) | @CELERY_APP.task(bind=True, base=SystemTask) | ||||||
| def outpost_controller( | def outpost_controller(self: SystemTask, outpost_pk: str, action: str = "up"): | ||||||
|     self: SystemTask, outpost_pk: str, action: str = "up", from_cache: bool = False |  | ||||||
| ): |  | ||||||
|     """Create/update/monitor/delete the deployment of an Outpost""" |     """Create/update/monitor/delete the deployment of an Outpost""" | ||||||
|     logs = [] |     logs = [] | ||||||
|     if from_cache: |     outpost: Outpost = Outpost.objects.filter(pk=outpost_pk).first() | ||||||
|         outpost: Outpost = cache.get(CACHE_KEY_OUTPOST_DOWN % outpost_pk) |  | ||||||
|         LOGGER.debug("Getting outpost from cache to delete") |  | ||||||
|     else: |  | ||||||
|         outpost: Outpost = Outpost.objects.filter(pk=outpost_pk).first() |  | ||||||
|         LOGGER.debug("Getting outpost from DB") |  | ||||||
|     if not outpost: |     if not outpost: | ||||||
|         LOGGER.warning("No outpost") |         LOGGER.warning("No outpost") | ||||||
|         return |         return | ||||||
| @ -155,9 +148,10 @@ def outpost_controller( | |||||||
|     except (ControllerException, ServiceConnectionInvalid) as exc: |     except (ControllerException, ServiceConnectionInvalid) as exc: | ||||||
|         self.set_error(exc) |         self.set_error(exc) | ||||||
|     else: |     else: | ||||||
|         if from_cache: |  | ||||||
|             cache.delete(CACHE_KEY_OUTPOST_DOWN % outpost_pk) |  | ||||||
|         self.set_status(TaskStatus.SUCCESSFUL, *logs) |         self.set_status(TaskStatus.SUCCESSFUL, *logs) | ||||||
|  |     finally: | ||||||
|  |         if action == "down": | ||||||
|  |             outpost.force_delete() | ||||||
|  |  | ||||||
|  |  | ||||||
| @CELERY_APP.task(bind=True, base=SystemTask) | @CELERY_APP.task(bind=True, base=SystemTask) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer