diff --git a/accounts/models.py b/accounts/models.py index d72f2f94b77c7560e5fb375c657058086d2092f4..6d67dc4ead48d71691aa5d2b912ff92001b2d7aa 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -21,7 +21,16 @@ from django.db import models from django.conf import settings from django.contrib.auth.models import AbstractBaseUser, User, BaseUserManager from peers.models import Peer +from flowspec.models import Route +# TODO: dependency issue: move logging_utils to general package +import flowspec.logging_utils +logger = flowspec.logging_utils.logger_init_default(__name__, "accounts_model.log", False) + +# + + +# class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) @@ -46,8 +55,54 @@ class UserProfile(models.Model): return False return networks + # deleting of rules by this account is allowed def is_delete_allowed(self): user_is_admin = self.user.is_superuser username = self.username return (user_is_admin and settings.ALLOW_DELETE_FULL_FOR_ADMIN) or settings.ALLOW_DELETE_FULL_FOR_USER_ALL or (username in settings.ALLOW_DELETE_FULL_FOR_USER_LIST) +# + +from django.dispatch import receiver +from django.db.models.signals import pre_delete + +#@receiver(pre_delete, sender=UserProfile) +@receiver(pre_delete, sender=User) +def user_pre_delete_handler(sender, instance, **kwargs): + logger.info("user_pre_delete_handler(): pre_delete instance="+str(instance)) + user_owned_rules_adopt_to_related_user(instance) + +def user_owned_rules_adopt_to_related_user(user): + routes_owned = Route.objects.filter(applier=user) + logger.info("user_owned_rules_adopt_to_related_user(): => routes_owned="+str(routes_owned)) + + users_peers = user.userprofile.peers.all() + users_peers1 = None + logger.info("user_owned_rules_adopt_to_related_user(): => users_peers="+str(users_peers)) + if len(users_peers)==1: + users_peers1 = users_peers[0] + logger.info("user_owned_rules_adopt_to_related_user(): => users_peers[0]="+str(users_peers1)) + #peers1_userprofiles = users_peers[0].user_profile + #logger.info("user_owned_rules_adopt_to_related_user(): => peers1_userprofiles="+str(peers1_userprofiles)) + + users_related = User.objects.filter(userprofile__peers__in=users_peers) + logger.info("user_owned_rules_adopt_to_related_user(): => users_related="+str(users_related)) + user_related1 = None + for user2 in users_related: + if user2 != user: + user_related1=user2 + break + + logger.info("user_owned_rules_adopt_to_related_user(): => user_related1="+str(user_related1)) + + if user_related1!=None: + if len(routes_owned)>0: + logger.info("user_owned_rules_adopt_to_related_user(): len="+str(len(routes_owned))) + for route in routes_owned: + logger.info("user_owned_rules_adopt_to_related_user(): owned route="+str(route)) + route.applier = user_related1 + logger.info("user_owned_rules_adopt_to_related_user(): reassigning owned route="+str(route)+" by user to be deleted ("+str(user)+") to new owner "+str(user_related1)) + route.save() + + return (routes_owned, user_related1, users_peers1) + diff --git a/flowspec/admin.py b/flowspec/admin.py index c7cd52c553f414a29d308526f442f4448fcb9c68..81497b996e1d6faa49ba2243d1136f1216915f3d 100644 --- a/flowspec/admin.py +++ b/flowspec/admin.py @@ -27,6 +27,14 @@ from django.contrib.auth.admin import UserAdmin from peers.models import * from longerusername.forms import UserCreationForm, UserChangeForm +from django.contrib import messages +from accounts.models import user_owned_rules_adopt_to_related_user + +# TODO: dependency issue: move logging_utils to general package +import flowspec.logging_utils +logger = flowspec.logging_utils.logger_init_default(__name__, "flowspec_admin.log", False) + +# class RouteAdmin(admin.ModelAdmin): form = RouteForm @@ -83,6 +91,21 @@ class UserProfileAdmin(UserAdmin): queryset = queryset.update(is_active=True) activate.short_description = "Activate Selected Users" + def delete_model(self, request, client): + if False: + messages.set_level(request, messages.ERROR) + messages.error(request, 'Blocking deletion') + else: + (adopted_rules, adoting_user, users_peer1) = user_owned_rules_adopt_to_related_user(client) # before actually calling the super.delete_model clean-up owned rules in order to get info about the cleanup which can be used for extra message to the admin UI + logger.info("delete_model() => adoting_user="+str(adoting_user)) + logger.info("delete_model() => adopted_rules="+str(adopted_rules)) + + if len(adopted_rules)>0: + messages.set_level(request, messages.INFO) + messages.error(request, 'additional info: the rules '+str(adopted_rules)+' were re-assigned to remaining user '+str(adoting_user)+' of peer '+str(users_peer1)) + + super().delete_model(request, client) + def get_userprofile_peers(self, instance): # instance is User instance peers = instance.userprofile.peers.all() diff --git a/flowspec/iprange_match.py b/flowspec/iprange_match.py index a89e79adf8a3fde0156745c28063ed51c39f80a9..1d84d9ff7bfa08acdf615959a9bf91ae7d2b084f 100644 --- a/flowspec/iprange_match.py +++ b/flowspec/iprange_match.py @@ -45,8 +45,13 @@ def get_matching_related_peer_for_rule_destination(ivaltrees_per_version, route) peer_name_tmp = None peer_name_tmp_not_applier_related = None try: - if route.applier!=None: - route_applier__peers_related = set(route.applier.userprofile.peers.select_related()) + applier = route.applier + except: + applier = None + + try: + if applier!=None: + route_applier__peers_related = set(applier.userprofile.peers.select_related()) else: route_applier__peers_related = None diff --git a/flowspec/models.py b/flowspec/models.py index a81a9655281727826bf953458404d90ed781985e..662436ff2fb0cfa0e3f7d7e649db518e27beebb5 100644 --- a/flowspec/models.py +++ b/flowspec/models.py @@ -157,7 +157,8 @@ class ThenAction(models.Model): class Route(models.Model): name = models.SlugField(max_length=128, verbose_name=_("Name")) - applier = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE) + #applier = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE) + applier = models.ForeignKey(User, blank=True, null=True, on_delete=models.DO_NOTHING) source = models.CharField(max_length=45+4, help_text=_("Network address. Use address/CIDR notation"), verbose_name=_("Source Address")) sourceport = models.TextField(blank=True, null=True, verbose_name=_("Source Port")) destination = models.CharField(max_length=45+4, help_text=_("Network address. Use address/CIDR notation"), verbose_name=_("Destination Address"))