Passed
Pull Request — master (#4822)
by Nils
06:14
created

generateOneTimeCode()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 61
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 35
nc 3
nop 1
dl 0
loc 61
rs 9.36
c 0
b 0
f 0

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
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      main.queries.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2025 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
use TeampassClasses\PasswordManager\PasswordManager;
33
use TeampassClasses\SessionManager\SessionManager;
34
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
35
use TeampassClasses\Language\Language;
36
use RobThree\Auth\TwoFactorAuth;
37
use TeampassClasses\PerformChecks\PerformChecks;
38
use TeampassClasses\ConfigManager\ConfigManager;
39
use TeampassClasses\EmailService\EmailService;
40
use TeampassClasses\EmailService\EmailSettings;
41
42
// Load functions
43
require_once 'main.functions.php';
44
45
loadClasses('DB');
46
$session = SessionManager::getSession();
47
$request = SymfonyRequest::createFromGlobals();
48
$lang = new Language($session->get('user-language') ?? 'english');
49
50
// TODO : ajouter un check sue l'envoi de la key
51
52
// Load config
53
$configManager = new ConfigManager();
54
$SETTINGS = $configManager->getAllSettings();
55
56
// Do checks
57
// Instantiate the class with posted data
58
$checkUserAccess = new PerformChecks(
59
    dataSanitizer(
60
        [
61
            'type' => htmlspecialchars($request->request->get('type', ''), ENT_QUOTES, 'UTF-8'),
62
        ],
63
        [
64
            'type' => 'trim|escape',
65
        ],
66
    ),
67
    [
68
        'user_id' => returnIfSet($session->get('user-id'), null),
69
        'user_key' => returnIfSet($session->get('key'), null),
70
    ]
71
);
72
// Handle the case
73
echo $checkUserAccess->caseHandler();
74
if (
75
    ($checkUserAccess->userAccessPage('home') === false ||
76
    $checkUserAccess->checkSession() === false)
77
    && in_array(filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS), ['get_teampass_settings', 'ga_generate_qr']) === false
78
) {
79
    // Not allowed page
80
    $session->set('system-error_code', ERR_NOT_ALLOWED);
81
    include $SETTINGS['cpassman_dir'] . '/error.php';
82
    exit;
83
}
84
85
// Define Timezone
86
date_default_timezone_set($SETTINGS['timezone'] ?? 'UTC');
87
set_time_limit(600);
88
89
// DO CHECKS
90
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
91
if (
92
    isset($post_type) === true
93
    && ($post_type === 'ga_generate_qr'
94
        || $post_type === 'get_teampass_settings')
95
) {
96
    // continue
97
    mainQuery($SETTINGS);
98
} elseif (
99
    $session->has('user-id') && null !== $session->get('user-id')
100
    && $checkUserAccess->userAccessPage('home') === false
101
) {
102
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
103
    include __DIR__.'/../error.php';
104
    exit();
105
} elseif (($session->has('user-id') && null !== $session->get('user-id')
106
        && $session->get('key') !== null)
107
    || (isset($post_type) === true
108
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES))
109
) {
110
    // continue
111
    mainQuery($SETTINGS);
112
} else {
113
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
114
    include __DIR__.'/../error.php';
115
    exit();
116
}
117
118
// Includes
119
include_once __DIR__.'/../sources/main.functions.php';
120
121
/**
122
 * Undocumented function.
123
 */
124
function mainQuery(array $SETTINGS)
125
{
126
    header('Content-type: text/html; charset=utf-8');
127
    header('Cache-Control: no-cache');
128
    error_reporting(E_ERROR);
129
130
    // Load libraries
131
    loadClasses('DB');
132
133
    // User's language loading
134
    $session = SessionManager::getSession();
135
    $lang = new Language($session->get('user-language') ?? 'english');
136
    $request = SymfonyRequest::createFromGlobals();
137
138
    // Prepare POST variables
139
    $inputData = dataSanitizer(
140
        [
141
            'type' => $request->request->filter('type', '', FILTER_SANITIZE_SPECIAL_CHARS),
142
            'data' => $request->request->filter('data', '', FILTER_SANITIZE_SPECIAL_CHARS),
143
            'key' => $request->request->filter('key', '', FILTER_SANITIZE_SPECIAL_CHARS),
144
            'type_category' => $request->request->filter('type_category', '', FILTER_SANITIZE_SPECIAL_CHARS),
145
        ],
146
        [
147
            'type' => 'trim|escape',
148
            'data' => 'trim|escape',
149
            'key' => 'trim|escape',
150
            'type_category' => 'trim|escape',
151
        ]
152
    );
153
    
154
    // Check KEY
155
    if (isValueSetNullEmpty($inputData['key']) === true) {
156
        echo prepareExchangedData(
157
            array(
158
                'error' => true,
159
                'message' => $lang->get('key_is_not_correct'),
160
            ),
161
            'encode',
162
            $inputData['key']
163
        );
164
        return false;
165
    }
166
    // decrypt and retreive data in JSON format
167
    $dataReceived = empty($inputData['data']) === false ? prepareExchangedData(
168
        $inputData['data'],
169
        'decode'
170
    ) : '';
171
    
172
    switch ($inputData['type_category']) {
173
        case 'action_password':
174
            echo passwordHandler($inputData['type'], $dataReceived, $SETTINGS);
175
            break;
176
177
        case 'action_user':
178
            echo userHandler($inputData['type'], $dataReceived, $SETTINGS, $inputData['key']);
179
            break;
180
181
        case 'action_mail':
182
            echo mailHandler($inputData['type'], $dataReceived, $SETTINGS);
183
            break;
184
185
        case 'action_key':
186
            // deepcode ignore ServerLeak: All cases handled by keyHandler return an encrypted string that is sent back to the client
187
            echo keyHandler($inputData['type'], $dataReceived, $SETTINGS);
188
            break;
189
190
        case 'action_system':
191
            echo systemHandler($inputData['type'], $dataReceived, $SETTINGS);
192
            break;
193
194
        case 'action_utils':
195
            echo utilsHandler($inputData['type'], $dataReceived, $SETTINGS);
196
            break;
197
    }
198
    
199
}
200
201
/**
202
 * Handler for all password tasks
203
 *
204
 * @param string $post_type
205
 * @param array|null|string $dataReceived
206
 * @param array $SETTINGS
207
 * @return string
208
 */
209
function passwordHandler(string $post_type, /*php8 array|null|string*/ $dataReceived, array $SETTINGS): string
210
{
211
    $session = SessionManager::getSession();
212
    $lang = new Language($session->get('user-language') ?? 'english');
213
214
    switch ($post_type) {
215
        case 'change_pw'://action_password
216
            return changePassword(
217
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
218
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '',
219
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
220
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
221
                (int) $session->get('user-id'),
222
                $SETTINGS
223
            );
224
225
        /*
226
         * Change user's authentication password
227
         */
228
        case 'change_user_auth_password'://action_password
229
230
            // Check new password and confirm match server side
231
            if ($dataReceived['new_password'] !== $dataReceived['new_password_confirm']) {
232
                return prepareExchangedData(
233
                    array(
234
                        'error' => true,
235
                        'message' => $lang->get('error_bad_credentials'),
236
                    ),
237
                    'encode'
238
                );
239
            }
240
241
            // Check if new password is strong
242
            if (!isPasswordStrong($dataReceived['new_password'])) {
243
                return prepareExchangedData(
244
                    array(
245
                        'error' => true,
246
                        'message' => $lang->get('complexity_level_not_reached'),
247
                    ),
248
                    'encode'
249
                );
250
            }
251
252
            return changeUserAuthenticationPassword(
253
                (int) $session->get('user-id'),
254
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
255
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
256
                $SETTINGS
257
            );
258
259
        /*
260
         * User's authentication password in LDAP has changed
261
         */
262
        case 'change_user_ldap_auth_password'://action_password
263
264
            // Users passwords are html escaped
265
            $userPassword = filter_var($dataReceived['current_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
266
267
            // Get current user hash
268
            $userHash = DB::queryFirstRow(
269
                "SELECT pw FROM " . prefixtable('users') . " WHERE id = %d;",
270
                $session->get('user-id')
271
            )['pw'];
272
273
            $passwordManager = new PasswordManager();
274
275
            // Verify provided user password
276
            if (!$passwordManager->verifyPassword($userHash, $userPassword)) {
277
                return prepareExchangedData(
278
                    array(
279
                        'error' => true,
280
                        'message' => $lang->get('error_bad_credentials'),
281
                    ),
282
                    'encode'
283
                );
284
            }
285
286
            return /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
287
                (int) $session->get('user-id'),
288
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
289
                filter_var($userPassword)
290
            );
291
292
        /*
293
         * test_current_user_password_is_correct
294
         */
295
        case 'test_current_user_password_is_correct'://action_password
296
            return isUserPasswordCorrect(
297
                (int) $session->get('user-id'),
298
                (string) $dataReceived['password'],
299
                $SETTINGS
300
            );
301
302
        /*
303
         * Default case
304
         */
305
        default :
306
            return prepareExchangedData(
307
                array(
308
                    'error' => true,
309
                ),
310
                'encode'
311
            );
312
    }
313
}
314
315
/**
316
 * Handler for all user tasks
317
 *
318
 * @param string $post_type
319
 * @param array|null|string $dataReceived
320
 * @param array $SETTINGS
321
 * @param string $post_key
322
 * @return string
323
 */
324
function userHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS, string $post_key): string
325
{
326
    $session = SessionManager::getSession();
327
328
    // List of post types allowed to all users
329
    $all_users_can_access = [
330
        'get_user_info',
331
        'increase_session_time',
332
        'generate_password',
333
        'refresh_list_items_seen',
334
        'ga_generate_qr',
335
        'user_get_session_time',
336
        'save_user_location'
337
    ];
338
339
    // Default values
340
    $filtered_user_id = $session->get('user-id');
341
342
    // User can't manage users and requested type is administrative.
343
    if ((int) $session->get('user-admin') !== 1 &&
344
        (int) $session->get('user-manager') !== 1 &&
345
        (int) $session->get('user-can_manage_all_users') !== 1 &&
346
        !in_array($post_type, $all_users_can_access)) {
347
348
        return prepareExchangedData(
349
            array(
350
                'error' => true,
351
            ),
352
            'encode'
353
        );
354
    }
355
356
    if (isset($dataReceived['user_id'])) {
357
        // Get info about user to modify
358
        $targetUserInfos = DB::queryFirstRow(
359
            'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
360
            WHERE id = %i',
361
            $dataReceived['user_id']
362
        );
363
364
        if (
365
            // Administrator user
366
            (int) $session->get('user-admin') === 1
367
            // Manager of basic/ro users in this role
368
            || ((int) $session->get('user-manager') === 1
369
                && in_array($targetUserInfos['isAdministratedByRole'], $session->get('user-roles_array'))
370
                && (int) $targetUserInfos['admin'] !== 1
371
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
372
                && (int) $targetUserInfos['gestionnaire'] !== 1)
373
            // Manager of all basic/ro users
374
            || ((int) $session->get('user-can_manage_all_users') === 1
375
                && (int) $targetUserInfos['admin'] !== 1
376
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
377
                && (int) $targetUserInfos['gestionnaire'] !== 1)
378
        ) {
379
            // This user is allowed to modify other users.
380
            $filtered_user_id = $dataReceived['user_id'];
381
        }
382
    }
383
384
    switch ($post_type) {
385
        /*
386
        * Get info 
387
        */
388
        case 'get_user_info'://action_user
389
            return getUserInfo(
390
                (int) $filtered_user_id,
391
                $SETTINGS
392
            );
393
394
        /*
395
        * Increase the session time of User
396
        */
397
        case 'increase_session_time'://action_user
398
            return increaseSessionDuration(
399
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
400
            );
401
402
        /*
403
        * Generate a password generic
404
        */
405
        case 'generate_password'://action_user
406
            return generateGenericPassword(
407
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
408
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_VALIDATE_BOOLEAN),
409
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN),
410
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_VALIDATE_BOOLEAN),
411
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_VALIDATE_BOOLEAN),
412
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN),
413
                $SETTINGS
414
            );
415
416
        /*
417
        * Refresh list of last items seen
418
        */
419
        case 'refresh_list_items_seen'://action_user
420
            if ($session->has('user-id') || (int) $session->get('user-id') && null !== $session->get('user-id') || (int) $session->get('user-id') > 0) {
421
                return refreshUserItemsSeenList(
422
                    $SETTINGS
423
                );
424
425
            } else {
426
                return json_encode(
427
                    array(
428
                        'error' => '',
429
                        'existing_suggestions' => 0,
430
                        'html_json' => '',
431
                    ),
432
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
433
                );
434
            }
