Passed
Pull Request — master (#4676)
by Nils
05:33
created

UserHandlerTrait::generateNewUserStep50()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 72
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 38
c 2
b 0
f 0
nc 5
nop 2
dl 0
loc 72
rs 8.6897

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Teampass - a collaborative passwords manager.
4
 * ---
5
 * This file is part of the TeamPass project.
6
 * 
7
 * TeamPass is free software: you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, version 3 of the License.
10
 * 
11
 * TeamPass is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
 * 
19
 * Certain components of this file may be under different licenses. For
20
 * details, see the `licenses` directory or individual file headers.
21
 * ---
22
 * @file      UserHandlerTrait.php
23
 * @author    Nils Laumaillé ([email protected])
24
 * @copyright 2009-2025 Teampass.net
25
 * @license   GPL-3.0
26
 * @see       https://www.teampass.net
27
 */
28
29
use TeampassClasses\Language\Language;
30
31
trait UserHandlerTrait {
32
    abstract protected function completeTask();
33
34
    /**
35
     * Handle user build cache tree
36
     * @param array $arguments Useful arguments for the task
37
     * @return void
38
     */
39
    private function handleUserBuildCacheTree($arguments) {
40
        performVisibleFoldersHtmlUpdate($arguments['user_id']);
41
    }
42
43
44
    /**
45
     * Generate user keys
46
     * @param array $taskData Données de la tâche
47
     * @param array $arguments Arguments nécessaires pour la création des clés
48
     * @return void
49
     */
50
    private function generateUserKeys($arguments) {
51
        // Get all subtasks related to this task
52
        $subtasks = DB::query(
53
            'SELECT * FROM ' . prefixTable('background_subtasks') . ' WHERE task_id = %i AND is_in_progress = 0 ORDER BY `task` ASC',
54
            $this->taskId
55
        );
56
    
57
        if (empty($subtasks)) {
58
            if (LOG_TASKS=== true) $this->logger->log("No subtask was found for task {$this->taskId}");
1 ignored issue
show
introduced by
The condition LOG_TASKS === true is always false.
Loading history...
59
            return;
60
        }
61
    
62
        // Process each subtask
63
        foreach ($subtasks as $subtask) {
64
            if (LOG_TASKS=== true) $this->logger->log("Processing subtask {$subtask['increment_id']} for task {$this->taskId}");
65
            $this->processGenerateUserKeysSubtask($subtask, $arguments);
66
        }
67
    
68
        // Are all subtasks completed?
69
        $remainingSubtasks = DB::queryFirstField(
70
            'SELECT COUNT(*) FROM ' . prefixTable('background_subtasks') . ' WHERE task_id = %i AND is_in_progress = 0',
71
            $this->taskId
72
        );    
73
        if ($remainingSubtasks == 0) {
74
            $this->completeTask();
75
        }
76
    }
77
    
78
79
    /**
80
     * Process a subtask for generating user keys.
81
     * @param array $subtask The subtask to process.
82
     * @param array $arguments Arguments for the task.
83
     * @return void
84
     */
85
    private function processGenerateUserKeysSubtask(array $subtask, array $arguments) {
86
        try {
87
            $taskData = json_decode($subtask['task'], true);
88
            
89
            // Mark the subtask as in progress
90
            DB::update(
91
                prefixTable('background_subtasks'),
92
                [
93
                    'is_in_progress' => 1,
94
                    'updated_at' => time(),
95
                    'status' => 'in progress'
96
                ],
97
                'increment_id = %i',
98
                $subtask['increment_id']
99
            );
100
            
101
            if (LOG_TASKS=== true) $this->logger->log("Subtask is in progress: ".$taskData['step'], 'INFO');
1 ignored issue
show
introduced by
The condition LOG_TASKS === true is always false.
Loading history...
102
            switch ($taskData['step'] ?? '') {
103
                case 'step0':
104
                    $this->generateNewUserStep0($arguments);
105
                    break;
106
                case 'step20':
107
                    $this->generateNewUserStep20($taskData, $arguments);
108
                    break;
109
                case 'step30':
110
                    $this->generateNewUserStep30($taskData, $arguments);
111
                    break;
112
                case 'step40':
113
                    $this->generateNewUserStep40($taskData, $arguments);
114
                    break;
115
                case 'step50':
116
                    $this->generateNewUserStep50($taskData, $arguments);
117
                    break;
118
                case 'step60':
119
                    $this->generateNewUserStep60($arguments);
120
                    break;
121
                default:
122
                    throw new Exception("Type of subtask unknown: {$this->processType}");
123
            }
124
    
125
            // Mark subtask as completed
126
            DB::update(
127
                prefixTable('background_subtasks'),
128
                [
129
                    'is_in_progress' => -1,
130
                    'finished_at' => time(),
131
                    'status' => 'completed',
132
                ],
133
                'increment_id = %i',
134
                $subtask['increment_id']
135
            );
136
    
137
        } catch (Exception $e) {
138
            // Failure handling
139
            DB::update(
140
                prefixTable('background_subtasks'),
141
                [
142
                    'is_in_progress' => -1,
143
                    'finished_at' => time(),
144
                    'updated_at' => time(),
145
                    'status' => 'failed',
146
                    'error_message' => $e->getMessage(),
147
                ],
148
                'increment_id = %i',
149
                $subtask['increment_id']
150
            );
151
            
152
            $this->logger->log("Subtask {$subtask['increment_id']} failure: " . $e->getMessage(), 'ERROR');
153
        }
154
    }
155
    
156
157
    /**
158
     * Generate new user keys - step 0
159
     * @param array $arguments Arguments for the task
160
     * @return void
161
     */
162
    private function generateNewUserStep0($arguments) {
163
        // CLear old sharekeys
164
        if ($arguments['user_self_change'] === 0) {
165
            deleteUserObjetsKeys($arguments['new_user_id'], $this->settings);
166
        }
167
    }
168
169
170
    /**
171
     * Generate new user keys
172
     * @param array $taskData Task data
173
     * @param array $arguments Arguments for the task
174
     */
175
    private function generateNewUserStep20($taskData, $arguments) {
176
        // get user private key
177
        $ownerInfo = $this->getOwnerInfos($arguments['owner_id'], $arguments['creator_pwd']);
178
        $userInfo = $this->getOwnerInfos($arguments['new_user_id'], $arguments['new_user_pwd']);
179
        
180
        // Start transaction for better performance
181
        DB::startTransaction();
182
183
        // Loop on items
184
        $rows = DB::query(
185
            'SELECT id, pw, perso
186
            FROM ' . prefixTable('items') . '
187
            WHERE perso =  %i
188
            ORDER BY id ASC
189
            LIMIT ' . $taskData['index'] . ', ' . $taskData['nb'],
190
            ($arguments['only_personal_items'] ?? 0) === 1 ? 1 : 0
191
        );
192
193
        foreach ($rows as $record) {
194
            // Get itemKey from current user
195
            $currentUserKey = DB::queryFirstRow(
196
                'SELECT share_key, increment_id
197
                FROM ' . prefixTable('sharekeys_items') . '
198
                WHERE object_id = %i AND user_id = %i',
199
                $record['id'],
200
                (int) $record['perso'] === 0 ? $arguments['owner_id'] : $arguments['new_user_id']
201
            );
202
203
            // do we have any input? (#3481)
204
            if ($currentUserKey === null || count($currentUserKey) === 0) {
205
                continue;
206
            }
207
208
            // Decrypt itemkey with admin key
209
            $itemKey = decryptUserObjectKey(
210
                $currentUserKey['share_key'],
211
                (int) $record['perso'] === 0 ? $ownerInfo['private_key'] : $userInfo['private_key']
212
            );
213
            
214
            // Prevent to change key if its key is empty
215
            if (empty($itemKey) === true) {
216
                $share_key_for_item = '';
217
            } else {
218
                // Encrypt Item key
219
                $share_key_for_item = encryptUserObjectKey($itemKey, $userInfo['public_key']);
220
            }
221
            
222
            $currentUserKey = DB::queryFirstRow(
223
                'SELECT increment_id
224
                FROM ' . prefixTable('sharekeys_items') . '
225
                WHERE object_id = %i AND user_id = %i',
226
                $record['id'],
227
                $arguments['new_user_id']
228
            );
229
230
            if ($currentUserKey) {
231
                // NOw update
232
                DB::update(
233
                    prefixTable('sharekeys_items'),
234
                    array(
235
                        'share_key' => $share_key_for_item,
236
                    ),
237
                    'increment_id = %i',
238
                    $currentUserKey['increment_id']
239
                );
240
            } else {
241
                DB::insert(
242
                    prefixTable('sharekeys_items'),
243
                    array(
244
                        'object_id' => (int) $record['id'],
245
                        'user_id' => (int) $arguments['new_user_id'],
246
                        'share_key' => $share_key_for_item,
247
                    )
248
                );
249
            }
250
        }
251
252
        // Commit transaction
253
        DB::commit();
254
    }
255
256
257
    /**
258
     * Generate new user keys - step 30
259
     * @param array $taskData Task data
260
     * @param array $arguments Arguments for the task
261
     * @return void
262
     */
263
    private function generateNewUserStep30($taskData, $arguments) {
264
        // get user private key
265
        $ownerInfo = $this->getOwnerInfos($arguments['owner_id'], $arguments['creator_pwd']);
266
        $userInfo = $this->getOwnerInfos($arguments['new_user_id'], $arguments['new_user_pwd']);
267
268
        // Start transaction for better performance
269
        DB::startTransaction();
270
271
        // Loop on logs
272
        $rows = DB::query(
273
            'SELECT increment_id
274
            FROM ' . prefixTable('log_items') . '
275
            WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
276
            ORDER BY increment_id ASC
277
            LIMIT ' . $taskData['index'] . ', ' . $taskData['nb']
278
        );
279
        foreach ($rows as $record) {
280
            // Get itemKey from current user
281
            $currentUserKey = DB::queryFirstRow(
282
                'SELECT share_key
283
                FROM ' . prefixTable('sharekeys_logs') . '
284
                WHERE object_id = %i AND user_id = %i',
285
                $record['increment_id'],
286
                $arguments['owner_id']
287
            );
288
289
            // do we have any input? (#3481)
290
            if ($currentUserKey === null || count($currentUserKey) === 0) {
291
                continue;
292
            }
293
294
            // Decrypt itemkey with admin key
295
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $ownerInfo['private_key']);
296
297
            // Encrypt Item key
298
            $share_key_for_item = encryptUserObjectKey($itemKey, $userInfo['public_key']);
299
300
            // Save the key in DB
301
            if ($arguments['user_self_change'] === false) {
302
                DB::insert(
303
                    prefixTable('sharekeys_logs'),
304
                    array(
305
                        'object_id' => (int) $record['increment_id'],
306
                        'user_id' => (int) $arguments['new_user_id'],
307
                        'share_key' => $share_key_for_item,
308
                    )
309
                );
310
            } else {
311
                // Get itemIncrement from selected user
312
                if ((int) $arguments['new_user_id'] !== (int) $arguments['owner_id']) {
313
                    $currentUserKey = DB::queryFirstRow(
314
                        'SELECT increment_id
315
                        FROM ' . prefixTable('sharekeys_items') . '
316
                        WHERE object_id = %i AND user_id = %i',
317
                        $record['id'],
318
                        $arguments['new_user_id']
319
                    );
320
                }
321
322
                // NOw update
323
                DB::update(
324
                    prefixTable('sharekeys_logs'),
325
                    array(
326
                        'share_key' => $share_key_for_item,
327
                    ),
328
                    'increment_id = %i',
329
                    $currentUserKey['increment_id']
330
                );
331
            }
332
        }
333
334
        // Commit transaction
335
        DB::commit();
336
    }
337
338
339
    /**
340
     * Generate new user keys - step 40
341
     * @param array $taskData Task data
342
     * @param array $arguments Arguments for the task
343
     * @return void
344
     */
345
    private function generateNewUserStep40($taskData, $arguments) {
346
        // get user private key
347
        $ownerInfo = $this->getOwnerInfos($arguments['owner_id'], $arguments['creator_pwd']);
348
        $userInfo = $this->getOwnerInfos($arguments['new_user_id'], $arguments['new_user_pwd']);
349
350
        // Start transaction for better performance
351
        DB::startTransaction();
352
353
        // Loop on fields
354
        $rows = DB::query(
355
            'SELECT id
356
            FROM ' . prefixTable('categories_items') . '
357
            WHERE encryption_type = "teampass_aes"
358
            ORDER BY id ASC
359
            LIMIT ' . $taskData['index'] . ', ' . $taskData['nb']
360
        );
361
        foreach ($rows as $record) {
362
            // Get itemKey from current user
363
            $currentUserKey = DB::queryFirstRow(
364
                'SELECT share_key
365
                FROM ' . prefixTable('sharekeys_fields') . '
366
                WHERE object_id = %i AND user_id = %i',
367
                $record['id'],
368
                $arguments['owner_id']
369
            );
370
371
            if (isset($currentUserKey['share_key']) === true) {
372
                // Decrypt itemkey with admin key
373
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $ownerInfo['private_key']);
374
375
                // Encrypt Item key
376
                $share_key_for_item = encryptUserObjectKey($itemKey, $userInfo['public_key']);
377
378
                // Save the key in DB
379
                if ($arguments['user_self_change'] === false) {
380
                    DB::insert(
381
                        prefixTable('sharekeys_fields'),
382
                        array(
383
                            'object_id' => (int) $record['id'],
384
                            'user_id' => (int) $arguments['new_user_id'],
385
                            'share_key' => $share_key_for_item,
386
                        )
387
                    );
388
                } else {
389
                    // Get itemIncrement from selected user
390
                    if ((int) $arguments['new_user_id'] !== (int) $arguments['owner_id']) {
391
                        $currentUserKey = DB::queryFirstRow(
392
                            'SELECT increment_id
393
                            FROM ' . prefixTable('sharekeys_items') . '
394
                            WHERE object_id = %i AND user_id = %i',
395
                            $record['id'],
396
                            $arguments['new_user_id']
397
                        );
398
                    }
399
400
                    // NOw update
401
                    DB::update(
402
                        prefixTable('sharekeys_fields'),
403
                        array(
404
                            'share_key' => $share_key_for_item,
405
                        ),
406
                        'increment_id = %i',
407
                        $currentUserKey['increment_id']
408
                    );
409
                }
410
            }
411
        }
412
413
        // Commit transaction
414
        DB::commit();
415
    }
