mirror of
				https://gitlab.crans.org/bde/nk20-scripts
				synced 2025-10-31 07:09:52 +01:00 
			
		
		
		
	Add script to force delete a user, in case of duplicates
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
		
							
								
								
									
										176
									
								
								management/commands/force_delete_user.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								management/commands/force_delete_user.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| import getpass | ||||
| from time import sleep | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.contrib.auth.models import User | ||||
| from django.core.mail import mail_admins | ||||
| from django.core.management.base import BaseCommand | ||||
| from django.db import transaction | ||||
| from django.db.models import Q | ||||
| from django.test import override_settings | ||||
|  | ||||
| from note.models import Alias, Transaction | ||||
|  | ||||
|  | ||||
| class Command(BaseCommand): | ||||
|     """ | ||||
|     This script is used to force delete a user. | ||||
|     THIS IS ONLY ATTENDED TO BE USED TO DELETE FAKE ACCOUNTS THAT | ||||
|     WERE VALIDATED BY ERRORS. Please use data anonymization if you | ||||
|     want to block the account of a user. | ||||
|     """ | ||||
|  | ||||
|     def add_arguments(self, parser): | ||||
|         parser.add_argument('user', type=str, nargs='+', help="User id to delete.") | ||||
|         parser.add_argument('--force', '-f', action='store_true', | ||||
|                             help="Force the script to have low verbosity.") | ||||
|         parser.add_argument('--doit', '-d', action='store_true', | ||||
|                             help="Don't ask for a final confirmation and commit modification. " | ||||
|                                  "This option should really be used carefully.") | ||||
|  | ||||
|     def handle(self, *args, **kwargs): | ||||
|         force = kwargs['force'] | ||||
|  | ||||
|         if not force: | ||||
|             self.stdout.write(self.style.WARNING("This is a dangerous script. " | ||||
|                                                  "Please use --force to indicate that you known what you are doing. " | ||||
|                                                  "Nothing will be deleted yet.")) | ||||
|             sleep(5) | ||||
|  | ||||
|         # We need to know who to blame. | ||||
|         qs = User.objects.filter(note__alias__normalized_name=Alias.normalize(getpass.getuser())) | ||||
|         if not qs.exists(): | ||||
|             self.stderr.write(self.style.ERROR("I don't know who you are. Please add your linux id as an alias of " | ||||
|                                                "your own account.")) | ||||
|             exit(2) | ||||
|         executor = qs.get() | ||||
|  | ||||
|         deleted_users = [] | ||||
|         deleted = [] | ||||
|  | ||||
|         # Don't send mails during the process | ||||
|         with override_settings(EMAIL_BACKEND='django.core.mail.backends.dummy.EmailBackend'): | ||||
|             for user_id in kwargs['user']: | ||||
|                 if user_id.isnumeric(): | ||||
|                     qs = User.objects.filter(pk=int(user_id)) | ||||
|                     if not qs.exists(): | ||||
|                         self.stderr.write(self.style.WARNING(f"User {user_id} was not found. Ignoring...")) | ||||
|                         continue | ||||
|                     user = qs.get() | ||||
|                 else: | ||||
|                     qs = Alias.objects.filter(normalized_name=Alias.normalize(user_id), note__noteuser__isnull=False) | ||||
|                     if not qs.exists(): | ||||
|                         self.stderr.write(self.style.WARNING(f"User {user_id} was not found. Ignoring...")) | ||||
|                         continue | ||||
|                     user = qs.get().note.user | ||||
|  | ||||
|                 with transaction.atomic(): | ||||
|                     local_deleted = [] | ||||
|  | ||||
|                     # Unlock note to enable modifications | ||||
|                     if force and not user.note.is_active: | ||||
|                         user.note.is_active = True | ||||
|                         user.note.save() | ||||
|  | ||||
|                     # Deleting transactions | ||||
|                     transactions = Transaction.objects.filter(Q(source=user.note) | Q(destination=user.note)).all() | ||||
|                     local_deleted += list(transactions) | ||||
|                     if kwargs['verbosity'] >= 1: | ||||
|                         for tr in transactions: | ||||
|                             self.stdout.write(f"Removing {tr}...") | ||||
|                     if force: | ||||
|                         transactions.delete() | ||||
|  | ||||
|                     # Deleting memberships | ||||
|                     memberships = user.memberships.all() | ||||
|                     local_deleted += list(memberships) | ||||
|                     if kwargs['verbosity'] >= 1: | ||||
|                         for membership in memberships: | ||||
|                             self.stdout.write(f"Removing {membership}...") | ||||
|                     if force: | ||||
|                         memberships.delete() | ||||
|  | ||||
|                     # Deleting aliases | ||||
|                     alias_set = user.note.alias.all() | ||||
|                     local_deleted += list(alias_set) | ||||
|                     if kwargs['verbosity'] >= 1: | ||||
|                         for alias in alias_set: | ||||
|                             self.stdout.write(f"Removing alias {alias}...") | ||||
|                     if force: | ||||
|                         alias_set.delete() | ||||
|  | ||||
|                     if 'activity' in settings.INSTALLED_APPS: | ||||
|                         from activity.models import Guest, Entry | ||||
|  | ||||
|                         # Deleting activity entries | ||||
|                         entries = Entry.objects.filter(Q(note=user.note) | Q(guest__inviter=user.note)).all() | ||||
|                         local_deleted += list(entries) | ||||
|                         if kwargs['verbosity'] >= 1: | ||||
|                             for entry in entries: | ||||
|                                 self.stdout.write(f"Removing {entry}...") | ||||
|                         if force: | ||||
|                             entries.delete() | ||||
|  | ||||
|                         # Deleting invited guests | ||||
|                         guests = Guest.objects.filter(inviter=user.note).all() | ||||
|                         local_deleted += list(guests) | ||||
|                         if kwargs['verbosity'] >= 1: | ||||
|                             for guest in guests: | ||||
|                                 self.stdout.write(f"Removing guest {guest}...") | ||||
|                         if force: | ||||
|                             guests.delete() | ||||
|  | ||||
|                     if 'treasury' in settings.INSTALLED_APPS: | ||||
|                         from treasury.models import SogeCredit | ||||
|  | ||||
|                         # Deleting soge credit | ||||
|                         credits = SogeCredit.objects.filter(user=user).all() | ||||
|                         local_deleted += list(credits) | ||||
|                         if kwargs['verbosity'] >= 1: | ||||
|                             for credit in credits: | ||||
|                                 self.stdout.write(f"Removing {credit}...") | ||||
|                         if force: | ||||
|                             credits.delete() | ||||
|  | ||||
|                     # Deleting note | ||||
|                     local_deleted.append(user.note) | ||||
|                     if force: | ||||
|                         user.note.delete() | ||||
|  | ||||
|                     if 'logs' in settings.INSTALLED_APPS: | ||||
|                         from logs.models import Changelog | ||||
|  | ||||
|                         # Replace log executors by the runner of the script | ||||
|                         Changelog.objects.filter(user=user).update(user=executor) | ||||
|  | ||||
|                     # Deleting profile | ||||
|                     local_deleted.append(user.profile) | ||||
|                     if force: | ||||
|                         user.profile.delete() | ||||
|  | ||||
|                     # Finally deleting user | ||||
|                     if force: | ||||
|                         user.delete() | ||||
|                     local_deleted.append(user) | ||||
|  | ||||
|                     # This script should really not be used. | ||||
|                     if not kwargs['doit'] and not input('You are about to delete real user data. ' | ||||
|                                                         'Are you really sure that it is what you want? [y/N] ')\ | ||||
|                             .lower().startswith('y'): | ||||
|                         self.stdout.write(self.style.ERROR("Aborted.")) | ||||
|                         exit(1) | ||||
|  | ||||
|                     if kwargs['verbosity'] >= 1: | ||||
|                         self.stdout.write(self.style.SUCCESS(f"User {user} deleted.")) | ||||
|                         deleted_users.append(user) | ||||
|                         deleted += local_deleted | ||||
|  | ||||
|         if deleted_users: | ||||
|             message = f"Les utilisateurs {deleted_users} ont été supprimés par {executor}.\n\n" | ||||
|             message += "Ont été supprimés en conséquence les objets suivants :\n\n" | ||||
|             for obj in deleted: | ||||
|                 message += f"{repr(obj)} (pk: {obj.pk})\n" | ||||
|             mail_admins("Utilisateurs supprimés", message) | ||||
		Reference in New Issue
	
	Block a user