Implement anonimising deletion of users

This commit is contained in:
Daan Sprenkels
2018-08-18 21:25:45 +02:00
parent ee08b6dcc0
commit 8488023710
6 changed files with 109 additions and 1 deletions

View File

@ -14,5 +14,12 @@ class UserAdmin(BaseUserAdmin):
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Activation'), {'fields': ('activation_token', 'reset_token')}),
)
list_display = ('username', 'email', 'name', 'date_joined', 'queue', 'is_staff')
list_display = ('username', 'email', 'name', 'date_joined', 'last_login', 'queue', 'is_staff')
search_fields = ('username', 'name', 'email')
def delete_model(self, request, user):
user.delete()
def delete_queryset(self, request, users):
for user in users.all():
self.delete_model(request, user)

View File

@ -1,4 +1,5 @@
import json
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from songs.models import Song

View File

@ -1,4 +1,5 @@
import json
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.db.utils import OperationalError

View File

@ -1,4 +1,5 @@
import json
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from ...utils import get_first_queue

View File

@ -0,0 +1,79 @@
from datetime import datetime, timedelta
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.conf import settings
from django.db.models import Q
from django.utils.timezone import get_default_timezone
_IMPORTANT_USERNAMES = ['root', 'admin', 'postmaster', 'dsprenkels', 'gmulder', 'bwesterb']
class Command(BaseCommand):
help = 'Get the list of users that has not logged in for quite a while'
def add_arguments(self, parser):
parser.add_argument(
'--purge',
action='store_const',
default=False,
const=True,
help='interactively purge the old users')
parser.add_argument(
'days_old',
nargs='?',
type=float,
default=365.25,
help='amount of days after which a user is considered old')
def handle(self, purge, *args, **kwargs):
if purge:
return self.handle_purge_users(*args, **kwargs)
else:
return self.handle_get_users(*args, **kwargs)
def handle_purge_users(self, days_old, *args, **kwargs):
users = get_old_users(days_old).all()
if not users:
self.stdout.write(self.style.NOTICE("No users to be deleted"))
return
self.stdout.write("{} {} {}\n".format('username'.ljust(19), 'name'.ljust(29), 'last_login'))
for user in users:
self.stdout.write("{} {} {}\n".format(user.username.ljust(19), user.name.ljust(29), user.last_login))
self.stdout.write("\n")
self.stdout.write(self.style.WARNING("I will be removing {} users, please check".format(len(users))))
confirmation = input("Type 'YES' to confirm: ")
for i in range(1, 3):
if confirmation == 'YES':
break
confirmation = input("Please type 'YES' to confirm (or ^C to abort): ")
else:
self.stdout.write(
self.style.ERROR(
'Aborting purge operation after {} prompts'.format(i + 1)))
return
deleted = 0
for i, user in enumerate(users):
print("[{: 4.0f}% ] {}".format(100 * i / len(users), user))
if user.username in _IMPORTANT_USERNAMES:
self.stdout.write(self.style.WARNING("Not deleting user '{}': username looks important".format(user)))
continue
user.delete()
deleted += 1
self.stdout.write(self.style.SUCCESS("Deleted {} users".format(deleted)))
def handle_get_users(self, days_old, *args, **kwargs):
for user in get_old_users(days_old):
self.stdout.write("{}\n".format(user))
def get_old_users(days_old):
"""Return a queryset with all users older than an amount of days"""
User = get_user_model()
tz = get_default_timezone()
return User.objects.filter(is_staff=False, is_superuser=False, is_active=True).filter(
Q(last_login__lt=datetime.now(tz) - timedelta(days_old))
| Q(last_login=None)).order_by('username')

View File

@ -1,11 +1,13 @@
from django.db import models
from queues.models import Queue
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import ASCIIUsernameValidator, UnicodeUsernameValidator
from django.utils import six, timezone
from django.utils.translation import ugettext_lazy as _
from django.core.mail import send_mail
from marietje.utils import get_first_queue
@ -105,6 +107,23 @@ class User(AbstractBaseUser, PermissionsMixin):
verbose_name = _('user')
verbose_name_plural = _('users')
def delete(self, *args, **kwargs):
"""
We want to override the deletion behaviour for users, because we want to keep the stats
valid. Instead of deleting the user, we will completely anonimise the user's personal
data. The user will still have their uploads and queues associated with them, but they are
not personal data.
See also: UserAdmin.delete_queryset(request, queryset)
"""
self.username = '_deleteduser{:d}'.format(self.id)
self.password = make_password(None)
self.name = '[deleted]'
self.email = ''
self.is_active = False
self.study = ''
self.save(*args, **kwargs)
def get_full_name(self):
return self.name