435
436
        /*
437
        * This will generate the QR Google Authenticator
438
        */
439
        case 'ga_generate_qr'://action_user
440
            require_once 'core/services/QRCodeService.php';
441
            return (new QRCodeService($SETTINGS))->generateForUser([
442
                'id' => (int) $filtered_user_id,
443
                'origin' => (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
444
                'send_mail' => (bool) filter_var($dataReceived['send_email'], FILTER_VALIDATE_BOOLEAN),
445
                'login' => (string) filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
446
                'password' => (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
447
                'token' => (string) filter_var($dataReceived['token'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
448
            ]);
449
450
        /*
451
        * This will set the user ready
452
        */
453
        case 'user_is_ready'://action_user
454
            return userIsReady(
455
                (int) $filtered_user_id,
456
                (string) $SETTINGS['cpassman_dir']
457
            );
458
459
        /*
460
        * This post type is used to check if the user session is still valid
461
        */
462
        case 'user_get_session_time'://action_user
463
            return userGetSessionTime(
464
                (int) $session->get('user-id'),
465
                (string) $SETTINGS['cpassman_dir'],
466
                (int) $SETTINGS['maximum_session_expiration_time'],
467
            );
468
469
        case 'save_user_location'://action_user
470
            return userSaveIp(
471
                (int) $session->get('user-id'),
472
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
473
            );
474
475
        /*
476
        * Default case
477
        */
478
        default :
479
            return prepareExchangedData(
480
                array(
481
                    'error' => true,
482
                ),
483
                'encode'
484
            );
485
    }
486
}
487
488
/**
489
 * Handler for all mail tasks
490
 *
491
 * @param string $post_type
492
 * @param array|null|string $dataReceived
493
 * @param array $SETTINGS
494
 * @return string
495
 */
496
function mailHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
497
{
498
    $session = SessionManager::getSession();
499
500
    switch ($post_type) {
501
        /*
502
         * CASE
503
         * Send email
504
         */
505
        case 'mail_me'://action_mail
506
            // Get info about user to send email
507
            $data_user = DB::queryFirstRow(
508
                'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
509
                WHERE email = %s',
510
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
511
            );
512
513
            // Unknown email address
514
            if (!$data_user) {
515
                return prepareExchangedData(
516
                    array(
517
                        'error' => true,
518
                    ),
519
                    'encode'
520
                );
521
            }
522
523
            // Only administrators and managers can send mails
524
            if (
525
                // Administrator user
526
                (int) $session->get('user-admin') === 1
527
                // Manager of basic/ro users in this role
528
                || ((int) $session->get('user-manager') === 1
529
                    && in_array($data_user['isAdministratedByRole'], $session->get('user-roles_array'))
530
                    && (int) $data_user['admin'] !== 1
531
                    && (int) $data_user['can_manage_all_users'] !== 1
532
                    && (int) $data_user['gestionnaire'] !== 1)
533
                // Manager of all basic/ro users
534
                || ((int) $session->get('user-can_manage_all_users') === 1
535
                    && (int) $data_user['admin'] !== 1
536
                    && (int) $data_user['can_manage_all_users'] !== 1
537
                    && (int) $data_user['gestionnaire'] !== 1)
538
            ) {
539
                return sendMailToUser(
540
                    filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
541
                    $dataReceived['body'],
542
                    (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
543
                    (array) filter_var_array(
544
                        $dataReceived['pre_replace'],
545
                        FILTER_SANITIZE_FULL_SPECIAL_CHARS
546
                    ),
547
                    true
548
                );
549
            }
550
551
            return prepareExchangedData(
552
                array(
553
                    'error' => true,
554
                ),
555
                'encode'
556
            );
557
        /*
558
        * Send emails not sent
559
        */
560
        case 'send_waiting_emails'://mail
561
            // Administrative task
562
            if ((int) $session->get('user-admin') !== 1) {
563
                return prepareExchangedData(
564
                    array(
565
                        'error' => true,
566
                    ),
567
                    'encode'
568
                );
569
            }
570
571
            sendEmailsNotSent(
572
                $SETTINGS
573
            );
574
            return prepareExchangedData(
575
                array(
576
                    'error' => false,
577
                    'message' => 'mail_sent',
578
                ),
579
                'encode'
580
            );
581
582
        /*
583
        * Default case
584
        */
585
        default :
586
            return prepareExchangedData(
587
                array(
588
                    'error' => true,
589
                ),
590
                'encode'
591
            );
592
    }
593
}
594
595
/**
596
 * Handler for all key related tasks
597
 *
598
 * @param string $post_type
599
 * @param array|null|string $dataReceived
600
 * @param array $SETTINGS
601
 * @return string
602
 */
603
function keyHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
604
{
605
    $session = SessionManager::getSession();
606
    $lang = new Language($session->get('user-language') ?? 'english');
607
608
    // List of post types allowed to all users
609
    $all_users_can_access = [
610
        'change_private_key_encryption_password',
611
        'user_new_keys_generation',
612
        'user_recovery_keys_download',
613
    ];
614
615
    // Default values
616
    $filtered_user_id = $session->get('user-id');
617
618
    if (isset($dataReceived['user_id'])) {
619
        // Get info about user to modify
620
        $targetUserInfos = DB::queryFirstRow(
621
            'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
622
            WHERE id = %i',
623
            $dataReceived['user_id']
624
        );
625
    
626
        if (
627
            // Administrator user
628
            (int) $session->get('user-admin') === 1
629
            // Manager of basic/ro users in this role
630
            || ((int) $session->get('user-manager') === 1
631
                && in_array($targetUserInfos['isAdministratedByRole'], $session->get('user-roles_array'))
632
                && (int) $targetUserInfos['admin'] !== 1
633
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
634
                && (int) $targetUserInfos['gestionnaire'] !== 1)
635
            // Manager of all basic/ro users
636
            || ((int) $session->get('user-can_manage_all_users') === 1
637
                && (int) $targetUserInfos['admin'] !== 1
638
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
639
                && (int) $targetUserInfos['gestionnaire'] !== 1)
640
        ) {
641
            // This user is allowed to modify other users.
642
            $filtered_user_id = $dataReceived['user_id'];
643
    
644
        } else if (!in_array($post_type, $all_users_can_access)) {
645
            // User can't manage users and requested type is administrative.
646
            return prepareExchangedData(
647
                array(
648
                    'error' => true,
649
                ),
650
                'encode'
651
            ); 
652
        }
653
    }
654
655
    switch ($post_type) {
656
        /*
657
         * Generate a temporary encryption key for user
658
         */
659
        case 'generate_temporary_encryption_key'://action_key
660
            require_once 'core/services/OneTimeCodeService.php';
661
            return (new OneTimeCodeService())->generateForUser((int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT));
662
663
        /*
664
         * user_sharekeys_reencryption_next
665
         */
666
        case 'user_sharekeys_reencryption_next'://action_key
667
            return continueReEncryptingUserSharekeys(
668
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
669
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
670
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
671
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
672
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
673
                $SETTINGS
674
            );
675
676
        /*
677
         * user_psk_reencryption
678
         */
679
        case 'user_psk_reencryption'://action_key
680
            return migrateTo3_DoUserPersonalItemsEncryption(
681
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
682
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
683
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
684
                (int) filter_var($dataReceived['counterItemsToTreat'], FILTER_SANITIZE_NUMBER_INT),
685
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
686
                $SETTINGS
687
            );
688
689
        /*
690
         * User's public/private keys change
691
         */
692
        case 'change_private_key_encryption_password'://action_key
693
694
            // Users passwords are html escaped
695
            $newPassword = filter_var($dataReceived['new_code'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
696
697
            // Get current user hash
698
            $userHash = DB::queryFirstRow(
699
                "SELECT pw FROM " . prefixtable('users') . " WHERE id = %d;",
700
                $session->get('user-id')
701
            )['pw'];
702
703
            $passwordManager = new PasswordManager();
704
705
            // Verify provided user password
706
            if (!$passwordManager->verifyPassword($userHash, $newPassword)) {
707
                return prepareExchangedData(
708
                    array(
709
                        'error' => true,
710
                        'message' => $lang->get('error_bad_credentials'),
711
                    ),
712
                    'encode'
713
                );
714
            }
715
716
            return changePrivateKeyEncryptionPassword(
717
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
718
                (string) $dataReceived['current_code'],
719
                (string) $newPassword,
720
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
721
                $SETTINGS
722
            );
723
724
        /*
725
         * Launch user keys change on his demand
726
         */
727
        case 'user_new_keys_generation'://action_key
728
729
            // Users passwords are html escaped
730
            $userPassword = filter_var($dataReceived['user_pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
731
732
            // Don't generate new user password -> verify it
733
            if ($dataReceived['generate_user_new_password'] !== true) {
734
735
                // Get current user hash
736
                $userHash = DB::queryFirstRow(
737
                    "SELECT pw FROM " . prefixtable('users') . " WHERE id = %d;",
738
                    $session->get('user-id')
739
                )['pw'];
740
741
                $passwordManager = new PasswordManager();
742
743
                // Verify provided user password
744
                if (!$passwordManager->verifyPassword($userHash, $userPassword)) {
745
                    return prepareExchangedData(
746
                        array(
747
                            'error' => true,
748
                            'message' => $lang->get('error_bad_credentials'),
749
                        ),
750
                        'encode'
751
                    );
752
                }
753
            }
754
755
            return handleUserKeys(
756
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
757
                (string) $userPassword,
758
                (int) isset($SETTINGS['maximum_number_of_items_to_treat']) === true ? $SETTINGS['maximum_number_of_items_to_treat'] : NUMBER_ITEMS_IN_BATCH,
759
                (string) filter_var($dataReceived['encryption_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
760
                (bool) filter_var($dataReceived['delete_existing_keys'], FILTER_VALIDATE_BOOLEAN),
761
                (bool) filter_var($dataReceived['send_email_to_user'], FILTER_VALIDATE_BOOLEAN),
762
                (bool) filter_var($dataReceived['encrypt_with_user_pwd'], FILTER_VALIDATE_BOOLEAN),
763
                (bool) isset($dataReceived['generate_user_new_password']) === true ? filter_var($dataReceived['generate_user_new_password'], FILTER_VALIDATE_BOOLEAN) : false,
764
                (string) filter_var($dataReceived['email_body'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
765
                (bool) filter_var($dataReceived['user_self_change'], FILTER_VALIDATE_BOOLEAN),
766
                (string) filter_var($dataReceived['recovery_public_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
767
                (string) filter_var($dataReceived['recovery_private_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
768
            );
769
770
        /*
771
         * Launch user recovery download
772
         */
773
        case 'user_recovery_keys_download'://action_key
774
            // Validate user password on local and LDAP accounts before download
775
            if ($session->get('user-auth_type') !== 'oauth2') {
776
                // Users passwords are html escaped
777
                $userPassword = filter_var($dataReceived['password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
778
779
                // Get current user hash
780
                $userHash = DB::queryFirstRow(
781
                    "SELECT pw FROM " . prefixtable('users') . " WHERE id = %i;",
782
                    $session->get('user-id')
783
                )['pw'];
784
785
                $passwordManager = new PasswordManager();
786
787
                // Verify provided user password
788
                if (!$passwordManager->verifyPassword($userHash, $userPassword)) {
789
                    return prepareExchangedData(
790
                        array(
791
                            'error' => true,
792
                            'message' => $lang->get('error_bad_credentials'),
793
                        ),
794
                        'encode'
795
                    );
796
                }
797
            }
798
799
            return handleUserRecoveryKeysDownload(
800
                (int) $filtered_user_id,
801
                (array) $SETTINGS,
802
            );
803
804
        /*
805
         * Default case
806
         */
807
        default :
808
            return prepareExchangedData(
809
                array(
810
                    'error' => true,
811
                ),
812
                'encode'
813
            );
814
    }
815
}
816
817
/**
818
 * Handler for all system tasks
819
 *
820
 * @param string $post_type Type of action to perform
821
 * @param mixed $dataReceived Data received for processing
822
 * @param array $SETTINGS Application settings
823
 * @return string JSON-encoded response
824
 */
825
function systemHandler(string $post_type, $dataReceived, array $SETTINGS): string
826
{
827
    try {
828
        $session = SessionManager::getSession();
829
830
        switch ($post_type) {
831
            case 'get_number_of_items_to_treat':
832
                return handleGetNumberOfItems($dataReceived, $SETTINGS);
833
            case 'sending_statistics':
834
                return handleSendingStatistics($SETTINGS);
835
            case 'generate_bug_report':
836
                return handleGenerateBugReport($dataReceived, $SETTINGS, $session);
837
            case 'get_teampass_settings':
838
                return handleGetTeampassSettings($SETTINGS);
839
            case 'save_token':
840
                return handleSaveToken($session);
841
            default:
842
                return prepareErrorResponse('Unknown action type');
843
        }
844
    } catch (Exception $e) {
845
        return prepareErrorResponse($e->getMessage());
846
    }
847
}
848
849
/**
850
 * Handles the 'get_number_of_items_to_treat' action
851
 */
852
function handleGetNumberOfItems($dataReceived, array $SETTINGS): string
853
{
854
    $userId = getValidatedInput($dataReceived, 'user_id', FILTER_SANITIZE_NUMBER_INT);
855
    if ($userId === false) {
856
        return prepareErrorResponse('Invalid User ID');
857
    }
858
859
    return prepareSuccessResponse([
860
        'count' => getNumberOfItemsToTreat((int)$userId, $SETTINGS)
861
    ]);
862
}
863
864
/**
865
 * Handles the 'sending_statistics' action
866
 */
867
function handleSendingStatistics(array $SETTINGS): string
868
{
869
    sendingStatistics($SETTINGS);
870
    return prepareSuccessResponse();
871
}
872
873
/**
874
 * Handles the 'generate_bug_report' action
875
 */
876
function handleGenerateBugReport($dataReceived, array $SETTINGS, $session): string
877
{
878
    if ((int) $session->get('user-admin') !== 1) {
879
        return prepareSuccessResponse([]); // Return empty success for non-admins
880
    }
881
882
    if (!is_array($dataReceived)) {
883
        return prepareErrorResponse('Invalid data format');
884
    }
885
886
    $reportContent = generateBugReport($dataReceived, $SETTINGS);
887
    if (empty($reportContent)) {
888
        return prepareErrorResponse('Failed to generate bug report');
889
    }
890
891
    return prepareSuccessResponse([
892
        'report' => $reportContent
893
    ]);
894
}
895
896
/**
897
 * Handles the 'get_teampass_settings' action
898
 */
899
function handleGetTeampassSettings(array $SETTINGS): string
900
{
901
    $allowedSettings = [
902
        'ldap_user_attribute', 'enable_pf_feature', 'clipboard_life_duration',
903
        'enable_favourites', 'copy_to_clipboard_small_icons', 'enable_attachment_encryption',
904
        'google_authentication', 'agses_authentication_enabled', 'yubico_authentication',
905
        'duo', 'personal_saltkey_security_level', 'enable_tasks_manager',
906
        'insert_manual_entry_item_history', 'show_item_data'
907
    ];
908
909
    return prepareSuccessResponse([
910
        'settings' => array_intersect_key($SETTINGS, array_flip($allowedSettings))
911
    ]);
912
}
913
914
/**
915
 * Handles the 'save_token' action
916
 */
917
function handleSaveToken($session): string
918
{
919
    $tokenParams = [
920
        'size' => FILTER_SANITIZE_NUMBER_INT,
921
        'secure' => FILTER_VALIDATE_BOOLEAN,
922
        'numeric' => FILTER_VALIDATE_BOOLEAN,
923
        'capital' => FILTER_VALIDATE_BOOLEAN,
924
        'symbols' => FILTER_VALIDATE_BOOLEAN,
925
        'lowercase' => FILTER_VALIDATE_BOOLEAN,
926
        'duration' => FILTER_SANITIZE_NUMBER_INT,
927
        'reason' => FILTER_SANITIZE_FULL_SPECIAL_CHARS
928
    ];
929
930
    $params = [];
931
    foreach ($tokenParams as $name => $filter) {
932
        $params[$name] = getPostInput($name, $filter);
933
    }
934
935
    $defaults = [
936
        'size' => 20,
937
        'secure' => false,
938
        'numeric' => false,
939
        'capital' => false,
940
        'symbols' => false,
941
        'lowercase' => false,
942
        'duration' => 3600,
943
        'reason' => ''
944
    ];
945
946
    // Apply defaults where needed
947
    foreach ($defaults as $key => $value) {
948
        if ($params[$key] === null) {
949
            $params[$key] = $value;
950
        }
951
    }
952
953
    // Generate token
954
    $passwordManager = new PasswordManager();
955
    $token = $passwordManager->generatePassword(
956
        (int)$params['size'],
957
        (bool)$params['secure'],
958
        (bool)$params['numeric'],
959
        (bool)$params['capital'],
960
        (bool)$params['symbols'],
961
        (bool)$params['lowercase']
962
    );
963
964
    DB::insert(prefixTable('tokens'), [
965
        'user_id' => (int) $session->get('user-id'),
966
        'token' => $token,
967
        'reason' => $params['reason'],
968
        'creation_timestamp' => time(),
969
        'end_timestamp' => time() + (int)$params['duration'],
970
    ]);
971
972
    return prepareSuccessResponse(['token' => $token]);
973
}
974
975
/**
976
 * Helper function to get and validate input
977
 */
978
function getValidatedInput(array $data, string $key, int $filter)
979
{
980
    if (!isset($data[$key])) {
981
        return false;
982
    }
983
    return filter_var($data[$key], $filter);
984
}
985
986
/**
987
 * Helper function to get POST input with default value
988
 */
989
function getPostInput(string $name, int $filter)
990
{
991
    return filter_input(INPUT_POST, $name, $filter);
992
}
993
994
/**
995
 * Prepares a success response
996
 */
997
function prepareSuccessResponse(array $data = []): string
998
{
999
    $response = array_merge(['error' => false], $data);
1000
    return prepareExchangedData($response, 'encode');
1001
}
1002
1003
/**
1004
 * Prepares an error response
1005
 */
1006
function prepareErrorResponse(string $message): string
1007
{
1008
    return prepareExchangedData([
1009
        'error' => true,
1010
        'message' => $message
1011
    ], 'encode');
1012
}
1013
1014
1015
1016
function utilsHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
1017
{
1018
    switch ($post_type) {
1019
        /*
1020
         * generate_an_otp
1021
         */
1022
        case 'generate_an_otp'://action_utils
1023
            return generateAnOTP(
1024
                (string) filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
1025
                (bool) filter_var($dataReceived['with_qrcode'], FILTER_VALIDATE_BOOLEAN),
1026
                (string) filter_var($dataReceived['secret_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
1027
            );
1028
1029
1030
        /*
1031
         * Default case
1032
         */
1033
        default :
1034
            return prepareExchangedData(
1035
                array(
1036
                    'error' => true,
1037
                ),
1038
                'encode'
1039
            );
1040
    }
1041
}
1042
1043
/**
1044
 * Permits to set the user ready
1045
 *
1046
 * @param integer $userid
1047
 * @param string $dir
1048
 * @return string
1049
 */
1050
function userIsReady(int $userid, string $dir): string
1051
{
1052
    DB::update(
1053
        prefixTable('users'),
1054
        array(
1055
            'is_ready_for_usage' => 1,
1056
        ),
1057
        'id = %i',
1058
        $userid
1059
    );
1060
1061
    // Send back
1062
    return prepareExchangedData(
1063
        array(
1064
            'error' => false,
1065
        ),
1066
        'encode'
1067
    ); 
1068
}
1069
1070
1071
/**
1072
 * Permits to set the user ready
1073
 *
1074
 * @param integer $userid
1075
 * @return string
1076
 */
1077
function userGetSessionTime(int $userid, string $dir, int $maximum_session_expiration_time): string
1078
{
1079
    $session = SessionManager::getSession();
1080
    // Send back
1081
    return prepareExchangedData(
1082
        array(
1083
            'error' => false,
1084
            'timestamp' => $session->get('user-session_duration'),
1085
            'max_time_to_add' => intdiv((($maximum_session_expiration_time*60) - ((int) $session->get('user-session_duration') - time())), 60),
1086
            'max_session_duration' => $maximum_session_expiration_time,
1087
        ),
1088
        'encode'
1089
    ); 
1090
}
1091
1092
/**
1093
 * Save the user's IP
1094
 *
1095
 * @param integer $userID
1096
 * @param string $action
1097
 * @return string
1098
 */
1099
function userSaveIp(int $userID, string $action): string
1100
{
1101
    if ($action === 'perform') {
1102
        DB::update(
1103
            prefixTable('users'),
1104
            array(
1105
                'user_ip' => getClientIpServer(),
1106
                'user_ip_lastdate' => time(),
1107
            ),
1108
            'id = %i',
1109
            $userID
1110
        );
1111
    }
1112
1113
    return prepareExchangedData(
1114
        array(
1115
            'error' => false,
1116
        ),
1117
        'encode'
1118
    );
1119
}
1120
1121
/**
1122
 * Provides the number of items
1123
 *
1124
 * @param int   $userId     User ID
1125
 * @param array $SETTINGS   TeampassSettings
1126
 *
1127
 * @return string
1128
 */
1129
function getNumberOfItemsToTreat(
1130
    int $userId,
1131
    array $SETTINGS
1132
): string
1133
{
1134
    // get number of items
1135
    DB::queryFirstRow(
1136
        'SELECT increment_id
1137
        FROM ' . prefixTable('sharekeys_items') .
1138
        ' WHERE user_id = %i',
1139
        $userId
1140
    );
1141
1142
    // Send back
1143
    return prepareExchangedData(
1144
        array(
1145
            'error' => false,
1146
            'nbItems' => DB::count(),
1147
        ),
1148
        'encode'
1149
    );
1150
}
1151
1152
1153
/**
1154
 * 
1155
 */
1156
function changePassword(
1157
    string $post_new_password,
1158
    string $post_current_password,
1159
    int $post_password_complexity,
1160
    string $post_change_request,
1161
    int $post_user_id,
1162
    array $SETTINGS
1163
): string
1164
{
1165
    $session = SessionManager::getSession();
1166
    
1167
    // Create password hash
1168
    $passwordManager = new PasswordManager();
1169
    $post_new_password_hashed = $passwordManager->hashPassword($post_new_password);
1170
1171
    // Load user's language
1172
    $lang = new Language($session->get('user-language') ?? 'english');
1173
1174
    // User has decided to change is PW
1175
    if ($post_change_request === 'reset_user_password_expected'
1176
        || $post_change_request === 'user_decides_to_change_password'
1177
    ) {
1178
        // Check that current user is correct
1179
        if ((int) $post_user_id !== (int) $session->get('user-id')) {
1180
            return prepareExchangedData(
1181
                array(
1182
                    'error' => true,
1183
                    'message' => $lang->get('error_not_allowed_to'),
1184
                ),
1185
                'encode'
1186
            );
1187
        }
1188
1189
        // check if expected security level is reached
1190
        $dataUser = DB::queryFirstRow(
1191
            'SELECT *
1192
            FROM ' . prefixTable('users') . ' WHERE id = %i',
1193
            $post_user_id
1194
        );
1195
1196
        // check if badly written
1197
        $dataUser['fonction_id'] = array_filter(
1198
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
1199
        );
1200
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
1201
        DB::update(
1202
            prefixTable('users'),
1203
            array(
1204
                'fonction_id' => $dataUser['fonction_id'],
1205
            ),
1206
            'id = %i',
1207
            $post_user_id
1208
        );
1209
1210
        if (empty($dataUser['fonction_id']) === false) {
1211
            $data = DB::queryFirstRow(
1212
                'SELECT complexity
1213
                FROM ' . prefixTable('roles_title') . '
1214
                WHERE id IN (' . $dataUser['fonction_id'] . ')
1215
                ORDER BY complexity DESC'
1216
            );
1217
        } else {
1218
            // In case user has no roles yet
1219
            $data = array();
1220
            $data['complexity'] = 0;
1221
        }
1222
1223
        if ((int) $post_password_complexity < (int) $data['complexity']) {
1224
            return prepareExchangedData(
1225
                array(
1226
                    'error' => true,
1227
                    'message' => '<div style="margin:10px 0 10px 15px;">' . $lang->get('complexity_level_not_reached') . '.<br>' .
1228
                        $lang->get('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
1229
                ),
1230
                'encode'
1231
            );
1232
        }
1233
1234
        // Check that the 2 passwords are differents
1235
        if ($post_current_password === $post_new_password) {
1236
            return prepareExchangedData(
1237
                array(
1238
                    'error' => true,
1239
                    'message' => $lang->get('password_already_used'),
1240
                ),
1241
                'encode'
1242
            );
1243
        }
1244
1245
        // update sessions
1246
        $session->set('user-last_pw_change', mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')));
1247
        $session->set('user-validite_pw', 1);
1248
1249
        // BEfore updating, check that the pwd is correct
1250
        if ($passwordManager->verifyPassword($post_new_password_hashed, $post_new_password) === true && empty($dataUser['private_key']) === false) {
1251
            $special_action = 'none';
1252
            if ($post_change_request === 'reset_user_password_expected') {
1253
                $session->set('user-private_key', decryptPrivateKey($post_current_password, $dataUser['private_key']));
1254
            }
1255
1256
            // update DB
1257
            DB::update(
1258
                prefixTable('users'),
1259
                array(
1260
                    'pw' => $post_new_password_hashed,
1261
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
1262
                    'last_pw' => $post_current_password,
1263
                    'special' => $special_action,
1264
                    'private_key' => encryptPrivateKey($post_new_password, $session->get('user-private_key')),
1265
                ),
1266
                'id = %i',
1267
                $post_user_id
1268
            );
1269
            // update LOG
1270
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $session->get('user-id'), $session->get('user-login'), $post_user_id);
1271
1272
            // Send back
1273
            return prepareExchangedData(
1274
                array(
1275
                    'error' => false,
1276
                    'message' => '',
1277
                ),
1278
                'encode'
1279
            );
1280
        }
1281
        // Send back
1282
        return prepareExchangedData(
1283
            array(
1284
                'error' => true,
1285
                'message' => $lang->get('pw_hash_not_correct'),
1286
            ),
1287
            'encode'
1288
        );
1289
    }
1290
    return prepareExchangedData(
1291
        array(
1292
            'error' => true,
1293
            'message' => $lang->get('error_not_allowed_to'),
1294
        ),
1295
        'encode'
1296
    );
1297
}
1298
1299
/**
1300
 * Sends emails that were not sent yet
1301
 *
1302
 * @param array $SETTINGS Application settings
1303
 */
1304
function sendEmailsNotSent(
1305
    array $SETTINGS
1306
)
1307
{
1308
    $emailSettings = new EmailSettings($SETTINGS);
1309
    $emailService = new EmailService();
1310
1311
    if (isKeyExistingAndEqual('enable_send_email_on_user_login', 1, $SETTINGS) === true) {
1312
        $row = DB::queryFirstRow(
1313
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
1314
            'cron',
1315
            'sending_emails'
1316
        );
1317
1318
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
1319
            $rows = DB::query(
1320
                'SELECT *
1321
                FROM ' . prefixTable('emails') .
1322
                ' WHERE status != %s',
1323
                'sent'
1324
            );
1325
            foreach ($rows as $record) {
1326
                // Send email
1327
                $ret = json_decode(
1328
                    $emailService->sendMail(
1329
                        $record['subject'],
1330
                        $record['body'],
1331
                        $record['receivers'],
1332
                        $emailSettings
1333
                    ),
1334
                    true
1335
                );
1336
1337
                // update item_id in files table
1338
                DB::update(
1339
                    prefixTable('emails'),
1340
                    array(
1341
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
1342
                    ),
1343
                    'timestamp = %s',
1344
                    $record['timestamp']
1345
                );
1346
            }
1347
        }
1348
        // update cron time
1349
        DB::update(
1350
            prefixTable('misc'),
1351
            array(
1352
                'valeur' => time(),
1353
                'updated_at' => time(),
1354
            ),
1355
            'intitule = %s AND type = %s',
1356
            'sending_emails',
1357
            'cron'
1358
        );
1359
    }
1360
}
1361
1362
/**
1363
 * Refreshes the list of items seen by the user
1364
 *
1365
 * @param array $SETTINGS Application settings
1366
 * @return string JSON-encoded response containing the list of items seen
1367
 */
1368
function refreshUserItemsSeenList(
1369
    array $SETTINGS
1370
): string
1371
{
1372
    $session = SessionManager::getSession();
1373
1374
    // get list of last items seen
1375
    $arr_html = array();
1376
    $rows = DB::query(
1377
        'SELECT i.id AS id, i.label AS label, i.id_tree AS id_tree, l.date, i.perso AS perso, i.restricted_to AS restricted
1378
        FROM ' . prefixTable('log_items') . ' AS l
1379
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
1380
        WHERE l.action = %s AND l.id_user = %i
1381
        ORDER BY l.date DESC
1382
        LIMIT 0, 100',
1383
        'at_shown',
1384
        $session->get('user-id')
1385
    );
1386
    if (DB::count() > 0) {
1387
        foreach ($rows as $record) {
1388
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
1389
                array_push(
1390
                    $arr_html,
1391
                    array(
1392
                        'id' => $record['id'],
1393
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
1394
                        'tree_id' => $record['id_tree'],
1395
                        'perso' => $record['perso'],
1396
                        'restricted' => $record['restricted'],
1397
                    )
1398
                );
1399
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
1400
                    break;
1401
                }
1402
            }
1403
        }
1404
    }
1405
1406
    // get wainting suggestions
1407
    $nb_suggestions_waiting = 0;
1408
    if (isKeyExistingAndEqual('enable_suggestion', 1, $SETTINGS) === true
1409
        && ((int) $session->get('user-admin') === 1 || (int) $session->get('user-manager') === 1)
1410
    ) {
1411
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1412
        $nb_suggestions_waiting = DB::count();
1413
    }
1414
1415
    return json_encode(
1416
        array(
1417
            'error' => '',
1418
            'existing_suggestions' => $nb_suggestions_waiting,
1419
            'html_json' => $arr_html,
1420
        ),
1421
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1422
    );
1423
}
1424
1425
/**
1426
 * Sends statistics to the Teampass Statistics database
1427
 *
1428
 * @param array $SETTINGS Application settings
1429
 */
1430
function sendingStatistics(
1431
    array $SETTINGS
1432
): void
1433
{
1434
    $session = SessionManager::getSession();
1435
    if (
1436
        isSetArrayOfValues([$SETTINGS['send_statistics_items'], $SETTINGS['send_stats_time']]) === true
1437
        && isKeyExistingAndEqual('send_stats', 1, $SETTINGS) === true
1438
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1439
    ) {
1440
        // get statistics data
1441
        $stats_data = getStatisticsData($SETTINGS);
1442
1443
        // get statistics items to share
1444
        $statsToSend = [];
1445
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1446
        $statsToSend['timestamp'] = time();
1447
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1448
            if ($data === 'stat_languages') {
1449
                $tmp = '';
1450
                foreach ($stats_data[$data] as $key => $value) {
1451
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1452
                }
1453
                $statsToSend[$data] = $tmp;
1454
            } elseif ($data === 'stat_country') {
1455
                $tmp = '';
1456
                foreach ($stats_data[$data] as $key => $value) {
1457
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1458
                }
1459
                $statsToSend[$data] = $tmp;
1460
            } else {
1461
                $statsToSend[$data] = $stats_data[$data];
1462
            }
1463
        }
1464
1465
        // connect to Teampass Statistics database
1466
        $link2 = new MeekroDB(
1467
            'teampass.pw',
1468
            'teampass_user',
1469
            'ZMlEfRzKzFLZNzie',
1470
            'teampass_followup',
1471
            '3306',
1472
            'utf8'
1473
        );
1474
1475
        $link2->insert(
1476
            'statistics',
1477
            $statsToSend
1478
        );
1479
1480
        // update table misc with current timestamp
1481
        DB::update(
1482
            prefixTable('misc'),
1483
            array(
1484
                'valeur' => time(),
1485
                'updated_at' => time(),
1486
            ),
1487
            'type = %s AND intitule = %s',
1488
            'admin',
1489
            'send_stats_time'
1490
        );
1491
1492
        //permits to test only once by session
1493
        $session->set('system-send_stats_done', 1);
1494
        $SETTINGS['send_stats_time'] = time();
1495
    }
1496
}
1497
1498
/**
1499
 * Generates a bug report based on the provided data and settings
1500
 *
1501
 * @param array $data Data related to the bug report
1502
 * @param array $SETTINGS Application settings
1503
 * @return string The generated bug report content
1504
 */
1505
function generateBugReport(
1506
    array $data,
1507
    array $SETTINGS
1508
): string
1509
{
1510
    $excludedVars = array(
1511
        'bck_script_passkey',
1512
        'email_smtp_server',
1513
        'email_auth_username',
1514
        'email_auth_pwd',
1515
        'email_from',
1516
        'onthefly-restore-key',
1517
        'onthefly-backup-key',
1518
        'ldap_password',
1519
        'ldap_hosts',
1520
        'proxy_ip',
1521
        'ldap_bind_passwd',
1522
        'syslog_host',
1523
        'duo_akey',
1524
        'duo_ikey',
1525
        'duo_skey',
1526
        'duo_host',
1527
        'oauth2_client_id',
1528
        'oauth2_tenant_id',
1529
        'oauth2_client_secret',
1530
        'oauth2_client_token',
1531
        'oauth2_client_endpoint',
1532
    );
1533
1534
    // Load user's language
1535
    $session = SessionManager::getSession();
1536
    $lang = new Language($session->get('user-language') ?? 'english');
1537
1538
    // Anonymisation URL
1539
    $urlFound = $SETTINGS['cpassman_url'] ?? '';
1540
    $anonymUrl = '';
1541
    if (!empty($urlFound)) {
1542
        $parsed = parse_url($urlFound);
1543
        $anonymUrl = $parsed['scheme'] . '://<anonym_url>' . ($parsed['path'] ?? '');
1544
    }
1545
1546
    // Filtrage et tri des réglages
1547
    $listOfOptions = [];
1548
    ksort($SETTINGS);
1549
1550
    foreach ($SETTINGS as $key => $value) {
1551
        if (in_array($key, $excludedVars, true)) {
1552
            $value = '<removed>';
1553
        } elseif (!empty($anonymUrl)) {
1554
            $value = str_replace($urlFound, $anonymUrl, (string) $value);
1555
        }
1556
1557
        $listOfOptions[] = "'$key' => '$value'";
1558
    }
1559
    $listOfOptions = implode("\n", $listOfOptions);
1560
1561
    // Récupère dernière erreur PHP
1562
    $phpError = error_get_last();
1563
    $phpErrorMsg = $phpError
1564
        ? $phpError['message'] . ' - ' . $phpError['file'] . ' (' . $phpError['line'] . ')'
1565
        : $lang->get('no_error_logged');
1566
1567
    // Dernières erreurs Teampass
1568
    $teampassErrors = [];
1569
    $errors = DB::query(
1570
        'SELECT label, date AS error_date
1571
         FROM ' . prefixTable('log_system') . "
1572
         WHERE `type` = 'error'
1573
         ORDER BY `date` DESC
1574
         LIMIT 10"
1575
    );
1576
1577
    foreach ($errors as $record) {
1578
        $teampassErrors[] = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1579
    }
1580
    $teampassErrors = count($teampassErrors) > 0 ? implode("\n", $teampassErrors) : $lang->get('no_error_logged');
1581
1582
    // Version DB
1583
    if (!defined('DB_PASSWD_CLEAR')) {
1584
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD));
1585
    }
1586
    $dbLink = @mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1587
    $dbVersion = $dbLink ? mysqli_get_server_info($dbLink) : $lang->get('undefined');
1588
1589
    // Now prepare text
1590
    $reportContent = <<<REPORT
1591
### Page on which it happened
1592
{$data['current_page']}
1593
1594
### Steps to reproduce
1595
1.
1596
2.
1597
3.
1598
1599
### Expected behaviour
1600
Tell us what should happen
1601
1602
1603
### Actual behaviour
1604
Tell us what happens instead
1605
1606
### Server configuration
1607
**Operating system**: {php_uname()}
1608
**Web server**: {$_SERVER['SERVER_SOFTWARE']}
1609
**Database**: {$dbVersion}
1610
**PHP version**: {PHP_VERSION}
1611
**Teampass version**: {TP_VERSION}.{TP_VERSION_MINOR}
1612
1613
**Teampass configuration variables:**
1614
```
1615
{$listOfOptions}
1616
```
1617
1618
**Updated from an older Teampass or fresh install:**
1619
1620
### Client configuration
1621
**Browser**: {$data['browser_name']} - {$data['browser_version']}
1622
**Operating system**: {$data['os']} - {$data['os_archi']} bits
1623
1624
### Logs
1625
1626
#### Web server error log
1627
```
1628
{$phpErrorMsg}
1629
```
1630
1631
#### Teampass 10 last system errors
1632
```
1633
{$teampassErrors}
1634
```
1635
1636
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1637
```
1638
Insert the log here and especially the answer of the query that failed.
1639
```
1640
REPORT;
1641
1642
    return $reportContent;
1643
}
1644
1645
/**
1646
 * Check that the user password is valid
1647
 *
1648
 * @param integer $post_user_id
1649
 * @param string $post_user_password
1650
 * @param array $SETTINGS
1651
 * @return string
1652
 */
1653
function isUserPasswordCorrect(
1654
    int $post_user_id,
1655
    string $post_user_password,
1656
    array $SETTINGS
1657
): string
1658
{
1659
    $session = SessionManager::getSession();
1660
    // Load user's language
1661
    $lang = new Language($session->get('user-language') ?? 'english');
1662
    
1663
    if (isUserIdValid($post_user_id) === true) {
1664
        // Check if user exists
1665
        $userInfo = DB::queryFirstRow(
1666
            'SELECT public_key, private_key, pw, auth_type
1667
            FROM ' . prefixTable('users') . '
1668
            WHERE id = %i',
1669
            $post_user_id
1670
        );
1671
        if (DB::count() > 0 && empty($userInfo['private_key']) === false) {
1672
            // Get itemKey from current user
1673
            // Get one item
1674
            $currentUserKey = DB::queryFirstRow(
1675
                'SELECT object_id, share_key, increment_id
1676
                FROM ' . prefixTable('sharekeys_items') . ' AS si
1677
                INNER JOIN ' . prefixTable('items') . ' AS i ON  (i.id = si.object_id)
1678
                INNER JOIN ' . prefixTable('nested_tree') . ' AS nt ON  (i.id_tree = nt.id)
1679
                WHERE user_id = %i AND nt.personal_folder = %i',
1680
                $post_user_id,
1681
                0
1682
            );
1683
            
1684
            if (DB::count() === 0) {
1685
                // This user has no items
1686
                // let's consider no items in DB
1687
                return prepareExchangedData(
1688
                    array(
1689
                        'error' => false,
1690
                        'message' => '',
1691
                        'debug' => '',
1692
                    ),
1693
                    'encode'
1694
                );
1695
            }
1696
1697
            if ($currentUserKey !== null) {
1698
                // Decrypt itemkey with user key
1699
                // use old password to decrypt private_key
1700
                $session->set('user-private_key', decryptPrivateKey($post_user_password, $userInfo['private_key']));
1701
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
1702
1703
                //echo $post_user_password."  --  ".$userInfo['private_key']. ";;";
1704
1705
                if (empty(base64_decode($itemKey)) === false) {
1706
                    // GOOD password
1707
                    return prepareExchangedData(
1708
                        array(
1709
                            'error' => false,
1710
                            'message' => '',
1711
                            'debug' => '',
1712
                        ),
1713
                        'encode'
1714
                    );
1715
                }
1716
            }
1717
1718
            // use the password check
1719
            $passwordManager = new PasswordManager();
1720
            
1721
            if ($passwordManager->verifyPassword($userInfo['pw'], htmlspecialchars_decode($post_user_password)) === true) {
1722
                // GOOD password
1723
                return prepareExchangedData(
1724
                    array(
1725
                        'error' => false,
1726
                        'message' => '',
1727
                        'debug' => '',
1728
                    ),
1729
                    'encode'
1730
                );
1731
            }
1732
        }
1733
    }
1734
1735
    return prepareExchangedData(
1736
        array(
1737
            'error' => true,
1738
            'message' => $lang->get('password_is_not_correct'),
1739
        ),
1740
        'encode'
1741
    );
1742
}
1743
1744
function changePrivateKeyEncryptionPassword(
1745
    int $post_user_id,
1746
    string $post_current_code,
1747
    string $post_new_code,
1748
    string $post_action_type,
1749
    array $SETTINGS
1750
): string
1751
{
1752
    $session = SessionManager::getSession();
1753
    // Load user's language
1754
    $lang = new Language($session->get('user-language') ?? 'english');
1755
    
1756
    if (empty($post_new_code) === true) {
1757
        // no user password
1758
        return prepareExchangedData(
1759
            array(
1760
                'error' => true,
1761
                'message' => $lang->get('error_bad_credentials'),
1762
                'debug' => '',
1763
            ),
1764
            'encode'
1765
        );
1766
    }
1767
1768
    if (isUserIdValid($post_user_id) === true) {
1769
        // Get user info
1770
        $userData = DB::queryFirstRow(
1771
            'SELECT private_key
1772
            FROM ' . prefixTable('users') . '
1773
            WHERE id = %i',
1774
            $post_user_id
1775
        );
1776
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
1777
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1778
                // Here the user has his private key encrypted with an OTC.
1779
                // We need to encrypt it with his real password
1780
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1781
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1782
            } else {
1783
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1784
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1785
            }
1786
1787
            // Should fail here to avoid break user private key.
1788
            if (strlen($privateKey) === 0 || strlen($hashedPrivateKey) < 30) {
1789
                if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1790
                    error_log("Error reencrypt user private key. User ID: {$post_user_id}, Given OTP: '{$post_current_code}'");
1791
                }
1792
                return prepareExchangedData(
1793
                    array(
1794
                        'error' => true,
1795
                        'message' => $lang->get('error_otp_secret'),
1796
                        'debug' => '',
1797
                    ),
1798
                    'encode'
1799
                );
1800
            }
1801
1802
            // Update user account
1803
            DB::update(
1804
                prefixTable('users'),
1805
                array(
1806
                    'private_key' => $hashedPrivateKey,
1807
                    'special' => 'none',
1808
                    'otp_provided' => 1,
1809
                ),
1810
                'id = %i',
1811
                $post_user_id
1812
            );
1813
1814
            $session->set('user-private_key', $privateKey);
1815
        }
1816
1817
        // Return
1818
        return prepareExchangedData(
1819
            array(
1820
                'error' => false,
1821
                'message' => '',
1822
            ),
1823
            'encode'
1824
        );
1825
    }
1826
    
1827
    return prepareExchangedData(
1828
        array(
1829
            'error' => true,
1830
            'message' => $lang->get('error_no_user'),
1831
            'debug' => '',
1832
        ),
1833
        'encode'
1834
    );
1835
}
1836
1837
function initializeUserPassword(
1838
    int $post_user_id,
1839
    string $post_special,
1840
    string $post_user_password,
1841
    bool $post_self_change,
1842
    array $SETTINGS
1843
): string
1844
{
1845
    // Load user's language
1846
    $session = SessionManager::getSession();
1847
    $lang = new Language($session->get('user-language') ?? 'english');
1848
    
1849
    if (isUserIdValid($post_user_id) === true) {
1850
        // Get user info
1851
        $userData = DB::queryFirstRow(
1852
            'SELECT email, auth_type, login
1853
            FROM ' . prefixTable('users') . '
1854
            WHERE id = %i',
1855
            $post_user_id
1856
        );
1857
        if (DB::count() > 0 && empty($userData['email']) === false) {
1858
            // If user pwd is empty then generate a new one and send it to user
1859
            if (empty($post_user_password) === true) {
1860
                // Generate new password
1861
                $post_user_password = generateQuickPassword();
1862
            }
1863
1864
            // If LDAP enabled, then
1865
            // check that this password is correct
1866
            $continue = true;
1867
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1868
                $continue = ldapCheckUserPassword(
1869
                    $userData['login'],
1870
                    $post_user_password,
1871
                    $SETTINGS
1872
                );
1873
            }
1874
1875
            if ($continue === true) {
1876
                // Only change if email is successfull
1877
                $passwordManager = new PasswordManager();
1878
                // GEnerate new keys
1879
                $userKeys = generateUserKeys($post_user_password);
1880
1881
                // Update user account
1882
                DB::update(
1883
                    prefixTable('users'),
1884
                    array(
1885
                        'special' => $post_special,
1886
                        'pw' => $passwordManager->hashPassword($post_user_password),
1887
                        'public_key' => $userKeys['public_key'],
1888
                        'private_key' => $userKeys['private_key'],
1889
                        'last_pw_change' => time(),
1890
                    ),
1891
                    'id = %i',
1892
                    $post_user_id
1893
                );
1894
1895
                // Return
1896
                return prepareExchangedData(
1897
                    array(
1898
                        'error' => false,
1899
                        'message' => '',
1900
                        'user_pwd' => $post_user_password,
1901
                        'user_email' => $userData['email'],
1902
                    ),
1903
                    'encode'
1904
                );
1905
            }
1906
            // Return error
1907
            return prepareExchangedData(
1908
                array(
1909
                    'error' => true,
1910
                    'message' => $lang->get('no_email_set'),
1911
                    'debug' => '',
1912
                    'self_change' => $post_self_change,
1913
                ),
1914
                'encode'
1915
            );
1916
        }
1917
1918
        // Error
1919
        return prepareExchangedData(
1920
            array(
1921
                'error' => true,
1922
                'message' => $lang->get('no_email_set'),
1923
                'debug' => '',
1924
            ),
1925
            'encode'
1926
        );
1927
    }
1928
    
1929
    return prepareExchangedData(
1930
        array(
1931
            'error' => true,
1932
            'message' => $lang->get('error_no_user'),
1933
            'debug' => '',
1934
        ),
1935
        'encode'
1936
    );
1937
}
1938
1939
/**
1940
 * Permits to start re-encrypting user's sharekeys
1941
 *
1942
 * @param integer $post_user_id
1943
 * @param boolean $post_self_change
1944
 * @param array $SETTINGS
1945
 * @return string
1946
 */
1947
function startReEncryptingUserSharekeys(
1948
    int $post_user_id,
1949
    bool $post_self_change,
1950
    array $SETTINGS
1951
): string
1952
{
1953
    // Load user's language
1954
    $session = SessionManager::getSession();
1955
    $lang = new Language($session->get('user-language') ?? 'english');
1956
    
1957
    if (isUserIdValid($post_user_id) === true) {
1958
        // Check if user exists
1959
        DB::queryFirstRow(
1960
            'SELECT *
1961
            FROM ' . prefixTable('users') . '
1962
            WHERE id = %i',
1963
            $post_user_id
1964
        );
1965
        if (DB::count() > 0) {
1966
            // CLear old sharekeys
1967
            if ($post_self_change === false) {
1968
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
1969
            }
1970
1971
            // Continu with next step
1972
            return prepareExchangedData(
1973
                array(
1974
                    'error' => false,
1975
                    'message' => '',
1976
                    'step' => 'step1',
1977
                    'userId' => $post_user_id,
1978
                    'start' => 0,
1979
                    'self_change' => $post_self_change,
1980
                ),
1981
                'encode'
1982
            );
1983
        }
1984
        // Nothing to do
1985
        return prepareExchangedData(
1986
            array(
1987
                'error' => true,
1988
                'message' => $lang->get('error_no_user'),
1989
            ),
1990
            'encode'
1991
        );
1992
    }
1993
1994
    return prepareExchangedData(
1995
        array(
1996
            'error' => true,
1997
            'message' => $lang->get('error_no_user'),
1998
        ),
1999
        'encode'
2000
    );
2001
}
2002
2003
/**
2004
 * Permits to encrypt user's keys
2005
 *
2006
 * @param integer $post_user_id
2007
 * @param boolean $post_self_change
2008
 * @param string $post_action
2009
 * @param integer $post_start
2010
 * @param integer $post_length
2011
 * @param array $SETTINGS
2012
 * @return string
2013
 */
2014
function continueReEncryptingUserSharekeys(
2015
    int     $post_user_id,
2016
    bool    $post_self_change,
2017
    string  $post_action,
2018
    int     $post_start,
2019
    int     $post_length,
2020
    array   $SETTINGS
2021
): string
2022
{
2023
    // Load user's language
2024
    $session = SessionManager::getSession();
2025
    $lang = new Language($session->get('user-language') ?? 'english');
2026
    
2027
    if (isUserIdValid($post_user_id) === true) {
2028
        // Check if user exists
2029
        $userInfo = DB::queryFirstRow(
2030
            'SELECT public_key
2031
            FROM ' . prefixTable('users') . '
2032
            WHERE id = %i',
2033
            $post_user_id
2034
        );
2035
        if (isset($userInfo['public_key']) === true) {
2036
            $return = [];
2037
2038
            // WHAT STEP TO PERFORM?
2039
            if ($post_action === 'step0') {
2040
                // CLear old sharekeys
2041
                if ($post_self_change === false) {
2042
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
2043
                }
2044
2045
                $return['post_action'] = 'step10';
2046
            }
2047
            
2048
            // STEP 1 - ITEMS
2049
            elseif ($post_action === 'step10') {
2050
                $return = continueReEncryptingUserSharekeysStep10(
2051
                    $post_user_id,
2052
                    $post_self_change,
2053
                    $post_action,
2054
                    $post_start,
2055
                    $post_length,
2056
                    $userInfo['public_key'],
2057
                    $SETTINGS
2058
                );
2059
            }
2060
2061
            // STEP 2 - LOGS
2062
            elseif ($post_action === 'step20') {
2063
                $return = continueReEncryptingUserSharekeysStep20(
2064
                    $post_user_id,
2065
                    $post_self_change,
2066
                    $post_action,
2067
                    $post_start,
2068
                    $post_length,
2069
                    $userInfo['public_key'],
2070
                    $SETTINGS
2071
                );
2072
            }
2073
2074
            // STEP 3 - FIELDS
2075
            elseif ($post_action === 'step30') {
2076
                $return = continueReEncryptingUserSharekeysStep30(
2077
                    $post_user_id,
2078
                    $post_self_change,
2079
                    $post_action,
2080
                    $post_start,
2081
                    $post_length,
2082
                    $userInfo['public_key'],
2083
                    $SETTINGS
2084
                );
2085
            }
2086
            
2087
            // STEP 4 - SUGGESTIONS
2088
            elseif ($post_action === 'step40') {
2089
                $return = continueReEncryptingUserSharekeysStep40(
2090
                    $post_user_id,
2091
                    $post_self_change,
2092
                    $post_action,
2093
                    $post_start,
2094
                    $post_length,
2095
                    $userInfo['public_key'],
2096
                    $SETTINGS
2097
                );
2098
            }
2099
            
2100
            // STEP 5 - FILES
2101
            elseif ($post_action === 'step50') {
2102
                $return = continueReEncryptingUserSharekeysStep50(
2103
                    $post_user_id,
2104
                    $post_self_change,
2105
                    $post_action,
2106
                    $post_start,
2107
                    $post_length,
2108
                    $userInfo['public_key'],
2109
                    $SETTINGS
2110
                );
2111
            }
2112
            
2113
            // STEP 6 - PERSONAL ITEMS
2114
            elseif ($post_action === 'step60') {
2115
                $return = continueReEncryptingUserSharekeysStep60(
2116
                    $post_user_id,
2117
                    $post_self_change,
2118
                    $post_action,
2119
                    $post_start,
2120
                    $post_length,
2121
                    $userInfo['public_key'],
2122
                    $SETTINGS
2123
                );
2124
            }
2125
            
2126
            // Continu with next step
2127
            return prepareExchangedData(
2128
                array(
2129
                    'error' => false,
2130
                    'message' => '',
2131
                    'step' => isset($return['post_action']) === true ? $return['post_action'] : '',
2132
                    'start' => isset($return['next_start']) === true ? $return['next_start'] : 0,
2133
                    'userId' => $post_user_id,
2134
                    'self_change' => $post_self_change,
2135
                ),
2136
                'encode'
2137
            );
2138
        }
2139
        
2140
        // Nothing to do
2141
        return prepareExchangedData(
2142
            array(
2143
                'error' => false,
2144
                'message' => '',
2145
                'step' => 'finished',
2146
                'start' => 0,
2147
                'userId' => $post_user_id,
2148
                'self_change' => $post_self_change,
2149
            ),
2150
            'encode'
2151
        );
2152
    }
2153
    
2154
    // Nothing to do
2155
    return prepareExchangedData(
2156
        array(
2157
            'error' => true,
2158
            'message' => $lang->get('error_no_user'),
2159
            'extra' => $post_user_id,
2160
        ),
2161
        'encode'
2162
    );
2163
}
2164
2165
function continueReEncryptingUserSharekeysStep10(
2166
    int $post_user_id,
2167
    bool $post_self_change,
2168
    string $post_action,
2169
    int $post_start,
2170
    int $post_length,
2171
    string $user_public_key,
2172
    array $SETTINGS
2173
): array 
2174
{
2175
    $session = SessionManager::getSession();
2176
    // Loop on items
2177
    $rows = DB::query(
2178
        'SELECT id, pw
2179
        FROM ' . prefixTable('items') . '
2180
        WHERE perso = 0
2181
        LIMIT ' . $post_start . ', ' . $post_length
2182
    );
2183
    foreach ($rows as $record) {
2184
        // Get itemKey from current user
2185
        $currentUserKey = DB::queryFirstRow(
2186
            'SELECT share_key, increment_id
2187
            FROM ' . prefixTable('sharekeys_items') . '
2188
            WHERE object_id = %i AND user_id = %i',
2189
            $record['id'],
2190
            $session->get('user-id')
2191
        );
2192
2193
        // do we have any input? (#3481)
2194
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2195
            continue;
2196
        }
2197
2198
        // Decrypt itemkey with admin key
2199
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2200
        
2201
        // Encrypt Item key
2202
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2203
        
2204
        // Save the key in DB
2205
        if ($post_self_change === false) {
2206
            DB::insert(
2207
                prefixTable('sharekeys_items'),
2208
                array(
2209
                    'object_id' => (int) $record['id'],
2210
                    'user_id' => (int) $post_user_id,
2211
                    'share_key' => $share_key_for_item,
2212
                )
2213
            );
2214
        } else {
2215
            // Get itemIncrement from selected user
2216
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2217
                $currentUserKey = DB::queryFirstRow(
2218
                    'SELECT increment_id
2219
                    FROM ' . prefixTable('sharekeys_items') . '
2220
                    WHERE object_id = %i AND user_id = %i',
2221
                    $record['id'],
2222
                    $post_user_id
2223
                );
2224
2225
                if (DB::count() > 0) {
2226
                    // NOw update
2227
                    DB::update(
2228
                        prefixTable('sharekeys_items'),
2229
                        array(
2230
                            'share_key' => $share_key_for_item,
2231
                        ),
2232
                        'increment_id = %i',
2233
                        $currentUserKey['increment_id']
2234
                    );
2235
                } else {
2236
                    DB::insert(
2237
                        prefixTable('sharekeys_items'),
2238
                        array(
2239
                            'object_id' => (int) $record['id'],
2240
                            'user_id' => (int) $post_user_id,
2241
                            'share_key' => $share_key_for_item,
2242
                        )
2243
                    );
2244
                }
2245
            }
2246
        }
2247
    }
2248
2249
    // SHould we change step?
2250
    DB::query(
2251
        'SELECT *
2252
        FROM ' . prefixTable('items') . '
2253
        WHERE perso = 0'
2254
    );
2255
2256
    $next_start = (int) $post_start + (int) $post_length;
2257
    return [
2258
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2259
        'post_action' => $next_start > DB::count() ? 'step20' : 'step10',
2260
    ];
2261
}
2262
2263
function continueReEncryptingUserSharekeysStep20(
2264
    int $post_user_id,
2265
    bool $post_self_change,
2266
    string $post_action,
2267
    int $post_start,
2268
    int $post_length,
2269
    string $user_public_key,
2270
    array $SETTINGS
2271
): array
2272
{
2273
    $session = SessionManager::getSession();
2274
    // Loop on logs
2275
    $rows = DB::query(
2276
        'SELECT increment_id
2277
        FROM ' . prefixTable('log_items') . '
2278
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
2279
        LIMIT ' . $post_start . ', ' . $post_length
2280
    );
2281
    foreach ($rows as $record) {
2282
        // Get itemKey from current user
2283
        $currentUserKey = DB::queryFirstRow(
2284
            'SELECT share_key
2285
            FROM ' . prefixTable('sharekeys_logs') . '
2286
            WHERE object_id = %i AND user_id = %i',
2287
            $record['increment_id'],
2288
            $session->get('user-id')
2289
        );
2290
2291
        // do we have any input? (#3481)
2292
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2293
            continue;
2294
        }
2295
2296
        // Decrypt itemkey with admin key
2297
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2298
2299
        // Encrypt Item key
2300
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2301
2302
        // Save the key in DB
2303
        if ($post_self_change === false) {
2304
            DB::insert(
2305
                prefixTable('sharekeys_logs'),
2306
                array(
2307
                    'object_id' => (int) $record['increment_id'],
2308
                    'user_id' => (int) $post_user_id,
2309
                    'share_key' => $share_key_for_item,
2310
                )
2311
            );
2312
        } else {
2313
            // Get itemIncrement from selected user
2314
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2315
                $currentUserKey = DB::queryFirstRow(
2316
                    'SELECT increment_id
2317
                    FROM ' . prefixTable('sharekeys_items') . '
2318
                    WHERE object_id = %i AND user_id = %i',
2319
                    $record['id'],
2320
                    $post_user_id
2321
                );
2322
            }
2323
2324
            // NOw update
2325
            DB::update(
2326
                prefixTable('sharekeys_logs'),
2327
                array(
2328
                    'share_key' => $share_key_for_item,
2329
                ),
2330
                'increment_id = %i',
2331
                $currentUserKey['increment_id']
2332
            );
2333
        }
2334
    }
2335
2336
    // SHould we change step?
2337
    DB::query(
2338
        'SELECT increment_id
2339
        FROM ' . prefixTable('log_items') . '
2340
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
2341
    );
2342
2343
    $next_start = (int) $post_start + (int) $post_length;
2344
    return [
2345
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2346
        'post_action' => $next_start > DB::count() ? 'step30' : 'step20',
2347
    ];
2348
}
2349
2350
function continueReEncryptingUserSharekeysStep30(
2351
    int $post_user_id,
2352
    bool $post_self_change,
2353
    string $post_action,
2354
    int $post_start,
2355
    int $post_length,
2356
    string $user_public_key,
2357
    array $SETTINGS
2358
): array
2359
{
2360
    $session = SessionManager::getSession();
2361
    // Loop on fields
2362
    $rows = DB::query(
2363
        'SELECT id
2364
        FROM ' . prefixTable('categories_items') . '
2365
        WHERE encryption_type = "teampass_aes"
2366
        LIMIT ' . $post_start . ', ' . $post_length
2367
    );
2368
    foreach ($rows as $record) {
2369
        // Get itemKey from current user
2370
        $currentUserKey = DB::queryFirstRow(
2371
            'SELECT share_key
2372
            FROM ' . prefixTable('sharekeys_fields') . '
2373
            WHERE object_id = %i AND user_id = %i',
2374
            $record['id'],
2375
            $session->get('user-id')
2376
        );
2377
2378
        // do we have any input? (#3481)
2379
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2380
            continue;
2381
        }
2382
2383
        // Decrypt itemkey with admin key
2384
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2385
2386
        // Encrypt Item key
2387
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2388
2389
        // Save the key in DB
2390
        if ($post_self_change === false) {
2391
            DB::insert(
2392
                prefixTable('sharekeys_fields'),
2393
                array(
2394
                    'object_id' => (int) $record['id'],
2395
                    'user_id' => (int) $post_user_id,
2396
                    'share_key' => $share_key_for_item,
2397
                )
2398
            );
2399
        } else {
2400
            // Get itemIncrement from selected user
2401
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2402
                $currentUserKey = DB::queryFirstRow(
2403
                    'SELECT increment_id
2404
                    FROM ' . prefixTable('sharekeys_items') . '
2405
                    WHERE object_id = %i AND user_id = %i',
2406
                    $record['id'],
2407
                    $post_user_id
2408
                );
2409
            }
2410
2411
            // NOw update
2412
            DB::update(
2413
                prefixTable('sharekeys_fields'),
2414
                array(
2415
                    'share_key' => $share_key_for_item,
2416
                ),
2417
                'increment_id = %i',
2418
                $currentUserKey['increment_id']
2419
            );
2420
        }
2421
    }
2422
2423
    // SHould we change step?
2424
    DB::query(
2425
        'SELECT *
2426
        FROM ' . prefixTable('categories_items') . '
2427
        WHERE encryption_type = "teampass_aes"'
2428
    );
2429
2430
    $next_start = (int) $post_start + (int) $post_length;
2431
    return [
2432
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2433
        'post_action' => $next_start > DB::count() ? 'step40' : 'step30',
2434
    ];
2435
}
2436
2437
function continueReEncryptingUserSharekeysStep40(
2438
    int $post_user_id,
2439
    bool $post_self_change,
2440
    string $post_action,
2441
    int $post_start,
2442
    int $post_length,
2443
    string $user_public_key,
2444
    array $SETTINGS
2445
): array
2446
{
2447
    $session = SessionManager::getSession();
2448
    // Loop on suggestions
2449
    $rows = DB::query(
2450
        'SELECT id
2451
        FROM ' . prefixTable('suggestion') . '
2452
        LIMIT ' . $post_start . ', ' . $post_length
2453
    );
2454
    foreach ($rows as $record) {
2455
        // Get itemKey from current user
2456
        $currentUserKey = DB::queryFirstRow(
2457
            'SELECT share_key
2458
            FROM ' . prefixTable('sharekeys_suggestions') . '
2459
            WHERE object_id = %i AND user_id = %i',
2460
            $record['id'],
2461
            $session->get('user-id')
2462
        );
2463
2464
        // do we have any input? (#3481)
2465
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2466
            continue;
2467
        }
2468
2469
        // Decrypt itemkey with admin key
2470
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2471
2472
        // Encrypt Item key
2473
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2474
2475
        // Save the key in DB
2476
        if ($post_self_change === false) {
2477
            DB::insert(
2478
                prefixTable('sharekeys_suggestions'),
2479
                array(
2480
                    'object_id' => (int) $record['id'],
2481
                    'user_id' => (int) $post_user_id,
2482
                    'share_key' => $share_key_for_item,
2483
                )
2484
            );
2485
        } else {
2486
            // Get itemIncrement from selected user
2487
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2488
                $currentUserKey = DB::queryFirstRow(
2489
                    'SELECT increment_id
2490
                    FROM ' . prefixTable('sharekeys_items') . '
2491
                    WHERE object_id = %i AND user_id = %i',
2492
                    $record['id'],
2493
                    $post_user_id
2494
                );
2495
            }
2496
2497
            // NOw update
2498
            DB::update(
2499
                prefixTable('sharekeys_suggestions'),
2500
                array(
2501
                    'share_key' => $share_key_for_item,
2502
                ),
2503
                'increment_id = %i',
2504
                $currentUserKey['increment_id']
2505
            );
2506
        }
2507
    }
2508
2509
    // SHould we change step?
2510
    DB::query(
2511
        'SELECT *
2512
        FROM ' . prefixTable('suggestion')
2513
    );
2514
2515
    $next_start = (int) $post_start + (int) $post_length;
2516
    return [
2517
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2518
        'post_action' => $next_start > DB::count() ? 'step50' : 'step40',
2519
    ];
2520
}
2521
2522
function continueReEncryptingUserSharekeysStep50(
2523
    int $post_user_id,
2524
    bool $post_self_change,
2525
    string $post_action,
2526
    int $post_start,
2527
    int $post_length,
2528
    string $user_public_key,
2529
    array $SETTINGS
2530
): array
2531
{
2532
    $session = SessionManager::getSession();
2533
    // Loop on files
2534
    $rows = DB::query(
2535
        'SELECT id
2536
        FROM ' . prefixTable('files') . '
2537
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2538
        LIMIT ' . $post_start . ', ' . $post_length
2539
    ); //aes_encryption
2540
    foreach ($rows as $record) {
2541
        // Get itemKey from current user
2542
        $currentUserKey = DB::queryFirstRow(
2543
            'SELECT share_key
2544
            FROM ' . prefixTable('sharekeys_files') . '
2545
            WHERE object_id = %i AND user_id = %i',
2546
            $record['id'],
2547
            $session->get('user-id')
2548
        );
2549
2550
        // do we have any input? (#3481)
2551
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2552
            continue;
2553
        }
2554
2555
        // Decrypt itemkey with admin key
2556
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2557
2558
        // Encrypt Item key
2559
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2560
2561
        // Save the key in DB
2562
        if ($post_self_change === false) {
2563
            DB::insert(
2564
                prefixTable('sharekeys_files'),
2565
                array(
2566
                    'object_id' => (int) $record['id'],
2567
                    'user_id' => (int) $post_user_id,
2568
                    'share_key' => $share_key_for_item,
2569
                )
2570
            );
2571
        } else {
2572
            // Get itemIncrement from selected user
2573
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2574
                $currentUserKey = DB::queryFirstRow(
2575
                    'SELECT increment_id
2576
                    FROM ' . prefixTable('sharekeys_items') . '
2577
                    WHERE object_id = %i AND user_id = %i',
2578
                    $record['id'],
2579
                    $post_user_id
2580
                );
2581
            }
2582
2583
            // NOw update
2584
            DB::update(
2585
                prefixTable('sharekeys_files'),
2586
                array(
2587
                    'share_key' => $share_key_for_item,
2588
                ),
2589
                'increment_id = %i',
2590
                $currentUserKey['increment_id']
2591
            );
2592
        }
2593
    }
2594
2595
    // SHould we change step?
2596
    DB::query(
2597
        'SELECT *
2598
        FROM ' . prefixTable('files') . '
2599
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2600
    );
2601
2602
    $next_start = (int) $post_start + (int) $post_length;
2603
    return [
2604
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2605
        'post_action' => $next_start > DB::count() ? 'step60' : 'step50',
2606
    ];
2607
}
2608
2609
function continueReEncryptingUserSharekeysStep60(
2610
    int $post_user_id,
2611
    bool $post_self_change,
2612
    string $post_action,
2613
    int $post_start,
2614
    int $post_length,
2615
    string $user_public_key,
2616
    array $SETTINGS
2617
): array
2618
{
2619
    $session = SessionManager::getSession();
2620
    // IF USER IS NOT THE SAME
2621
    if ((int) $post_user_id === (int) $session->get('user-id')) {
2622
        return [
2623
            'next_start' => 0,
2624
            'post_action' => 'finished',
2625
        ];
2626
    }
2627
    
2628
    // Loop on persoanl items
2629
    if (count($session->get('user-personal_folders')) > 0) {
2630
        $rows = DB::query(
2631
            'SELECT id, pw
2632
            FROM ' . prefixTable('items') . '
2633
            WHERE perso = 1 AND id_tree IN %ls AND encryption_type = %s
2634
            LIMIT ' . $post_start . ', ' . $post_length,
2635
            $session->get('user-personal_folders'),
2636
            "defuse"
2637
        );
2638
        foreach ($rows as $record) {
2639
            // Get itemKey from current user
2640
            $currentUserKey = DB::queryFirstRow(
2641
                'SELECT share_key, increment_id
2642
                FROM ' . prefixTable('sharekeys_items') . '
2643
                WHERE object_id = %i AND user_id = %i',
2644
                $record['id'],
2645
                $session->get('user-id')
2646
            );
2647
2648
            // Decrypt itemkey with admin key
2649
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2650
2651
            // Encrypt Item key
2652
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2653
2654
            // Save the key in DB
2655
            if ($post_self_change === false) {
2656
                DB::insert(
2657
                    prefixTable('sharekeys_items'),
2658
                    array(
2659
                        'object_id' => (int) $record['id'],
2660
                        'user_id' => (int) $post_user_id,
2661
                        'share_key' => $share_key_for_item,
2662
                    )
2663
                );
2664
            } else {
2665
                // Get itemIncrement from selected user
2666
                if ((int) $post_user_id !== (int) $session->get('user-id')) {
2667
                    $currentUserKey = DB::queryFirstRow(
2668
                        'SELECT increment_id
2669
                        FROM ' . prefixTable('sharekeys_items') . '
2670
                        WHERE object_id = %i AND user_id = %i',
2671
                        $record['id'],
2672
                        $post_user_id
2673
                    );
2674
                }
2675
2676
                // NOw update
2677
                DB::update(
2678
                    prefixTable('sharekeys_items'),
2679
                    array(
2680
                        'share_key' => $share_key_for_item,
2681
                    ),
2682
                    'increment_id = %i',
2683
                    $currentUserKey['increment_id']
2684
                );
2685
            }
2686
        }
2687
    }
2688
2689
    // SHould we change step?
2690
    DB::query(
2691
        'SELECT *
2692
        FROM ' . prefixTable('items') . '
2693
        WHERE perso = 0'
2694
    );
2695
2696
    $next_start = (int) $post_start + (int) $post_length;
2697
    return [
2698
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2699
        'post_action' => $next_start > DB::count() ? 'finished' : 'step60',
2700
    ];
2701
}
2702
2703
function migrateTo3_DoUserPersonalItemsEncryption(
2704
    int $post_user_id,
2705
    int $post_start,
2706
    int $post_length,
2707
    int $post_counterItemsToTreat,
2708
    string $post_user_psk,
2709
    array $SETTINGS
2710
) {
2711
    $next_step = 'psk';
2712
    
2713
    $session = SessionManager::getSession();
2714
    $lang = new Language($session->get('user-language') ?? 'english');
2715
    
2716
    if (isUserIdValid($post_user_id) === true) {
2717
        // Check if user exists
2718
        $userInfo = DB::queryFirstRow(
2719
            'SELECT public_key, encrypted_psk
2720
            FROM ' . prefixTable('users') . '
2721
            WHERE id = %i',
2722
            $post_user_id
2723
        );
2724
        if (DB::count() > 0) {
2725
            // check if psk is correct.
2726
            if (empty($userInfo['encrypted_psk']) === false) {//echo $post_user_psk." ;; ".$userInfo['encrypted_psk']." ;; ";
2727
                $user_key_encoded = defuse_validate_personal_key(
2728
                    html_entity_decode($post_user_psk), // convert tspecial string back to their original characters due to FILTER_SANITIZE_FULL_SPECIAL_CHARS
2729
                    $userInfo['encrypted_psk']
2730
                );
2731
2732
                if (strpos($user_key_encoded, "Error ") !== false) {
2733
                    return prepareExchangedData(
2734
                        array(
2735
                            'error' => true,
2736
                            'message' => $lang->get('bad_psk'),
2737
                        ),
2738
                        'encode'
2739
                    );
2740
                }
2741
2742
                // Get number of user's personal items with no AES encryption
2743
                if ($post_counterItemsToTreat === -1) {
2744
                    DB::query(
2745
                        'SELECT id
2746
                        FROM ' . prefixTable('items') . '
2747
                        WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2748
                        $session->get('user-personal_folders'),
2749
                        'teampass_aes'
2750
                    );
2751
                    $countUserPersonalItems = DB::count();
2752
                } else {
2753
                    $countUserPersonalItems = $post_counterItemsToTreat;
2754
                }
2755
2756
                // Loop on persoanl items
2757
                $rows = DB::query(
2758
                    'SELECT id, pw
2759
                    FROM ' . prefixTable('items') . '
2760
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s
2761
                    LIMIT ' . $post_length,
2762
                    $session->get('user-personal_folders'),
2763
                    'teampass_aes'
2764
                );
2765
                foreach ($rows as $record) {
2766
                    // Decrypt with Defuse
2767
                    $passwd = cryption(
2768
                        $record['pw'],
2769
                        $user_key_encoded,
2770
                        'decrypt',
2771
                        $SETTINGS
2772
                    );
2773
2774
                    // Encrypt with Object Key
2775
                    $cryptedStuff = doDataEncryption(html_entity_decode($passwd['string']));
2776
2777
                    // Store new password in DB
2778
                    DB::update(
2779
                        prefixTable('items'),
2780
                        array(
2781
                            'pw' => $cryptedStuff['encrypted'],
2782
                            'encryption_type' => 'teampass_aes',
2783
                        ),
2784
                        'id = %i',
2785
                        $record['id']
2786
                    );
2787
2788
                    // Insert in DB the new object key for this item by user
2789
                    DB::insert(
2790
                        prefixTable('sharekeys_items'),
2791
                        array(
2792
                            'object_id' => (int) $record['id'],
2793
                            'user_id' => (int) $post_user_id,
2794
                            'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2795
                        )
2796
                    );
2797
2798
2799
                    // Does this item has Files?
2800
                    // Loop on files
2801
                    $rows = DB::query(
2802
                        'SELECT id, file
2803
                        FROM ' . prefixTable('files') . '
2804
                        WHERE status != %s
2805
                        AND id_item = %i',
2806
                        TP_ENCRYPTION_NAME,
2807
                        $record['id']
2808
                    );
2809
                    //aes_encryption
2810
                    foreach ($rows as $record2) {
2811
                        // Now decrypt the file
2812
                        prepareFileWithDefuse(
2813
                            'decrypt',
2814
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2815
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2816
                            $post_user_psk
2817
                        );
2818
2819
                        // Encrypt the file
2820
                        $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2821
2822
                        DB::update(
2823
                            prefixTable('files'),
2824
                            array(
2825
                                'file' => $encryptedFile['fileHash'],
2826
                                'status' => TP_ENCRYPTION_NAME,
2827
                            ),
2828
                            'id = %i',
2829
                            $record2['id']
2830
                        );
2831
2832
                        // Save key
2833
                        DB::insert(
2834
                            prefixTable('sharekeys_files'),
2835
                            array(
2836
                                'object_id' => (int) $record2['id'],
2837
                                'user_id' => (int) $session->get('user-id'),
2838
                                'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $session->get('user-public_key')),
2839
                            )
2840
                        );
2841
2842
                        // Unlink original file
2843
                        unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2844
                    }
2845
                }
2846
2847
                // SHould we change step?
2848
                $next_start = (int) $post_start + (int) $post_length;
2849
                DB::query(
2850
                    'SELECT id
2851
                    FROM ' . prefixTable('items') . '
2852
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2853
                    $session->get('user-personal_folders'),
2854
                    'teampass_aes'
2855
                );
2856
                if (DB::count() === 0 || ($next_start - $post_length) >= $countUserPersonalItems) {
2857
                    // Now update user
2858
                    DB::update(
2859
                        prefixTable('users'),
2860
                        array(
2861
                            'special' => 'none',
2862
                            'upgrade_needed' => 0,
2863
                            'encrypted_psk' => '',
2864
                        ),
2865
                        'id = %i',
2866
                        $post_user_id
2867
                    );
2868
2869
                    $next_step = 'finished';
2870
                    $next_start = 0;
2871
                }
2872
2873
                // Continu with next step
2874
                return prepareExchangedData(
2875
                    array(
2876
                        'error' => false,
2877
                        'message' => '',
2878
                        'step' => $next_step,
2879
                        'start' => $next_start,
2880
                        'userId' => $post_user_id
2881
                    ),
2882
                    'encode'
2883
                );
2884
            }
2885
        }
2886
        
2887
        // Nothing to do
2888
        return prepareExchangedData(
2889
            array(
2890
                'error' => true,
2891
                'message' => $lang->get('error_no_user'),
2892
            ),
2893
            'encode'
2894
        );
2895
    }
2896
    
2897
    // Nothing to do
2898
    return prepareExchangedData(
2899
        array(
2900
            'error' => true,
2901
            'message' => $lang->get('error_no_user'),
2902
        ),
2903
        'encode'
2904
    );
2905
}
2906
2907
2908
function getUserInfo(
2909
    int $post_user_id,
2910
    array $SETTINGS
2911
)
2912
{
2913
    // Load user's language
2914
    $session = SessionManager::getSession();
2915
    $lang = new Language($session->get('user-language') ?? 'english');
2916
    
2917
    if (isUserIdValid($post_user_id) === true) {
2918
        // Get user info
2919
        $userData = DB::queryFirstRow(
2920
            'SELECT special, auth_type, is_ready_for_usage, ongoing_process_id, otp_provided, keys_recovery_time
2921
            FROM ' . prefixTable('users') . '
2922
            WHERE id = %i',
2923
            $post_user_id
2924
        );
2925
        if (DB::count() > 0) {
2926
            return prepareExchangedData(
2927
                array(
2928
                    'error' => false,
2929
                    'message' => '',
2930
                    'queryResults' => $userData,
2931
                ),
2932
                'encode'
2933
            );
2934
        }
2935
    }
2936
    return prepareExchangedData(
2937
        array(
2938
            'error' => true,
2939
            'message' => $lang->get('error_no_user'),
2940
        ),
2941
        'encode'
2942
    );
2943
}
2944
2945
/**
2946
 * Change user auth password
2947
 *
2948
 * @param integer $post_user_id
2949
 * @param string $post_current_pwd
2950
 * @param string $post_new_pwd
2951
 * @param array $SETTINGS
2952
 * @return string
2953
 */
