Completed
Push — development ( 757133...5c36b2 )
by Thomas
21s queued 11s
created

GdprHandler::deleteWsSessions()   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
65
                $this->connection->commit();
66
67
                $executed = true;
68
            } catch (Exception $e) {
69
                $this->connection->rollBack();
70
71
                return [
72
                    'userId' => $userId,
73
                    'username' => $user->getUsername(),
74
                    'email' => $user->getEMail(),
75
                    'error' => $e->getMessage(),
76
                ];
77
            }
78
        }
79
80
        return [
81
            'userId' => $userId,
82
            'username' => $user->getUsername(),
83
            'email' => $user->getEMail(),
84
            'cacheCount' => $cacheCount,
85
            'cacheLogCount' => $cacheLogCount,
86
            'cachePicturesCount' => $cachePicturesCount,
87
            'cacheLogPicturesCount' => $cacheLogPicturesCount,
88
            'executed' => $executed,
89
        ];
90
    }
91
92
    private function processUser(int $userId): void
93
    {
94
        $this->anonymizeUser($userId);
95
        $this->deleteUserOptions($userId);
96
        $this->deleteUserCacheLists($userId);
97
        $this->deleteEmailUser($userId);
98
        $this->deleteQueries($userId);
99
    }
100
101
    private function processCaches(int $userId, array $caches): void
102
    {
103
        $this->anonymizeCaches($caches);
104
        $this->anonymizeCacheLogs($userId);
105
        $this->deleteCacheAdoptions($userId);
106
    }
107
108
    private function anonymizeUser(int $userId): void
109
    {
110
        $newUsername = 'delete_' . $userId;
111
112
        $this->connection->executeQuery(<<<SQL
113
            UPDATE user SET
114
                username = :username,
115
                last_login = NULL,
116
                password = NULL,
117
                roles = '',
118
                email = NULL,
119
                is_active_flag = 0,
120
                latitude = 0,
121
                longitude = 0,
122
                accept_mailing = 0,
123
                usermail_send_addr = 0,
124
                last_name = '',
125
                first_name = '',
126
                watchmail_mode = 0,
127
                watchmail_hour = 0,
128
                watchmail_nextmail = 0,
129
                watchmail_day = 0,
130
                statpic_logo = 0,
131
                statpic_text = '',
132
                notify_radius = 0,
133
                notify_oconly = 0,
134
                gdpr_deletion = 1,
135
                description = '',
136
                date_created = '1970-01-01 00:00:00',
137
                last_modified = '1970-01-01 00:00:00',
138
                last_email_problem = NULL,
139
                new_pw_code = NULL,
140
                new_pw_date = NULL,
141
                permanent_login_flag = 0,
142
                admin = 0,
143
                domain = NULL
144
            WHERE user_id = :userId
145
SQL
146
        , [
147
            'userId' => $userId,
148
            'username' => $newUsername,
149
        ]);
150
    }
151
152
    private function deleteCacheAdoptions(int $userId): void
153
    {
154
        $this->connection->executeQuery('DELETE FROM cache_adoptions WHERE from_user_id = :userId OR to_user_id = :userId', [
155
            'userId' => $userId,
156
        ]);
157
    }
158
159
    private function deleteUserOptions(int $userId): void
160
    {
161
        $this->connection->executeQuery('DELETE FROM user_options WHERE user_id = :userId', [
162
            'userId' => $userId,
163
        ]);
164
    }
165
166 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...
167
    {
168
        $this->connection->executeQuery('DELETE FROM cache_lists WHERE user_id = :userId', [
169
            'userId' => $userId,
170
        ]);
171
172
        $this->connection->executeQuery('DELETE FROM cache_list_watches WHERE user_id = :userId', [
173
            'userId' => $userId,
174
        ]);
175
176
        $this->connection->executeQuery('DELETE FROM cache_list_bookmarks WHERE user_id = :userId', [
177
            'userId' => $userId,
178
        ]);
179
    }
180
181
    private function anonymizeCacheLogs(int $userId): void
182
    {
183
        $this->connection->executeQuery('UPDATE cache_logs SET text=\'-User gelöscht-\', gdpr_deletion = 1 WHERE user_id = :userId', [
184
            'userId' => $userId,
185
        ]);
186
    }
