changeUserAuthenticationPassword()   B
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 87
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 46
c 2
b 0
f 0
nc 5
nop 4
dl 0
loc 87
rs 8.5559

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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