Skip to content
GitLab
Explore
Sign in
Commits on Source (1)
Add script to force delete a user, in case of duplicates
· 8ec7d68a
ynerant
authored
Feb 22, 2021
Signed-off-by:
Yohann D'ANELLO
<
ynerant@crans.org
>
8ec7d68a
Hide whitespace changes
Inline
Side-by-side
management/commands/force_delete_user.py
0 → 100644
View file @
8ec7d68a
# 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
)