2954
function changeUserAuthenticationPassword(
2955
    int $post_user_id,
2956
    string $post_current_pwd,
2957
    string $post_new_pwd,
2958
    array $SETTINGS
2959
)
2960
{
2961
    $session = SessionManager::getSession();
2962
    $lang = new Language($session->get('user-language') ?? 'english');
2963
 
2964
    if (isUserIdValid($post_user_id) === true) {
2965
        // Get user info
2966
        $userData = DB::queryFirstRow(
2967
            'SELECT auth_type, login, private_key
2968
            FROM ' . prefixTable('users') . '
2969
            WHERE id = %i',
2970
            $post_user_id
2971
        );
2972
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
2973
            // Now check if current password is correct
2974
            // For this, just check if it is possible to decrypt the privatekey
2975
            // And compare it to the one in session
2976
            try {
2977
                $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
2978
            } catch (Exception $e) {
2979
                return prepareExchangedData(
2980
                    array(
2981
                        'error' => true,
2982
                        'message' => $lang->get('bad_password'),
2983
                    ),
2984
                    'encode'
2985
                );
2986
            }
2987
2988
            $lang = new Language($session->get('user-language') ?? 'english');
2989
2990
            if ($session->get('user-private_key') === $privateKey) {
2991
                // Encrypt it with new password
2992
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
2993
2994
                // Generate new hash for auth password
2995
                $passwordManager = new PasswordManager();
2996
2997
                // Prepare variables
2998
                $newPw = $passwordManager->hashPassword($post_new_pwd);
2999
3000
                // Update user account
3001
                DB::update(
3002
                    prefixTable('users'),
3003
                    array(
3004
                        'private_key' => $hashedPrivateKey,
3005
                        'pw' => $newPw,
3006
                        'special' => 'none',
3007
                        'last_pw_change' => time(),
3008
                    ),
3009
                    'id = %i',
3010
                    $post_user_id
3011
                );
3012
3013
                $session->set('user-private_key', $privateKey);
3014
3015
                return prepareExchangedData(
3016
                    array(
3017
                        'error' => false,
3018
                        'message' => $lang->get('done'),'',
3019
                    ),
3020
                    'encode'
3021
                );
3022
            }
3023
            
3024
            // ERROR
3025
            return prepareExchangedData(
3026
                array(
3027
                    'error' => true,
3028
                    'message' => $lang->get('bad_password'),
3029
                ),
3030
                'encode'
3031
            );
3032
        }
3033
    }
