1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace OcLegacy\Admin\Gdpr; |
4
|
|
|
|
5
|
|
|
use Doctrine\DBAL\Connection; |
6
|
|
|
use Exception; |
7
|
|
|
|
8
|
|
|
class GdprHandler |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* @var Connection |
12
|
|
|
*/ |
13
|
|
|
private $connection; |
14
|
|
|
/** |
15
|
|
|
* @var string |
16
|
|
|
*/ |
17
|
|
|
private $projectDir; |
18
|
|
|
|
19
|
|
|
public function __construct(Connection $connection, string $projectDir) |
20
|
|
|
{ |
21
|
|
|
$this->connection = $connection; |
22
|
|
|
$this->projectDir = $projectDir; |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
public function handle(\user $user, bool $execute = false): array |
26
|
|
|
{ |
27
|
|
|
$userId = $user->getUserId(); |
28
|
|
|
|
29
|
|
|
$caches = $this->getCaches($userId); |
30
|
|
|
$cacheLogs = $this->getCacheLogs($userId); |
31
|
|
|
|
32
|
|
|
$cachePictures = $this->fetchPictures($caches, 'cache_id', OBJECT_CACHE); |
33
|
|
|
$cacheLogPictures = $this->fetchPictures($cacheLogs, 'id', OBJECT_CACHELOG); |
34
|
|
|
|
35
|
|
|
$cachePicturesModified = $this->fetchPicturesModified($caches, 'cache_id', OBJECT_CACHE); |
36
|
|
|
$cacheLogPicturesModified = $this->fetchPicturesModified($cacheLogs, 'id', OBJECT_CACHELOG); |
37
|
|
|
|
38
|
|
|
$cacheCount = count($caches); |
39
|
|
|
$cacheLogCount = count($cacheLogs); |
40
|
|
|
$cachePicturesCount = count($cachePictures) + count($cachePicturesModified); |
41
|
|
|
$cacheLogPicturesCount = count($cacheLogPictures) + count($cacheLogPicturesModified); |
42
|
|
|
|
43
|
|
|
$executed = false; |
44
|
|
|
|
45
|
|
|
if ($execute) { |
46
|
|
|
try { |
47
|
|
|
$this->connection->beginTransaction(); |
48
|
|
|
|
49
|
|
|
$this->processUser($userId); |
50
|
|
|
$this->processCaches($userId, $caches); |
51
|
|
|
$this->deletePictures($cachePictures); |
52
|
|
|
$this->deletePictures($cacheLogPictures); |
53
|
|
|
|
54
|
|
|
$this->deletePicturesModified($cachePicturesModified); |
55
|
|
|
$this->deletePicturesModified($cacheLogPicturesModified); |
56
|
|
|
|
57
|
|
|
$this->deleteCacheLogSubTables($userId); |
58
|
|
|
$this->deleteCacheIgnore($userId); |
59
|
|
|
$this->deleteFieldNotes($userId); |
60
|
|
|
$this->deleteLogEntries($userId); |
61
|
|
|
$this->deleteLogins($userId); |
62
|
|
|
$this->deleteOkapiAuthorizations($userId); |
63
|
|
|
$this->deleteWatches($userId); |
64
|
|
|
$this->deleteWsSessions($userId); |
65
|
|
|
|
66
|
|
|
$this->connection->commit(); |
67
|
|
|
|
68
|
|
|
$executed = true; |
69
|
|
|
} catch (Exception $e) { |
70
|
|
|
$this->connection->rollBack(); |
71
|
|
|
|
72
|
|
|
return [ |
73
|
|
|
'userId' => $userId, |
74
|
|
|
'username' => $user->getUsername(), |
75
|
|
|
'email' => $user->getEMail(), |
76
|
|
|
'error' => $e->getMessage(), |
77
|
|
|
]; |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
return [ |
82
|
|
|
'userId' => $userId, |
83
|
|
|
'username' => $user->getUsername(), |
84
|
|
|
'email' => $user->getEMail(), |
85
|
|
|
'cacheCount' => $cacheCount, |
86
|
|
|
'cacheLogCount' => $cacheLogCount, |
87
|
|
|
'cachePicturesCount' => $cachePicturesCount, |
88
|
|
|
'cacheLogPicturesCount' => $cacheLogPicturesCount, |
89
|
|
|
'executed' => $executed, |
90
|
|
|
]; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
private function processUser(int $userId): void |
94
|
|
|
{ |
95
|
|
|
$this->anonymizeUser($userId); |
96
|
|
|
$this->deleteUserOptions($userId); |
97
|
|
|
$this->deleteUserCacheLists($userId); |
98
|
|
|
$this->deleteEmailUser($userId); |
99
|
|
|
$this->deleteQueries($userId); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
private function processCaches(int $userId, array $caches): void |
103
|
|
|
{ |
104
|
|
|
$this->anonymizeCaches($caches); |
105
|
|
|
$this->anonymizeCacheLogs($userId); |
106
|
|
|
$this->deleteCacheAdoptions($userId); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
private function anonymizeUser(int $userId): void |
110
|
|
|
{ |
111
|
|
|
$newUsername = 'delete_' . $userId; |
112
|
|
|
|
113
|
|
|
$this->connection->executeQuery(<<<SQL |
114
|
|
|
UPDATE user SET |
115
|
|
|
username = :username, |
116
|
|
|
last_login = NULL, |
117
|
|
|
password = NULL, |
118
|
|
|
roles = '', |
119
|
|
|
email = NULL, |
120
|
|
|
is_active_flag = 0, |
121
|
|
|
latitude = 0, |
122
|
|
|
longitude = 0, |
123
|
|
|
accept_mailing = 0, |
124
|
|
|
usermail_send_addr = 0, |
125
|
|
|
last_name = '', |
126
|
|
|
first_name = '', |
127
|
|
|
watchmail_mode = 0, |
128
|
|
|
watchmail_hour = 0, |
129
|
|
|
watchmail_nextmail = 0, |
130
|
|
|
watchmail_day = 0, |
131
|
|
|
statpic_logo = 0, |
132
|
|
|
statpic_text = '', |
133
|
|
|
notify_radius = 0, |
134
|
|
|
notify_oconly = 0, |
135
|
|
|
gdpr_deletion = 1, |
136
|
|
|
description = '', |
137
|
|
|
date_created = '1970-01-01 00:00:00', |
138
|
|
|
last_modified = '1970-01-01 00:00:00', |
139
|
|
|
last_email_problem = NULL, |
140
|
|
|
new_pw_code = NULL, |
141
|
|
|
new_pw_date = NULL, |
142
|
|
|
permanent_login_flag = 0, |
143
|
|
|
admin = 0, |
144
|
|
|
domain = NULL |
145
|
|
|
WHERE user_id = :userId |
146
|
|
|
SQL |
147
|
|
|
, [ |
148
|
|
|
'userId' => $userId, |
149
|
|
|
'username' => $newUsername, |
150
|
|
|
]); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
private function deleteCacheAdoptions(int $userId): void |
154
|
|
|
{ |
155
|
|
|
$this->connection->executeQuery('DELETE FROM cache_adoptions WHERE from_user_id = :userId OR to_user_id = :userId', [ |
156
|
|
|
'userId' => $userId, |
157
|
|
|
]); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
private function deleteUserOptions(int $userId): void |
161
|
|
|
{ |
162
|
|
|
$this->connection->executeQuery('DELETE FROM user_options WHERE user_id = :userId', [ |
163
|
|
|
'userId' => $userId, |
164
|
|
|
]); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
View Code Duplication |
private function deleteUserCacheLists(int $userId): void |
|
|
|
|
168
|
|
|
{ |
169
|
|
|
$this->connection->executeQuery('DELETE FROM cache_lists WHERE user_id = :userId', [ |
170
|
|
|
'userId' => $userId, |
171
|
|
|
]); |
172
|
|
|
|
173
|
|
|
$this->connection->executeQuery('DELETE FROM cache_list_watches WHERE user_id = :userId', [ |
174
|
|
|
'userId' => $userId, |
175
|
|
|
]); |
176
|
|
|
|
177
|
|
|
$this->connection->executeQuery('DELETE FROM cache_list_bookmarks WHERE user_id = :userId', [ |
178
|
|
|
'userId' => $userId, |
179
|
|
|
]); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
private function anonymizeCacheLogs(int $userId): void |
183
|
|
|
{ |
184
|
|
|
$this->connection->executeQuery('UPDATE cache_logs SET text=\'-User gelöscht-\', gdpr_deletion = 1 WHERE user_id = :userId', [ |
185
|
|
|
'userId' => $userId, |
186
|
|
|
]); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
private function deleteCacheLogSubTables(int $userId): void |
190
|
|
|
{ |
191
|
|
|
$this->connection->executeQuery('DELETE FROM cache_logs_archived WHERE user_id = :userId', [ |
192
|
|
|
'userId' => $userId, |
193
|
|
|
]); |
194
|
|
|
|
195
|
|
|
$this->connection->executeQuery('DELETE FROM cache_logs_modified WHERE user_id = :userId', [ |
196
|
|
|
'userId' => $userId, |
197
|
|
|
]); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
private function getCaches(int $userId): array |
201
|
|
|
{ |
202
|
|
|
return $this->connection->fetchAll('SELECT * FROM caches WHERE user_id = :userId', [ |
203
|
|
|
'userId' => $userId, |
204
|
|
|
]); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
private function getCacheLogs(int $userId): array |
208
|
|
|
{ |
209
|
|
|
return $this->connection->fetchAll('SELECT * FROM cache_logs WHERE user_id = :userId', [ |
210
|
|
|
'userId' => $userId, |
211
|
|
|
]); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
private function anonymizeCaches(array $caches): void |
215
|
|
|
{ |
216
|
|
|
foreach ($caches as $cache) { |
217
|
|
|
$this->connection->executeQuery(<<<'SQL' |
218
|
|
|
UPDATE caches SET |
219
|
|
|
name='-Cache nach DSGVO gelöscht-', |
220
|
|
|
show_cachelists = 0, |
221
|
|
|
status = 6, |
222
|
|
|
wp_gc = '', |
223
|
|
|
wp_gc_maintained ='', |
224
|
|
|
gdpr_deletion = 1 |
225
|
|
|
WHERE cache_id = :cacheId; |
226
|
|
|
SQL |
227
|
|
|
, [ |
228
|
|
|
'cacheId' => (int) $cache['cache_id'], |
229
|
|
|
]); |
230
|
|
|
|
231
|
|
|
$this->connection->executeQuery(<<<'SQL' |
232
|
|
|
UPDATE cache_desc SET |
233
|
|
|
`desc` = '### DSGVO Löschung ###<br>Dieses Listing wurde aufgrund der Anforderungen des Urhebers, seine Daten im Rahmen des Datenschutzes zu löschen - neutralisiert.<br>Leider können wir keine Details aus dem Listing beibehalten. Die nicht personenbezogenen Parameter des Caches wie Lage, Wertung und die Logs Dritter können zum Erhalt des Spieles aber beibehalten.<br><br>###<br>Eurer OC Team - im Auftrag des Datenschutzbeauftragen', |
234
|
|
|
desc_html = 1, |
235
|
|
|
hint = '', |
236
|
|
|
short_desc = '' |
237
|
|
|
WHERE cache_id = :cacheId |
238
|
|
|
SQL |
239
|
|
|
, [ |
240
|
|
|
'cacheId' => (int) $cache['cache_id'], |
241
|
|
|
]); |
242
|
|
|
|
243
|
|
|
$this->connection->executeQuery('DELETE FROM cache_desc_modified WHERE cache_id = :cacheId', [ |
244
|
|
|
'cacheId' => (int) $cache['cache_id'], |
245
|
|
|
]); |
246
|
|
|
|
247
|
|
|
$this->connection->executeQuery('DELETE FROM cache_ignore WHERE cache_id = :cacheId', [ |
248
|
|
|
'cacheId' => (int) $cache['cache_id'], |
249
|
|
|
]); |
250
|
|
|
|
251
|
|
|
$this->connection->executeQuery('DELETE FROM caches_modified WHERE cache_id = :cacheId', [ |
252
|
|
|
'cacheId' => (int) $cache['cache_id'], |
253
|
|
|
]); |
254
|
|
|
} |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
public function fetchPictures(array $data, string $idField, int $objectType): array |
258
|
|
|
{ |
259
|
|
|
if ($data === []) { |
260
|
|
|
return []; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
$ids = array_map(static function (array $cache) use ($idField) { |
264
|
|
|
return (int) $cache[$idField]; |
265
|
|
|
}, $data); |
266
|
|
|
|
267
|
|
|
$pictures = $this->connection->fetchAll('SELECT * FROM pictures WHERE object_id IN (' . implode(',', $ids) . ') AND object_type = :objectType', [ |
268
|
|
|
'objectType' => $objectType, |
269
|
|
|
]); |
270
|
|
|
|
271
|
|
|
$modifiedPictures = $this->connection->fetchAll('SELECT * FROM pictures_modified WHERE object_id IN (' . implode(',', $ids) . ') AND object_type = :objectType', [ |
272
|
|
|
'objectType' => $objectType, |
273
|
|
|
]); |
274
|
|
|
|
275
|
|
|
return array_merge($pictures, $modifiedPictures); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
public function fetchPicturesModified(array $data, string $idField, int $objectType): array |
279
|
|
|
{ |
280
|
|
|
if ($data === []) { |
281
|
|
|
return []; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
$ids = array_map(static function (array $cache) use ($idField) { |
285
|
|
|
return (int) $cache[$idField]; |
286
|
|
|
}, $data); |
287
|
|
|
|
288
|
|
|
return $this->connection->fetchAll('SELECT * FROM pictures_modified WHERE object_id IN (' . implode(',', $ids) . ') AND object_type = :objectType', [ |
289
|
|
|
'objectType' => $objectType, |
290
|
|
|
]); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
private function deletePictures(array $pictures): void |
294
|
|
|
{ |
295
|
|
|
foreach ($pictures as $picture) { |
296
|
|
|
$imagePath = parse_url($picture['url'], PHP_URL_PATH); |
297
|
|
|
@unlink($this->projectDir . $imagePath); |
|
|
|
|
298
|
|
|
|
299
|
|
|
if (isset($picture['thumb_url'])) { |
300
|
|
|
$thumbPath = parse_url($picture['thumb_url'], PHP_URL_PATH); |
301
|
|
|
@unlink($this->projectDir . $thumbPath); |
|
|
|
|
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
$this->connection->executeQuery('DELETE FROM pictures WHERE object_id = :objectId AND object_type = :objectType', [ |
305
|
|
|
'objectId' => $picture['object_id'], |
306
|
|
|
'objectType' => $picture['object_type'], |
307
|
|
|
]); |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
private function deletePicturesModified(array $pictures): void |
312
|
|
|
{ |
313
|
|
|
foreach ($pictures as $picture) { |
314
|
|
|
$imagePath = parse_url($picture['url'], PHP_URL_PATH); |
315
|
|
|
|
316
|
|
|
@unlink($this->projectDir . $imagePath); |
|
|
|
|
317
|
|
|
|
318
|
|
|
$this->connection->executeQuery('DELETE FROM pictures_modified WHERE object_id = :objectId AND object_type = :objectType', [ |
319
|
|
|
'objectId' => $picture['object_id'], |
320
|
|
|
'objectType' => $picture['object_type'], |
321
|
|
|
]); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
private function deleteCacheIgnore(int $userId): void |
326
|
|
|
{ |
327
|
|
|
$this->connection->executeQuery('DELETE FROM cache_ignore WHERE user_id = :userId', [ |
328
|
|
|
'userId' => $userId, |
329
|
|
|
]); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
private function deleteEmailUser(int $userId): void |
333
|
|
|
{ |
334
|
|
|
$this->connection->executeQuery('DELETE FROM email_user WHERE from_user_id = :userId OR to_user_id = :userId', [ |
335
|
|
|
'userId' => $userId, |
336
|
|
|
]); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
private function deleteFieldNotes(int $userId): void |
340
|
|
|
{ |
341
|
|
|
$this->connection->executeQuery('DELETE FROM field_note WHERE user_id = :userId', [ |
342
|
|
|
'userId' => $userId, |
343
|
|
|
]); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
private function deleteLogEntries(int $userId): void |
347
|
|
|
{ |
348
|
|
|
$this->connection->executeQuery('DELETE FROM logentries WHERE userid = :userId', [ |
349
|
|
|
'userId' => $userId, |
350
|
|
|
]); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
private function deleteLogins(int $userId): void |
354
|
|
|
{ |
355
|
|
|
$this->connection->executeQuery('DELETE FROM logins WHERE user_id = :userId', [ |
356
|
|
|
'userId' => $userId, |
357
|
|
|
]); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
private function deleteOkapiAuthorizations(int $userId): void |
361
|
|
|
{ |
362
|
|
|
$this->connection->executeQuery(<<<'SQL' |
363
|
|
|
DELETE oa, oc |
364
|
|
|
FROM okapi_authorizations oa |
365
|
|
|
INNER JOIN okapi_consumers oc ON oa.consumer_key = oc.`key` |
366
|
|
|
INNER JOIN okapi_nonces ono ON oa.consumer_key = ono.consumer_key |
367
|
|
|
INNER JOIN okapi_diagnostics od ON oa.consumer_key = od.consumer_key |
368
|
|
|
INNER JOIN okapi_cache oca ON oa.consumer_key = oca.`key` |
369
|
|
|
INNER JOIN okapi_stats_hourly osh ON oa.consumer_key = osh.consumer_key |
370
|
|
|
INNER JOIN okapi_stats_monthly osm ON oa.consumer_key = osm.consumer_key |
371
|
|
|
INNER JOIN okapi_stats_temp ost ON oa.consumer_key = ost.consumer_key |
372
|
|
|
INNER JOIN okapi_submitted_objects oso ON oa.consumer_key = oso.consumer_key |
373
|
|
|
INNER JOIN okapi_tokens ot ON oa.consumer_key = ot.consumer_key |
374
|
|
|
WHERE oa.user_id = :userId |
375
|
|
|
SQL |
376
|
|
|
, [ |
377
|
|
|
'userId' => $userId, |
378
|
|
|
]); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
private function deleteQueries(int $userId): void |
382
|
|
|
{ |
383
|
|
|
$this->connection->executeQuery('DELETE FROM queries WHERE user_id = :userId', [ |
384
|
|
|
'userId' => $userId, |
385
|
|
|
]); |
386
|
|
|
} |
387
|
|
|
|
388
|
|
View Code Duplication |
private function deleteWatches(int $userId): void |
|
|
|
|
389
|
|
|
{ |
390
|
|
|
$this->connection->executeQuery('DELETE FROM watches_logqueue WHERE user_id = :userId', [ |
391
|
|
|
'userId' => $userId, |
392
|
|
|
]); |
393
|
|
|
|
394
|
|
|
$this->connection->executeQuery('DELETE FROM watches_notified WHERE user_id = :userId', [ |
395
|
|
|
'userId' => $userId, |
396
|
|
|
]); |
397
|
|
|
|
398
|
|
|
$this->connection->executeQuery('DELETE FROM watches_waiting WHERE user_id = :userId', [ |
399
|
|
|
'userId' => $userId, |
400
|
|
|
]); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
private function deleteWsSessions(int $userId): void |
404
|
|
|
{ |
405
|
|
|
$this->connection->executeQuery('DELETE FROM ws_sessions WHERE user_id = :userId', [ |
406
|
|
|
'userId' => $userId, |
407
|
|
|
]); |
408
|
|
|
} |
409
|
|
|
} |
410
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.