416
417
418
    /**
419
     * Generate new user keys - step 50
420
     * @param array $taskData Task data
421
     * @param array $arguments Arguments for the task
422
     * @return void
423
     */
424
    private function generateNewUserStep50($taskData, $arguments) {
425
        // get user private key
426
        $ownerInfo = $this->getOwnerInfos($arguments['owner_id'], $arguments['creator_pwd']);
427
        $userInfo = $this->getOwnerInfos($arguments['new_user_id'], $arguments['new_user_pwd']);
428
429
        // Start transaction for better performance
430
        DB::startTransaction();
431
432
        // Loop on suggestions
433
        $rows = DB::query(
434
            'SELECT id
435
            FROM ' . prefixTable('suggestion') . '
436
            ORDER BY id ASC
437
            LIMIT ' . $taskData['index'] . ', ' . $taskData['nb']
438
        );
439
        foreach ($rows as $record) {
440
            // Get itemKey from current user
441
            $currentUserKey = DB::queryFirstRow(
442
                'SELECT share_key
443
                FROM ' . prefixTable('sharekeys_suggestions') . '
444
                WHERE object_id = %i AND user_id = %i',
445
                $record['id'],
446
                $arguments['owner_id']
447
            );
448
449
            // do we have any input? (#3481)
450
            if ($currentUserKey === null || count($currentUserKey) === 0) {
451
                continue;
452
            }
453
454
            // Decrypt itemkey with admin key
455
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $ownerInfo['private_key']);
456
457
            // Encrypt Item key
458
            $share_key_for_item = encryptUserObjectKey($itemKey, $userInfo['public_key']);
459
460
            // Save the key in DB
461
            if ($arguments['user_self_change'] === false) {
462
                DB::insert(
463
                    prefixTable('sharekeys_suggestions'),
464
                    array(
465
                        'object_id' => (int) $record['id'],
466
                        'user_id' => (int) $arguments['new_user_id'],
467
                        'share_key' => $share_key_for_item,
468
                    )
469
                );
470
            } else {
471
                // Get itemIncrement from selected user
472
                if ((int) $arguments['new_user_id'] !== (int) $arguments['owner_id']) {
473
                    $currentUserKey = DB::queryFirstRow(
474
                        'SELECT increment_id
475
                        FROM ' . prefixTable('sharekeys_items') . '
476
                        WHERE object_id = %i AND user_id = %i',
477
                        $record['id'],
478
                        $arguments['new_user_id']
479
                    );
480
                }
481
482
                // NOw update
483
                DB::update(
484
                    prefixTable('sharekeys_suggestions'),
485
                    array(
486
                        'share_key' => $share_key_for_item,
487
                    ),
488
                    'increment_id = %i',
489
                    $currentUserKey['increment_id']
490
                );
491
            }
492
        }