3034
        
3035
    return prepareExchangedData(
3036
        array(
3037
            'error' => true,
3038
            'message' => $lang->get('error_no_user'),
3039
        ),
3040
        'encode'
3041
    );
3042
}
3043
3044
/**
3045
 * Change user LDAP auth password
3046
 *
3047
 * @param integer $post_user_id
3048
 * @param string $post_previous_pwd
3049
 * @param string $post_current_pwd
3050
 * @return string
3051
 */            
3052
function changeUserLDAPAuthenticationPassword(
3053
    int $post_user_id,
3054
    string $post_previous_pwd,
3055
    string $post_current_pwd
3056
)
3057
{
3058
    $session = SessionManager::getSession();
3059
    // Load user's language
3060
    $lang = new Language($session->get('user-language') ?? 'english');
3061
    
3062
    if (isUserIdValid($post_user_id) === true) {
3063
        // Get user info
3064
        $userData = DB::queryFirstRow(
3065
            'SELECT auth_type, login, private_key, special
3066
            FROM ' . prefixTable('users') . '
3067
            WHERE id = %i',
3068
            $post_user_id
3069
        );
3070
3071
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
3072
            // Now check if current password is correct (only if not ldap)
3073
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
3074
                // As it is a change for an LDAP user
3075
                
3076
                // Now check if current password is correct
3077
                // For this, just check if it is possible to decrypt the privatekey
3078
                // And compare it to the one in session
3079
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3080
3081
                // Encrypt it with new password
3082
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3083
3084
                // Update user account
3085
                DB::update(
3086
                    prefixTable('users'),
3087
                    array(
3088
                        'private_key' => $hashedPrivateKey,
3089
                        'special' => 'none',
3090
                    ),
3091
                    'id = %i',
3092
                    $post_user_id
3093
                );
3094
3095
                $session->set('user-private_key', $privateKey);
3096
3097
                return prepareExchangedData(
3098
                    array(
3099
                        'error' => false,
3100
                        'message' => $lang->get('done'),'',
3101
                    ),
3102
                    'encode'
3103
                );
3104
            }
3105
3106
            // For this, just check if it is possible to decrypt the privatekey
3107
            // And try to decrypt one existing key
3108
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3109
3110
            if (empty($privateKey) === true) {
3111
                return prepareExchangedData(
3112
                    array(
3113
                        'error' => true,
3114
                        'message' => $lang->get('password_is_not_correct'),
3115
                    ),
3116
                    'encode'
3117
                );
3118
            }
3119
            // Get one itemKey from current user
3120
            $currentUserKey = DB::queryFirstRow(
3121
                'SELECT ski.share_key, ski.increment_id, l.id_user
3122
                FROM ' . prefixTable('sharekeys_items') . ' AS ski
3123
                INNER JOIN ' . prefixTable('log_items') . ' AS l ON ski.object_id = l.id_item
3124
                WHERE ski.user_id = %i
3125
                ORDER BY RAND()
3126
                LIMIT 1',
3127
                $post_user_id
3128
            );
3129
3130
            if (is_countable($currentUserKey) && count($currentUserKey) > 0) {
3131
                // Decrypt itemkey with user key
3132
                // use old password to decrypt private_key
3133
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
3134
                
3135
                if (empty(base64_decode($itemKey)) === false) {
3136
                    // GOOD password
3137
                    // Encrypt it with current password
3138
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3139
                    
3140
                    // Update user account
3141
                    DB::update(
3142
                        prefixTable('users'),
3143
                        array(
3144
                            'private_key' => $hashedPrivateKey,
3145
                            'special' => 'none',
3146
                        ),
3147
                        'id = %i',
3148
                        $post_user_id
3149
                    );
3150
                    
3151
                    $lang = new Language($session->get('user-language') ?? 'english');
3152
                    $session->set('user-private_key', $privateKey);
3153
3154
                    return prepareExchangedData(
3155
                        array(
3156
                            'error' => false,
3157
                            'message' => $lang->get('done'),
3158
                        ),
3159
                        'encode'
3160
                    );
3161
                }
3162
            }
3163
            
3164
            // ERROR
3165
            return prepareExchangedData(
3166
                array(
3167
                    'error' => true,
3168
                    'message' => $lang->get('bad_password'),
3169
                ),
3170
                'encode'
3171
            );
3172
        }
