Completed
Push — development ( bbfd57...9ee667 )
by Thomas
26s queued 10s
created

GdprHandler::deleteLogEntries()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
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
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
298
299
            if (isset($picture['thumb_url'])) {
300
                $thumbPath = parse_url($picture['thumb_url'], PHP_URL_PATH);
301
                @unlink($this->projectDir . $thumbPath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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