userHandler()   D
last analyzed

Complexity

Conditions 28
Paths 31

Size

Total Lines 160
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 28
eloc 89
nc 31
nop 4
dl 0
loc 160
rs 4.1666
c 1
b 0
f 0

How to fix   Long Method    Complexity   

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