sendEmailsNotSent()   B
last analyzed

Complexity

Conditions 6
Paths 3

Size

Total Lines 54
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 35
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 54
rs 8.7377

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