187
188
    private function deleteCacheLogSubTables(int $userId): void
189
    {
190
        $this->connection->executeQuery('DELETE FROM cache_logs_archived WHERE user_id = :userId', [
191
            'userId' => $userId,
192
        ]);
193
194
        $this->connection->executeQuery('DELETE FROM cache_logs_modified WHERE user_id = :userId', [
195
            'userId' => $userId,
196
        ]);
197
    }
198
199
    private function getCaches(int $userId): array
200
    {
201
        return $this->connection->fetchAll('SELECT * FROM caches WHERE user_id = :userId', [
202
            'userId' => $userId,
203
        ]);
204
    }
205
206
    private function getCacheLogs(int $userId): array
207
    {
208
        return $this->connection->fetchAll('SELECT * FROM cache_logs WHERE user_id = :userId', [
209
            'userId' => $userId,
210
        ]);
211
    }
212
213
    private function anonymizeCaches(array $caches): void
214
    {
215
        foreach ($caches as $cache) {
216
            $this->connection->executeQuery(<<<'SQL'
217
                UPDATE caches SET
218
                    name='-Cache nach DSGVO gelöscht-',
219
                    show_cachelists = 0,
220
                    status = 6,
221
                    wp_gc = '',
222
                    wp_gc_maintained ='',
223
                    gdpr_deletion = 1
224
                WHERE cache_id = :cacheId;
225
SQL
226
            , [
227
                'cacheId' => (int) $cache['cache_id'],
228
            ]);
229
230
            $this->connection->executeQuery(<<<'SQL'
231
                UPDATE cache_desc SET
232
                    `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',
233
                    desc_html = 1,
234
                    hint = '',
235
                    short_desc = ''
236
                WHERE cache_id = :cacheId
237
SQL
238
            , [
239
                'cacheId' => (int) $cache['cache_id'],
240
            ]);
241
242
            $this->connection->executeQuery('DELETE FROM cache_desc_modified WHERE cache_id = :cacheId', [
243
                'cacheId' => (int) $cache['cache_id'],
244
            ]);
245
246
            $this->connection->executeQuery('DELETE FROM cache_ignore WHERE cache_id = :cacheId', [
247
                'cacheId' => (int) $cache['cache_id'],
248
            ]);
249
250
            $this->connection->executeQuery('DELETE FROM caches_modified WHERE cache_id = :cacheId', [
251
                'cacheId' => (int) $cache['cache_id'],
252
            ]);
253
        }
254
    }
255
256
    public function fetchPictures(array $data, string $idField, int $objectType): array
257
    {
258
        if ($data === []) {
259
            return [];
260
        }
261
262
        $ids = array_map(static function (array $cache) use ($idField) {
263
            return (int) $cache[$idField];
264
        }, $data);
265
266
        $pictures = $this->connection->fetchAll('SELECT * FROM pictures WHERE object_id IN (' . implode(',', $ids) . ') AND object_type = :objectType', [
267
            'objectType' => $objectType,
268
        ]);
269
270
        $modifiedPictures = $this->connection->fetchAll('SELECT * FROM pictures_modified WHERE object_id IN (' . implode(',', $ids) . ') AND object_type = :objectType', [
271
            'objectType' => $objectType,
272
        ]);
273
274
        return array_merge($pictures, $modifiedPictures);
275
    }
276
277
    public function fetchPicturesModified(array $data, string $idField, int $objectType): array
278
    {
279
        if ($data === []) {
280
            return [];
281
        }
282
283
        $ids = array_map(static function (array $cache) use ($idField) {
284
            return (int) $cache[$idField];
285
        }, $data);
286
287
        return $this->connection->fetchAll('SELECT * FROM pictures_modified WHERE object_id IN (' . implode(',', $ids) . ') AND object_type = :objectType', [
288
            'objectType' => $objectType,
289
        ]);
290
    }
291
292
    private function deletePictures(array $pictures): void
293
    {
294
        foreach ($pictures as $picture) {
295
            $imagePath = parse_url($picture['url'], PHP_URL_PATH);
296
            @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...
297
298
            if (isset($picture['thumb_url'])) {
299
                $thumbPath = parse_url($picture['thumb_url'], PHP_URL_PATH);
300
                @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...
301
            }
302
303
            $this->connection->executeQuery('DELETE FROM pictures WHERE object_id = :objectId AND object_type = :objectType', [
304
                'objectId' => $picture['object_id'],
305
                'objectType' => $picture['object_type'],
306
            ]);
307
        }
308
    }
309
310
    private function deletePicturesModified(array $pictures): void
311
    {
312
        foreach ($pictures as $picture) {
313
            $imagePath = parse_url($picture['url'], PHP_URL_PATH);
314
315
            @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...
316
317
            $this->connection->executeQuery('DELETE FROM pictures_modified WHERE object_id = :objectId AND object_type = :objectType', [
318
                'objectId' => $picture['object_id'],
319
                'objectType' => $picture['object_type'],
320
            ]);
321
        }
322
    }
323
324
    private function deleteCacheIgnore(int $userId): void
325
    {
326
        $this->connection->executeQuery('DELETE FROM cache_ignore WHERE user_id = :userId', [
327
            'userId' => $userId,
328
        ]);
329
    }
330
331
    private function deleteEmailUser(int $userId): void
332
    {
333
        $this->connection->executeQuery('DELETE FROM email_user WHERE from_user_id = :userId OR to_user_id = :userId', [
334
            'userId' => $userId,
335
        ]);
336
    }
337
338
    private function deleteFieldNotes(int $userId): void
339
    {
340
        $this->connection->executeQuery('DELETE FROM field_note WHERE user_id = :userId', [
341
            'userId' => $userId,
342
        ]);
343
    }
344
345
    private function deleteLogEntries(int $userId): void
346
    {
347
        $this->connection->executeQuery('DELETE FROM logentries WHERE userid = :userId', [
348
            'userId' => $userId,
349
        ]);
350
    }
351
352
    private function deleteLogins(int $userId): void
353
    {
354
        $this->connection->executeQuery('DELETE FROM logins WHERE user_id = :userId', [
355
            'userId' => $userId,
356
        ]);
357
    }
358
359
    private function deleteOkapiAuthorizations(int $userId): void
360
    {
361
        $this->connection->executeQuery(<<<'SQL'
362
            DELETE oa, oc
363
            FROM okapi_authorizations oa
364
            INNER JOIN okapi_consumers oc ON oa.consumer_key = oc.`key`
365
            INNER JOIN okapi_nonces ono ON oa.consumer_key = ono.consumer_key
366
            INNER JOIN okapi_diagnostics od ON oa.consumer_key = od.consumer_key
367
            INNER JOIN okapi_cache oca ON oa.consumer_key = oca.`key`
368
            INNER JOIN okapi_stats_hourly osh ON oa.consumer_key = osh.consumer_key
369
            INNER JOIN okapi_stats_monthly osm ON oa.consumer_key = osm.consumer_key
370
            INNER JOIN okapi_stats_temp ost ON oa.consumer_key = ost.consumer_key
371
            INNER JOIN okapi_submitted_objects oso ON oa.consumer_key = oso.consumer_key
372
            INNER JOIN okapi_tokens ot ON oa.consumer_key = ot.consumer_key
373
            WHERE oa.user_id = :userId
374
SQL
375
, [
376
            'userId' => $userId,
377
        ]);
378
    }
379
380
    private function deleteQueries(int $userId): void
381
    {
382
        $this->connection->executeQuery('DELETE FROM queries WHERE user_id = :userId', [
383
            'userId' => $userId,
384
        ]);
385
    }
386
387 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...
388
    {
389
        $this->connection->executeQuery('DELETE FROM watches_logqueue WHERE user_id = :userId', [
390
            'userId' => $userId,
391
        ]);
392
393
        $this->connection->executeQuery('DELETE FROM watches_notified WHERE user_id = :userId', [
394
            'userId' => $userId,
395
        ]);
396
397
        $this->connection->executeQuery('DELETE FROM watches_waiting WHERE user_id = :userId', [
398
            'userId' => $userId,
399
        ]);
400
    }
401
}
402