493
494
        // Commit transaction
495
        DB::commit();
496
    }
497
498
499
    /**
500
     * Generate new user keys - step 60
501
     * @param array $arguments Arguments for the task
502
     */
503
    private function generateNewUserStep60($arguments) {
504
        $lang = new Language('english');
505
506
        // IF USER IS NOT THE SAME
507
        if ((int) $arguments['new_user_id'] === (int) $arguments['owner_id']) {
508
            return [
509
                'new_index' => 0,
510
                'new_action' => 'finished',
511
            ];
512
        }
513
        
514
        // update LOG
515
        logEvents(
516
            $this->settings,
517
            'user_mngt',
518
            'at_user_new_keys',
519
            TP_USER_ID,
520
            "",
521
            (string) $arguments['new_user_id']
522
        );
523
524
        // if done then send email to new user
525
        // get user info
526
        $userInfo = DB::queryFirstRow(
527
            'SELECT email, login, auth_type, special, lastname, name
528
            FROM ' . prefixTable('users') . '
529
            WHERE id = %i',
530
            $arguments['new_user_id']
531
        );
532
533
        // SEND EMAIL TO USER depending on context
534
        // Config 1: new user is a local user
535
        // Config 2: new user is an LDAP user
536
        // Config 3: send new password
537
        // COnfig 4: send new encryption code
538
        if (isset($arguments['send_email']) === true && (int) $arguments['send_email'] === 1) {
539
            sendMailToUser(
540
                filter_var($userInfo['email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
541
                // @scrutinizer ignore-type
542
                empty($arguments['email_body']) === false ? $arguments['email_body'] : $lang->get('email_body_user_config_1'),
543
                'TEAMPASS - ' . $lang->get('login_credentials'),
544
                (array) filter_var_array(
545
                    [
546
                        '#code#' => cryption($arguments['new_user_code'], '','decrypt', $this->settings)['string'],
547
                        '#lastname#' => isset($userInfo['name']) === true ? $userInfo['name'] : '',
548
                        '#login#' => isset($userInfo['login']) === true ? $userInfo['login'] : '',
549
                    ],
550
                    FILTER_SANITIZE_FULL_SPECIAL_CHARS
551
                ),
552
                false,
553
                $arguments['new_user_pwd']
554
            );
555
        }
556
            
557
        // Set user as ready for usage
558
        DB::update(
559
            prefixTable('users'),
560
            array(
561
                'is_ready_for_usage' => 1,
562
                'otp_provided' => isset($arguments['otp_provided_new_value']) === true && (int) $arguments['otp_provided_new_value'] === 1 ? $arguments['otp_provided_new_value'] : 0
563
            ),
564
            'id = %i',
565
            $arguments['new_user_id']
566
        );
567
    }
568
    
569
570
    /**
571
     * Get owner info
572
     * @param int $owner_id Owner ID
573
     * @param string $owner_pwd Owner password
574
     * @return array Owner information
575
     */
576
    private function getOwnerInfos(int $owner_id, string $owner_pwd) {
577
        $userInfo = DB::queryFirstRow(
578
            'SELECT pw, public_key, private_key, login, name
579
            FROM ' . prefixTable('users') . '
580
            WHERE id = %i',
581
            $owner_id
582
        );
583
584
        // decrypt owner password
585
        $pwd = cryption($owner_pwd, '','decrypt', $this->settings)['string'];
586
        // decrypt private key and send back
587
        return [
588
            'private_key' => decryptPrivateKey($pwd, $userInfo['private_key']),
589
            'public_key' => $userInfo['public_key'],
590
            'login' => $userInfo['login'],
591
            'name' => $userInfo['name'],
592
        ];
593
    }
594
}