generateQRCode()   D
last analyzed

Complexity

Conditions 15
Paths 17

Size

Total Lines 192
Code Lines 111

Duplication

Lines 0
Ratio 0 %

Importance

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