3173
    }
3174
3175
    // ERROR
3176
    return prepareExchangedData(
3177
        array(
3178
            'error' => true,
3179
            'message' => $lang->get('error_no_user'),
3180
        ),
3181
        'encode'
3182
    );
3183
}
3184
3185
/**
3186
 * Change user LDAP auth password
3187
 *
3188
 * @param integer $post_user_id
3189
 * @param string $post_current_pwd
3190
 * @param string $post_new_pwd
3191
 * @param array $SETTINGS
3192
 * @return string
3193
 */
3194
function increaseSessionDuration(
3195
    int $duration
3196
): string
3197
{
3198
    $session = SessionManager::getSession();
3199
    // check if session is not already expired.
3200
    if ($session->get('user-session_duration') > time()) {
3201
        // Calculate end of session
3202
        $session->set('user-session_duration', (int) $session->get('user-session_duration') + $duration);
3203
        // Update table
3204
        DB::update(
3205
            prefixTable('users'),
3206
            array(
3207
                'session_end' => $session->get('user-session_duration'),
3208
            ),
3209
            'id = %i',
3210
            $session->get('user-id')
3211
        );
3212
        // Return data
3213
        return '[{"new_value":"' . $session->get('user-session_duration') . '"}]';
3214
    }
3215
    
3216
    return '[{"new_value":"expired"}]';
3217
}
3218
3219
function generateAnOTP(string $label, bool $with_qrcode = false, string $secretKey = ''): string
3220
{
3221
    // generate new secret
3222
    $tfa = new TwoFactorAuth();
3223
    if ($secretKey === '') {
3224
        $secretKey = $tfa->createSecret();
3225
    }
3226
3227
    // generate new QR
3228
    if ($with_qrcode === true) {
3229
        $qrcode = $tfa->getQRCodeImageAsDataUri(
3230
            $label,
3231
            $secretKey
3232
        );
3233
    }
3234
3235
    // ERROR
3236
    return prepareExchangedData(
3237
        array(
3238
            'error' => false,
3239
            'message' => '',
3240
            'secret' => $secretKey,
3241
            'qrcode' => $qrcode ?? '',
3242
        ),
3243
        'encode'
3244
    );
3245
}