-
Notifications
You must be signed in to change notification settings - Fork 87
/
erase_user_data.py
204 lines (152 loc) · 4.94 KB
/
erase_user_data.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env python3
# Script for erasing all data about a user from the database.
# Intended for GDPR erasure requests.
#
# NOTE: We recommend implementing a "GDPR Erasure Ban" on the user's last IP/HWID before erasing their data, to prevent abuse.
# This is acceptable under the GDPR as a "legitimate interest" to prevent GDPR erasure being used to avoid moderation/bans.
# You would need to do this *before* running this script, to avoid losing the IP/HWID of the user entirely.
import argparse
import os
import psycopg2
from uuid import UUID
LATEST_DB_MIGRATION = "20230725193102_AdminNotesImprovementsForeignKeys"
def main():
parser = argparse.ArgumentParser()
# Yes we need both to reliably pseudonymize the admin_log table.
parser.add_argument("user_id", help="User ID to erase data for")
parser.add_argument("user_name", help="User name to erase data for")
parser.add_argument("--ignore-schema-mismatch", action="store_true")
parser.add_argument("--connection-string", required=True, help="Database connection string to use. See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING")
args = parser.parse_args()
conn = psycopg2.connect(args.connection_string)
cur = conn.cursor()
check_schema_version(cur, args.ignore_schema_mismatch)
user_id = args.user_id
user_name = args.user_name
clear_admin(cur, user_id)
pseudonymize_admin_log(cur, user_name, user_id)
clear_assigned_user_id(cur, user_id)
clear_connection_log(cur, user_id)
clear_play_time(cur, user_id)
clear_player(cur, user_id)
clear_preference(cur, user_id)
clear_server_ban(cur, user_id)
clear_server_ban_exemption(cur, user_id)
clear_server_role_ban(cur, user_id)
clear_uploaded_resource_log(cur, user_id)
clear_whitelist(cur, user_id)
print("Committing...")
conn.commit()
def check_schema_version(cur: "psycopg2.cursor", ignore_mismatch: bool):
cur.execute('SELECT "MigrationId" FROM "__EFMigrationsHistory" ORDER BY "__EFMigrationsHistory" DESC LIMIT 1')
schema_version = cur.fetchone()
if schema_version == None:
print("Unable to read database schema version.")
exit(1)
if schema_version[0] != LATEST_DB_MIGRATION:
print(f"Unsupport schema version of DB: '{schema_version[0]}'. Supported: {LATEST_DB_MIGRATION}")
if ignore_mismatch:
return
exit(1)
def clear_admin(cur: "psycopg2.cursor", user_id: str):
print("Clearing admin...")
cur.execute("""
DELETE FROM
admin
WHERE
user_id = %s
""", (user_id,))
def pseudonymize_admin_log(cur: "psycopg2.cursor", user_name: str, user_id: str):
print("Pseudonymizing admin_log...")
cur.execute("""
UPDATE
admin_log l
SET
message = replace(message, %s, %s)
FROM
admin_log_player lp
WHERE
lp.round_id = l.round_id AND lp.log_id = l.admin_log_id AND player_user_id = %s;
""", (user_name, user_id, user_id,))
def clear_assigned_user_id(cur: "psycopg2.cursor", user_id: str):
print("Clearing assigned_user_id...")
cur.execute("""
DELETE FROM
assigned_user_id
WHERE
user_id = %s
""", (user_id,))
def clear_connection_log(cur: "psycopg2.cursor", user_id: str):
print("Clearing connection_log...")
cur.execute("""
DELETE FROM
connection_log
WHERE
user_id = %s
""", (user_id,))
def clear_play_time(cur: "psycopg2.cursor", user_id: str):
print("Clearing play_time...")
cur.execute("""
DELETE FROM
play_time
WHERE
player_id = %s
""", (user_id,))
def clear_player(cur: "psycopg2.cursor", user_id: str):
print("Clearing player...")
cur.execute("""
DELETE FROM
player
WHERE
user_id = %s
""", (user_id,))
def clear_preference(cur: "psycopg2.cursor", user_id: str):
print("Clearing preference...")
cur.execute("""
DELETE FROM
preference
WHERE
user_id = %s
""", (user_id,))
def clear_server_ban(cur: "psycopg2.cursor", user_id: str):
print("Clearing server_ban...")
cur.execute("""
DELETE FROM
server_ban
WHERE
player_user_id = %s
""", (user_id,))
def clear_server_ban_exemption(cur: "psycopg2.cursor", user_id: str):
print("Clearing server_ban_exemption...")
cur.execute("""
DELETE FROM
server_ban_exemption
WHERE
user_id = %s
""", (user_id,))
def clear_server_role_ban(cur: "psycopg2.cursor", user_id: str):
print("Clearing server_role_ban...")
cur.execute("""
DELETE FROM
server_role_ban
WHERE
player_user_id = %s
""", (user_id,))
def clear_uploaded_resource_log(cur: "psycopg2.cursor", user_id: str):
print("Clearing uploaded_resource_log...")
cur.execute("""
DELETE FROM
uploaded_resource_log
WHERE
user_id = %s
""", (user_id,))
def clear_whitelist(cur: "psycopg2.cursor", user_id: str):
print("Clearing whitelist...")
cur.execute("""
DELETE FROM
whitelist
WHERE
user_id = %s
""", (user_id,))
main()
# "I'm surprised you managed to write this entire Python file without spamming the word 'sus' everywhere." - Remie