Passed
Pull Request — master (#4676)
by Nils
05:33
created

sendEmailsNotSent()   B

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