Passed
Push — master ( 08a4fb...fb9f5a )
by Nils
24:11 queued 19:15
created

keyHandler()   C

Complexity

Conditions 12
Paths 9

Size

Total Lines 108
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 12
eloc 61
c 5
b 0
f 0
nc 9
nop 3
dl 0
loc 108
rs 6.4242

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      main.queries.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2024 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
43
// Load functions
44
require_once 'main.functions.php';
45
46
loadClasses('DB');
47
$session = SessionManager::getSession();
48
$request = SymfonyRequest::createFromGlobals();
49
$lang = new Language($session->get('user-language') ?? 'english');
50
51
// TODO : ajouter un check sue l'envoi de la key
52
53
// Load config
54
$configManager = new ConfigManager();
55
$SETTINGS = $configManager->getAllSettings();
56
57
// Do checks
58
// Instantiate the class with posted data
59
$checkUserAccess = new PerformChecks(
60
    dataSanitizer(
61
        [
62
            'type' => $request->request->get('type', '') !== '' ? htmlspecialchars($request->request->get('type')) : '',
63
        ],
64
        [
65
            'type' => 'trim|escape',
66
        ],
67
    ),
68
    [
69
        'user_id' => returnIfSet($session->get('user-id'), null),
70
        'user_key' => returnIfSet($session->get('key'), null),
71
    ]
72
);
73
// Handle the case
74
echo $checkUserAccess->caseHandler();
75
if (
76
    ($checkUserAccess->userAccessPage('home') === false ||
77
    $checkUserAccess->checkSession() === false)
78
    && in_array(filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS), ['get_teampass_settings', 'ga_generate_qr']) === false
79
) {
80
    // Not allowed page
81
    $session->set('system-error_code', ERR_NOT_ALLOWED);
82
    include $SETTINGS['cpassman_dir'] . '/error.php';
83
    exit;
84
}
85
86
// Define Timezone
87
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
88
set_time_limit(600);
89
90
// DO CHECKS
91
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
92
if (
93
    isset($post_type) === true
94
    && ($post_type === 'ga_generate_qr'
95
        || $post_type === 'get_teampass_settings')
96
) {
97
    // continue
98
    mainQuery($SETTINGS);
99
} elseif (
100
    $session->has('user-id') && null !== $session->get('user-id')
101
    && $checkUserAccess->userAccessPage('home') === false
102
) {
103
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
104
    include __DIR__.'/../error.php';
105
    exit();
106
} elseif (($session->has('user-id') && null !== $session->get('user-id')
107
        && $session->get('key') !== null)
108
    || (isset($post_type) === true
109
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES))
110
) {
111
    // continue
112
    mainQuery($SETTINGS);
113
} else {
114
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
115
    include __DIR__.'/../error.php';
116
    exit();
117
}
118
119
// Includes
120
include_once __DIR__.'/../sources/main.functions.php';
121
122
/**
123
 * Undocumented function.
124
 */
125
function mainQuery(array $SETTINGS)
126
{
127
    header('Content-type: text/html; charset=utf-8');
128
    header('Cache-Control: no-cache');
129
    error_reporting(E_ERROR);
130
131
    // Load libraries
132
    loadClasses('DB');
133
134
    // User's language loading
135
    $session = SessionManager::getSession();
136
    $lang = new Language($session->get('user-language') ?? 'english');
137
138
    // Prepare post variables
139
    $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
140
    $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
141
    $post_type_category = filter_input(INPUT_POST, 'type_category', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
142
    $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
143
144
    // Check KEY
145
    if (isValueSetNullEmpty($post_key) === true) {
146
        echo prepareExchangedData(
147
            array(
148
                'error' => true,
149
                'message' => $lang->get('key_is_not_correct'),
150
            ),
151
            'encode',
152
            $post_key
153
        );
154
        return false;
155
    }
156
    // decrypt and retreive data in JSON format
157
    $dataReceived = empty($post_data) === false ? prepareExchangedData(
158
        $post_data,
159
        'decode'
160
    ) : '';
161
    
162
    switch ($post_type_category) {
163
        case 'action_password':
164
            echo passwordHandler($post_type, $dataReceived, $SETTINGS);
165
            break;
166
167
        case 'action_user':
168
            echo userHandler($post_type, $dataReceived, $SETTINGS, $post_key);
169
            break;
170
171
        case 'action_mail':
172
            echo mailHandler($post_type, $dataReceived, $SETTINGS);
173
            break;
174
175
        case 'action_key':
176
            // deepcode ignore ServerLeak: All cases handled by keyHandler return an encrypted string that is sent back to the client
177
            echo keyHandler($post_type, $dataReceived, $SETTINGS);
178
            break;
179
180
        case 'action_system':
181
            echo systemHandler($post_type, $dataReceived, $SETTINGS);
182
            break;
183
184
        case 'action_utils':
185
            echo utilsHandler($post_type, $dataReceived, $SETTINGS);
186
            break;
187
    }
188
    
189
    // Manage type of action asked
190
    //switch ($post_type) {
191
        /*
192
         * TODO Check if suggestions are existing
193
         */
194
        /*
195
        case 'is_existings_suggestions':
196
            if ($_SESSION['user_manager'] === '1' || $_SESSION['is_admin'] === '1') {
197
                $count = 0;
198
                DB::query('SELECT * FROM ' . prefixTable('items_change'));
199
                $count += DB::count();
200
                DB::query('SELECT * FROM ' . prefixTable('suggestion'));
201
                $count += DB::count();
202
203
                echo '[ { "error" : "" , "count" : "' . $count . '" , "show_sug_in_menu" : "0"} ]';
204
                break;
205
            }
206
            
207
            if (isset($_SESSION['nb_item_change_proposals']) && $_SESSION['nb_item_change_proposals'] > 0) {
208
                echo '[ { "error" : "" , "count" : "' . $_SESSION['nb_item_change_proposals'] . '" , "show_sug_in_menu" : "1"} ]';
209
                break;
210
            }
211
            
212
            echo '[ { "error" : "" , "count" : "" , "show_sug_in_menu" : "0"} ]';
213
214
            break;
215
        */
216
    //}
217
}
218
219
/**
220
 * Handler for all password tasks
221
 *
222
 * @param string $post_type
223
 * @param array|null|string $dataReceived
224
 * @param array $SETTINGS
225
 * @return string
226
 */
227
function passwordHandler(string $post_type, /*php8 array|null|string*/ $dataReceived, array $SETTINGS): string
228
{
229
    switch ($post_type) {
230
        case 'change_pw'://action_password
231
            return changePassword(
232
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
233
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '',
234
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
235
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
236
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
237
                $SETTINGS
238
            );
239
240
        /*
241
        * Change user's authenticataion password
242
        */
243
        case 'change_user_auth_password'://action_password
244
            return changeUserAuthenticationPassword(
245
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
246
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
247
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
248
                $SETTINGS
249
            );
250
251
        /*
252
        * User's authenticataion password in LDAP has changed
253
        */
254
        case 'change_user_ldap_auth_password'://action_password
255
            return /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
256
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
257
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
258
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
259
                $SETTINGS
260
            );
261
262
        /*
263
        * test_current_user_password_is_correct
264
        */
265
        case 'test_current_user_password_is_correct'://action_password
266
            return isUserPasswordCorrect(
267
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
268
                (string) $dataReceived['password'],
269
                $SETTINGS
270
            );
271
272
        /*
273
        * User's password has to be initialized
274
        */
275
        case 'initialize_user_password'://action_password
276
            return initializeUserPassword(
277
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
278
                (string) filter_var($dataReceived['special'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
279
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
280
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
281
                $SETTINGS
282
            );
283
284
        /*
285
        * Default case
286
        */
287
        default :
288
            return prepareExchangedData(
289
                array(
290
                    'error' => true,
291
                ),
292
                'encode'
293
            );
294
    }
295
}
296
297
/**
298
 * Handler for all user tasks
299
 *
300
 * @param string $post_type
301
 * @param array|null|string $dataReceived
302
 * @param array $SETTINGS
303
 * @param string $post_key
304
 * @return string
305
 */
306
function userHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS, string $post_key): string
307
{
308
    $session = SessionManager::getSession();
309
310
    switch ($post_type) {
311
        /*
312
        * Get info 
313
        */
314
        case 'get_user_info'://action_user
315
            return getUserInfo(
316
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
317
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
318
                $SETTINGS
319
            );
320
321
        /*
322
        * Increase the session time of User
323
        */
324
        case 'increase_session_time'://action_user
325
            return increaseSessionDuration(
326
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
327
            );
328
329
        /*
330
        * Generate a password generic
331
        */
332
        case 'generate_password'://action_user
333
            return generateGenericPassword(
334
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
335
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_VALIDATE_BOOLEAN),
336
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN),
337
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_VALIDATE_BOOLEAN),
338
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_VALIDATE_BOOLEAN),
339
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN),
340
                $SETTINGS
341
            );
342
343
        /*
344
        * Refresh list of last items seen
345
        */
346
        case 'refresh_list_items_seen'://action_user
347
            if ($session->has('user-id') || (int) $session->get('user-id') && null !== $session->get('user-id') || (int) $session->get('user-id') > 0) {
348
                return refreshUserItemsSeenList(
349
                    $SETTINGS
350
                );
351
352
            } else {
353
                return json_encode(
354
                    array(
355
                        'error' => '',
356
                        'existing_suggestions' => 0,
357
                        'html_json' => '',
358
                    ),
359
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
360
                );
361
            }
362
363
        /*
364
        * This will generate the QR Google Authenticator
365
        */
366
        case 'ga_generate_qr'://action_user
367
            return generateQRCode(
368
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
369
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
370
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
371
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
372
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
373
                (string) filter_var($dataReceived['token'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
374
                $SETTINGS,
375
                (string) $post_key
0 ignored issues
show
Unused Code introduced by
The call to generateQRCode() has too many arguments starting with (string)$post_key. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

375
            return /** @scrutinizer ignore-call */ generateQRCode(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
376
            );
377
378
        /*
379
        * This will set the user ready
380
        */
381
        case 'user_is_ready'://action_user
382
            return userIsReady(
383
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
384
                (string) $SETTINGS['cpassman_dir']
385
            );
386
387
        /*
388
        * This post type is used to check if the user session is still valid
389
        */
390
        case 'user_get_session_time'://action_user
391
            return userGetSessionTime(
392
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
393
                (string) $SETTINGS['cpassman_dir'],
394
                (int) $SETTINGS['maximum_session_expiration_time'],
395
            );
396
397
        case 'save_user_location'://action_user
398
            return userSaveIp(
399
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
400
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
401
            );
402
403
        /*
404
        * Default case
405
        */
406
        default :
407
            return prepareExchangedData(
408
                array(
409
                    'error' => true,
410
                ),
411
                'encode'
412
            );
413
    }
414
}
415
416
/**
417
 * Handler for all mail tasks
418
 *
419
 * @param string $post_type
420
 * @param array|null|string $dataReceived
421
 * @param array $SETTINGS
422
 * @return string
423
 */
424
function mailHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
425
{
426
    switch ($post_type) {
427
        /*
428
        * CASE
429
        * Send email
430
        */
431
        case 'mail_me'://action_mail
432
            return sendMailToUser(
433
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
434
                $dataReceived['body'],
435
                (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
436
                (array) filter_var_array(
437
                    $dataReceived['pre_replace'],
438
                    FILTER_SANITIZE_FULL_SPECIAL_CHARS
439
                ),
440
                true
441
            );
442
        
443
        /*
444
        * Send emails not sent
445
        */
446
        case 'send_waiting_emails'://mail
447
            sendEmailsNotSent(
448
                $SETTINGS
449
            );
450
            return prepareExchangedData(
451
                array(
452
                    'error' => false,
453
                    'message' => 'mail_sent',
454
                ),
455
                'encode'
456
            );
457
458
        /*
459
        * Default case
460
        */
461
        default :
462
            return prepareExchangedData(
463
                array(
464
                    'error' => true,
465
                ),
466
                'encode'
467
            );
468
    }
469
}
470
471
/**
472
 * Handler for all key related tasks
473
 *
474
 * @param string $post_type
475
 * @param array|null|string $dataReceived
476
 * @param array $SETTINGS
477
 * @return string
478
 */
479
function keyHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
480
{
481
    $session = SessionManager::getSession();
482
    switch ($post_type) {
483
        /*
484
        * Generate a temporary encryption key for user
485
        */
486
        case 'generate_temporary_encryption_key'://action_key
487
            return generateOneTimeCode(
488
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT)
489
            );
490
        
491
        /*
492
        * user_sharekeys_reencryption_start
493
        */
494
        case 'user_sharekeys_reencryption_start'://action_key
495
            return startReEncryptingUserSharekeys(
496
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
497
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
498
                $SETTINGS
499
            );
500
501
        /*
502
        * user_sharekeys_reencryption_next
503
        */
504
        case 'user_sharekeys_reencryption_next'://action_key
505
            return continueReEncryptingUserSharekeys(
506
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
507
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
508
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
509
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
510
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
511
                $SETTINGS
512
            );
513
514
        /*
515
        * user_psk_reencryption
516
        */
517
        case 'user_psk_reencryption'://action_key
518
            return migrateTo3_DoUserPersonalItemsEncryption(
519
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
520
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
521
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
522
                (int) filter_var($dataReceived['counterItemsToTreat'], FILTER_SANITIZE_NUMBER_INT),
523
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
524
                $SETTINGS
525
            );
526
527
        /*
528
        * User's public/private keys change
529
        */
530
        case 'change_private_key_encryption_password'://action_key
531
            return changePrivateKeyEncryptionPassword(
532
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
533
                (string) $dataReceived['current_code'],
534
                (string) filter_var($dataReceived['new_code'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
535
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
536
                $SETTINGS
537
            );
538
539
        /*
540
        * Launch user keys change on his demand
541
        */
542
        case 'user_new_keys_generation'://action_key
543
544
            // Only admins can reset user password
545
            if ($session->get('user-admin') === 1
546
                && empty($dataReceived['user_id']) === false) {
547
                // Use id requested from user if he is admin.
548
                $userId = $dataReceived['user_id'];
549
            } else {
550
                // Use id from session if user not admin or id not sent.
551
                $userId = $session->get('user-id');
552
            }
553
554
            return handleUserKeys(
555
                (int) filter_var($userId, FILTER_SANITIZE_NUMBER_INT),
556
                (string) filter_var($dataReceived['user_pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
557
                (int) isset($SETTINGS['maximum_number_of_items_to_treat']) === true ? $SETTINGS['maximum_number_of_items_to_treat'] : NUMBER_ITEMS_IN_BATCH,
1 ignored issue
show
introduced by
The condition (int)IssetNode === true is always false.
Loading history...
558
                (string) filter_var($dataReceived['encryption_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
559
                (bool) filter_var($dataReceived['delete_existing_keys'], FILTER_VALIDATE_BOOLEAN),
560
                (bool) filter_var($dataReceived['send_email_to_user'], FILTER_VALIDATE_BOOLEAN),
561
                (bool) filter_var($dataReceived['encrypt_with_user_pwd'], FILTER_VALIDATE_BOOLEAN),
562
                (bool) isset($dataReceived['generate_user_new_password']) === true ? filter_var($dataReceived['generate_user_new_password'], FILTER_VALIDATE_BOOLEAN) : false,
563
                (string) filter_var($dataReceived['email_body'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
564
                (bool) filter_var($dataReceived['user_self_change'], FILTER_VALIDATE_BOOLEAN),
565
                (string) filter_var($dataReceived['recovery_public_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
566
                (string) filter_var($dataReceived['recovery_private_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
567
            );
568
569
        /*
570
        * Launch user recovery download
571
        */
572
        case 'user_recovery_keys_download'://action_key
573
            return handleUserRecoveryKeysDownload(
574
                (int) $session->get('user-id'),
575
                (array) $SETTINGS,
576
            );
577
578
        /*
579
        * Default case
580
        */
581
        default :
582
            return prepareExchangedData(
583
                array(
584
                    'error' => true,
585
                ),
586
                'encode'
587
            );
588
    }
589
}
590
591
/**
592
 * Handler for all system tasks
593
 *
594
 * @param string $post_type
595
 * @param array|null|string $dataReceived
596
 * @param array $SETTINGS
597
 * @return string
598
 */
599
function systemHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
600
{
601
    $session = SessionManager::getSession();
602
    switch ($post_type) {
603
        /*
604
        * How many items for this user
605
        */
606
        case 'get_number_of_items_to_treat'://action_system
607
            return getNumberOfItemsToTreat(
608
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
609
                $SETTINGS
610
            );
611
612
        /*
613
        * refresh_folders_categories
614
        */
615
        case 'refresh_folders_categories'://action_system
616
            handleFoldersCategories(
617
                []
618
            );
619
            return prepareExchangedData(
620
                array(
621
                    'error' => false,
622
                ),
623
                'encode'
624
            );
625
626
        /*
627
        * Sending statistics
628
        */
629
        case 'sending_statistics'://action_system
630
            sendingStatistics(
631
                $SETTINGS
632
            );
633
            return prepareExchangedData(
634
                array(
635
                    'error' => false,
636
                ),
637
                'encode'
638
            );
639
640
        /*
641
            * Generate BUG report
642
            */
643
        case 'generate_bug_report'://action_system
644
            return generateBugReport(
645
                (array) $dataReceived,
646
                $SETTINGS
647
            );
648
649
        /*
650
        * get_teampass_settings
651
        */
652
        case 'get_teampass_settings'://action_system
653
            // Encrypt data to return
654
            return prepareExchangedData(
655
                array_intersect_key(
656
                    $SETTINGS, 
657
                    array(
658
                        'ldap_user_attribute' => '',
659
                        'enable_pf_feature' => '',
660
                        'clipboard_life_duration' => '',
661
                        'enable_favourites' => '',
662
                        'copy_to_clipboard_small_icons' => '',
663
                        'enable_attachment_encryption' => '',
664
                        'google_authentication' => '',
665
                        'agses_authentication_enabled' => '',
666
                        'yubico_authentication' => '',
667
                        'duo' => '',
668
                        'personal_saltkey_security_level' => '',
669
                        'enable_tasks_manager' => '',
670
                        'insert_manual_entry_item_history' => '',
671
                    )
672
                ),
673
                'encode'
674
            );
675
676
        /*
677
            * Generates a TOKEN with CRYPT
678
            */
679
        case 'save_token'://action_system
680
            $token = GenerateCryptKey(
681
                null !== filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) ? (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) : 20,
682
                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,
683
                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,
684
                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,
685
                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,
686
                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,
687
                $SETTINGS
688
            );
689
            
690
            // store in DB
691
            DB::insert(
692
                prefixTable('tokens'),
693
                array(
694
                    'user_id' => (int) $session->get('user-id'),
695
                    'token' => $token,
696
                    'reason' => filter_input(INPUT_POST, 'reason', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
697
                    'creation_timestamp' => time(),
698
                    'end_timestamp' => time() + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT), // in secs
699
                )
700
            );
701
702
            return '[{"token" : "' . $token . '"}]';
703
704
        /*
705
        * Default case
706
        */
707
        default :
708
            return prepareExchangedData(
709
                array(
710
                    'error' => true,
711
                ),
712
                'encode'
713
            );
714
    }
715
}
716
717
718
function utilsHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
719
{
720
    switch ($post_type) {
721
        /*
722
        * generate_an_otp
723
        */
724
        case 'generate_an_otp'://action_utils
725
            return generateAnOTP(
726
                (string) filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
727
                (bool) filter_var($dataReceived['with_qrcode'], FILTER_VALIDATE_BOOLEAN),
728
                (string) filter_var($dataReceived['secret_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
729
            );
730
731
732
        /*
733
        * Default case
734
        */
735
        default :
736
            return prepareExchangedData(
737
                array(
738
                    'error' => true,
739
                ),
740
                'encode'
741
            );
742
    }
743
}
744
745
/**
746
 * Permits to set the user ready
747
 *
748
 * @param integer $userid
749
 * @param string $dir
750
 * @return string
751
 */
752
function userIsReady(int $userid, string $dir): string
753
{
754
    DB::update(
755
        prefixTable('users'),
756
        array(
757
            'is_ready_for_usage' => 1,
758
        ),
759
        'id = %i',
760
        $userid
761
    );
762
763
    // Send back
764
    return prepareExchangedData(
765
        array(
766
            'error' => false,
767
        ),
768
        'encode'
769
    ); 
770
}
771
772
773
/**
774
 * Permits to set the user ready
775
 *
776
 * @param integer $userid
777
 * @return string
778
 */
779
function userGetSessionTime(int $userid, string $dir, int $maximum_session_expiration_time): string
780
{
781
    $session = SessionManager::getSession();
782
    // Send back
783
    return prepareExchangedData(
784
        array(
785
            'error' => false,
786
            'timestamp' => $session->get('user-session_duration'),
787
            'max_time_to_add' => intdiv((($maximum_session_expiration_time*60) - ((int) $session->get('user-session_duration') - time())), 60),
788
            'max_session_duration' => $maximum_session_expiration_time,
789
        ),
790
        'encode'
791
    ); 
792
}
793
794
/**
795
 * Save the user's IP
796
 *
797
 * @param integer $userID
798
 * @param string $action
799
 * @return string
800
 */
801
function userSaveIp(int $userID, string $action): string
802
{
803
    if ($action === 'perform') {
804
        DB::update(
805
            prefixTable('users'),
806
            array(
807
                'user_ip' => getClientIpServer(),
808
                'user_ip_lastdate' => time(),
809
            ),
810
            'id = %i',
811
            $userID
812
        );
813
    }
814
815
    return prepareExchangedData(
816
        array(
817
            'error' => false,
818
        ),
819
        'encode'
820
    );
821
}
822
823
/**
824
 * Provides the number of items
825
 *
826
 * @param int   $userId     User ID
827
 * @param array $SETTINGS   TeampassSettings
828
 *
829
 * @return string
830
 */
831
function getNumberOfItemsToTreat(
832
    int $userId,
833
    array $SETTINGS
834
): string
835
{
836
    // get number of items
837
    DB::queryFirstRow(
838
        'SELECT increment_id
839
        FROM ' . prefixTable('sharekeys_items') .
840
        ' WHERE user_id = %i',
841
        $userId
842
    );
843
844
    // Send back
845
    return prepareExchangedData(
846
        array(
847
            'error' => false,
848
            'nbItems' => DB::count(),
849
        ),
850
        'encode'
851
    );
852
}
853
854
855
/**
856
 * 
857
 */
858
function changePassword(
859
    string $post_new_password,
860
    string $post_current_password,
861
    int $post_password_complexity,
862
    string $post_change_request,
863
    int $post_user_id,
864
    array $SETTINGS
865
): string
866
{
867
    $session = SessionManager::getSession();
868
    
869
    // Create password hash
870
    $passwordManager = new PasswordManager();
871
    $post_new_password_hashed = $passwordManager->hashPassword($post_new_password);
872
873
    // Load user's language
874
    $lang = new Language($session->get('user-language') ?? 'english');
875
876
    // User has decided to change is PW
877
    if ($post_change_request === 'reset_user_password_expected'
878
        || $post_change_request === 'user_decides_to_change_password'
879
    ) {
880
        // Check that current user is correct
881
        if ((int) $post_user_id !== (int) $session->get('user-id')) {
882
            return prepareExchangedData(
883
                array(
884
                    'error' => true,
885
                    'message' => $lang->get('error_not_allowed_to'),
886
                ),
887
                'encode'
888
            );
889
        }
890
891
        // check if expected security level is reached
892
        $dataUser = DB::queryfirstrow(
893
            'SELECT *
894
            FROM ' . prefixTable('users') . ' WHERE id = %i',
895
            $post_user_id
896
        );
897
898
        // check if badly written
899
        $dataUser['fonction_id'] = array_filter(
900
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
901
        );
902
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
903
        DB::update(
904
            prefixTable('users'),
905
            array(
906
                'fonction_id' => $dataUser['fonction_id'],
907
            ),
908
            'id = %i',
909
            $post_user_id
910
        );
911
912
        if (empty($dataUser['fonction_id']) === false) {
913
            $data = DB::queryFirstRow(
914
                'SELECT complexity
915
                FROM ' . prefixTable('roles_title') . '
916
                WHERE id IN (' . $dataUser['fonction_id'] . ')
917
                ORDER BY complexity DESC'
918
            );
919
        } else {
920
            // In case user has no roles yet
921
            $data = array();
922
            $data['complexity'] = 0;
923
        }
924
925
        if ((int) $post_password_complexity < (int) $data['complexity']) {
926
            return prepareExchangedData(
927
                array(
928
                    'error' => true,
929
                    'message' => '<div style="margin:10px 0 10px 15px;">' . $lang->get('complexity_level_not_reached') . '.<br>' .
930
                        $lang->get('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
931
                ),
932
                'encode'
933
            );
934
        }
935
936
        // Check that the 2 passwords are differents
937
        if ($post_current_password === $post_new_password) {
938
            return prepareExchangedData(
939
                array(
940
                    'error' => true,
941
                    'message' => $lang->get('password_already_used'),
942
                ),
943
                'encode'
944
            );
945
        }
946
947
        // update sessions
948
        $session->set('user-last_pw_change', mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')));
949
        $session->set('user-validite_pw', 1);
950
951
        // BEfore updating, check that the pwd is correct
952
        if ($passwordManager->verifyPassword($post_new_password_hashed, $post_new_password) === true && empty($dataUser['private_key']) === false) {
953
            $special_action = 'none';
954
            if ($post_change_request === 'reset_user_password_expected') {
955
                $session->set('user-private_key', decryptPrivateKey($post_current_password, $dataUser['private_key']));
956
            }
957
958
            // update DB
959
            DB::update(
960
                prefixTable('users'),
961
                array(
962
                    'pw' => $post_new_password_hashed,
963
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
964
                    'last_pw' => $post_current_password,
965
                    'special' => $special_action,
966
                    'private_key' => encryptPrivateKey($post_new_password, $session->get('user-private_key')),
967
                ),
968
                'id = %i',
969
                $post_user_id
970
            );
971
            // update LOG
972
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $session->get('user-id'), $session->get('user-login'), $post_user_id);
973
974
            // Send back
975
            return prepareExchangedData(
976
                array(
977
                    'error' => false,
978
                    'message' => '',
979
                ),
980
                'encode'
981
            );
982
        }
983
        // Send back
984
        return prepareExchangedData(
985
            array(
986
                'error' => true,
987
                'message' => $lang->get('pw_hash_not_correct'),
988
            ),
989
            'encode'
990
        );
991
    }
992
    return prepareExchangedData(
993
        array(
994
            'error' => true,
995
            'message' => $lang->get('error_not_allowed_to'),
996
        ),
997
        'encode'
998
    );
999
}
1000
1001
function generateQRCode(
1002
    $post_id,
1003
    $post_demand_origin,
1004
    $post_send_mail,
1005
    $post_login,
1006
    $post_pwd,
1007
    $post_token,
1008
    array $SETTINGS
1009
): string
1010
{
1011
    // Load user's language
1012
    $session = SessionManager::getSession();
1013
    $lang = new Language($session->get('user-language') ?? 'english');
1014
1015
    // is this allowed by setting
1016
    if (isKeyExistingAndEqual('ga_reset_by_user', 0, $SETTINGS) === true
1017
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
1018
    ) {
1019
        // User cannot ask for a new code
1020
        return prepareExchangedData(
1021
            array(
1022
                'error' => true,
1023
                'message' => "113 ".$lang->get('error_not_allowed_to')." - ".isKeyExistingAndEqual('ga_reset_by_user', 1, $SETTINGS),
1024
            ),
1025
            'encode'
1026
        );
1027
    }
1028
    
1029
    // Check if user exists
1030
    if (isValueSetNullEmpty($post_id) === true) {
1031
        // Get data about user
1032
        $dataUser = DB::queryfirstrow(
1033
            'SELECT id, email, pw
1034
            FROM ' . prefixTable('users') . '
1035
            WHERE login = %s',
1036
            $post_login
1037
        );
1038
    } else {
1039
        $dataUser = DB::queryfirstrow(
1040
            'SELECT id, login, email, pw
1041
            FROM ' . prefixTable('users') . '
1042
            WHERE id = %i',
1043
            $post_id
1044
        );
1045
        $post_login = $dataUser['login'];
1046
    }
1047
    // Get number of returned users
1048
    $counter = DB::count();
1049
1050
    // Do treatment
1051
    if ($counter === 0) {
1052
        // Not a registered user !
1053
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
1054
        return prepareExchangedData(
1055
            array(
1056
                'error' => true,
1057
                'message' => $lang->get('no_user'),
1058
                'tst' => 1,
1059
            ),
1060
            'encode'
1061
        );
1062
    }
1063
1064
    $passwordManager = new PasswordManager();
1065
    if (
1066
        isSetArrayOfValues([$post_pwd, $dataUser['pw']]) === true
1067
        && $passwordManager->verifyPassword($dataUser['pw'], $post_pwd) === false
1068
        && $post_demand_origin !== 'users_management_list'
1069
    ) {
1070
        // checked the given password
1071
        logEvents($SETTINGS, 'failed_auth', 'password_is_not_correct', '', stripslashes($post_login), stripslashes($post_login));
1072
        return prepareExchangedData(
1073
            array(
1074
                'error' => true,
1075
                'message' => $lang->get('no_user'),
1076
                'tst' => $post_demand_origin,
1077
            ),
1078
            'encode'
1079
        );
1080
    }
1081
    
1082
    if (empty($dataUser['email']) === true) {
1083
        return prepareExchangedData(
1084
            array(
1085
                'error' => true,
1086
                'message' => $lang->get('no_email_set'),
1087
            ),
1088
            'encode'
1089
        );
1090
    }
1091
1092
    // Check if token already used
1093
    $dataToken = DB::queryfirstrow(
1094
        'SELECT end_timestamp, reason
1095
        FROM ' . prefixTable('tokens') . '
1096
        WHERE token = %s AND user_id = %i',
1097
        $post_token,
1098
        $dataUser['id']
1099
    );
1100
    $tokenId = '';
1101
    if (DB::count() > 0 && is_null($dataToken['end_timestamp']) === false && $dataToken['reason'] === 'auth_qr_code') {
1102
        // This token has already been used
1103
        return prepareExchangedData(
1104
            array(
1105
                'error' => true,
1106
                'message' => 'TOKEN already used',//$lang->get('no_email_set'),
1107
            ),
1108
            'encode'
1109
        );
1110
    } elseif(DB::count() === 0) {
1111
        // Store token for this action
1112
        DB::insert(
1113
            prefixTable('tokens'),
1114
            array(
1115
                'user_id' => (int) $dataUser['id'],
1116
                'token' => $post_token,
1117
                'reason' => 'auth_qr_code',
1118
                'creation_timestamp' => time(),
1119
            )
1120
        );
1121
        $tokenId = DB::insertId();
1122
    }
1123
    
1124
    // generate new GA user code
1125
    $tfa = new TwoFactorAuth($SETTINGS['ga_website_name']);
1126
    $gaSecretKey = $tfa->createSecret();
1127
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true, $SETTINGS);
1128
1129
    DB::update(
1130
        prefixTable('users'),
1131
        [
1132
            'ga' => $gaSecretKey,
1133
            'ga_temporary_code' => $gaTemporaryCode,
1134
        ],
1135
        'id = %i',
1136
        $dataUser['id']
1137
    );
1138
1139
    // Log event
1140
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $dataUser['id'], stripslashes($post_login), stripslashes($post_login));
1141
1142
    // Update token status
1143
    DB::update(
1144
        prefixTable('tokens'),
1145
        [
1146
            'end_timestamp' => time(),
1147
        ],
1148
        'id = %i',
1149
        $tokenId
1150
    );
1151
1152
    // send mail?
1153
    if ((int) $post_send_mail === 1) {
1154
        prepareSendingEmail(
1155
            $lang->get('email_ga_subject'),
1156
            str_replace(
1157
                '#2FACode#',
1158
                $gaTemporaryCode,
1159
                $lang->get('email_ga_text')
1160
            ),
1161
            $dataUser['email']
1162
        );
1163
1164
        // send back
1165
        return prepareExchangedData(
1166
            array(
1167
                'error' => false,
1168
                'message' => $post_send_mail,
1169
                'email' => $dataUser['email'],
1170
                'email_result' => str_replace(
1171
                    '#email#',
1172
                    '<b>' . obfuscateEmail($dataUser['email']) . '</b>',
1173
                    addslashes($lang->get('admin_email_result_ok'))
1174
                ),
1175
            ),
1176
            'encode'
1177
        );
1178
    }
1179
    
1180
    // send back
1181
    return prepareExchangedData(
1182
        array(
1183
            'error' => false,
1184
            'message' => '',
1185
            'email' => $dataUser['email'],
1186
            'email_result' => str_replace(
1187
                '#email#',
1188
                '<b>' . obfuscateEmail($dataUser['email']) . '</b>',
1189
                addslashes($lang->get('admin_email_result_ok'))
1190
            ),
1191
        ),
1192
        'encode'
1193
    );
1194
}
1195
1196
function sendEmailsNotSent(
1197
    array $SETTINGS
1198
)
1199
{
1200
    if (isKeyExistingAndEqual('enable_send_email_on_user_login', 1, $SETTINGS) === true) {
1201
        $row = DB::queryFirstRow(
1202
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
1203
            'cron',
1204
            'sending_emails'
1205
        );
1206
1207
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
1208
            $rows = DB::query(
1209
                'SELECT *
1210
                FROM ' . prefixTable('emails') .
1211
                ' WHERE status != %s',
1212
                'sent'
1213
            );
1214
            foreach ($rows as $record) {
1215
                // Send email
1216
                $ret = json_decode(
1217
                    sendEmail(
1218
                        $record['subject'],
1219
                        $record['body'],
1220
                        $record['receivers'],
1221
                        $SETTINGS
1222
                    ),
1223
                    true
1224
                );
1225
1226
                // update item_id in files table
1227
                DB::update(
1228
                    prefixTable('emails'),
1229
                    array(
1230
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
1231
                    ),
1232
                    'timestamp = %s',
1233
                    $record['timestamp']
1234
                );
1235
            }
1236
        }
1237
        // update cron time
1238
        DB::update(
1239
            prefixTable('misc'),
1240
            array(
1241
                'valeur' => time(),
1242
                'updated_at' => time(),
1243
            ),
1244
            'intitule = %s AND type = %s',
1245
            'sending_emails',
1246
            'cron'
1247
        );
1248
    }
1249
}
1250
1251
function generateGenericPassword(
1252
    int $size,
1253
    bool $secure,
1254
    bool $lowercase,
1255
    bool $capitalize,
1256
    bool $numerals,
1257
    bool $symbols,
1258
    array $SETTINGS
1259
): string
1260
{
1261
    if ((int) $size > (int) $SETTINGS['pwd_maximum_length']) {
1262
        return prepareExchangedData(
1263
            array(
1264
                'error_msg' => 'Password length is too long! ',
1265
                'error' => 'true',
1266
            ),
1267
            'encode'
1268
        );
1269
    }
1270
    // Load libraries
1271
    //require_once __DIR__.'/../vendor/autoload.php';
1272
    $generator = new ComputerPasswordGenerator();
1273
    $generator->setRandomGenerator(new Php7RandomGenerator());
1274
1275
    // Manage size
1276
    $generator->setLength(($size <= 0) ? 10 : $size);
1277
1278
    if ($secure === true) {
1279
        $generator->setSymbols(true);
1280
        $generator->setLowercase(true);
1281
        $generator->setUppercase(true);
1282
        $generator->setNumbers(true);
1283
    } else {
1284
        $generator->setLowercase($lowercase);
1285
        $generator->setUppercase($capitalize);
1286
        $generator->setNumbers($numerals);
1287
        $generator->setSymbols($symbols);
1288
    }
1289
1290
    return prepareExchangedData(
1291
        array(
1292
            'key' => $generator->generatePasswords(),
1293
            'error' => '',
1294
        ),
1295
        'encode'
1296
    );
1297
}
1298
1299
function refreshUserItemsSeenList(
1300
    array $SETTINGS
1301
): string
1302
{
1303
    $session = SessionManager::getSession();
1304
1305
    // get list of last items seen
1306
    $arr_html = array();
1307
    $rows = DB::query(
1308
        '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
1309
        FROM ' . prefixTable('log_items') . ' AS l
1310
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
1311
        WHERE l.action = %s AND l.id_user = %i
1312
        ORDER BY l.date DESC
1313
        LIMIT 0, 100',
1314
        'at_shown',
1315
        $session->get('user-id')
1316
    );
1317
    if (DB::count() > 0) {
1318
        foreach ($rows as $record) {
1319
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
1320
                array_push(
1321
                    $arr_html,
1322
                    array(
1323
                        'id' => $record['id'],
1324
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
1325
                        'tree_id' => $record['id_tree'],
1326
                        'perso' => $record['perso'],
1327
                        'restricted' => $record['restricted'],
1328
                    )
1329
                );
1330
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
1331
                    break;
1332
                }
1333
            }
1334
        }
1335
    }
1336
1337
    // get wainting suggestions
1338
    $nb_suggestions_waiting = 0;
1339
    if (isKeyExistingAndEqual('enable_suggestion', 1, $SETTINGS) === true
1340
        && ((int) $session->get('user-admin') === 1 || (int) $session->get('user-manager') === 1)
1341
    ) {
1342
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1343
        $nb_suggestions_waiting = DB::count();
1344
    }
1345
1346
    return json_encode(
1347
        array(
1348
            'error' => '',
1349
            'existing_suggestions' => $nb_suggestions_waiting,
1350
            'html_json' => $arr_html,
1351
        ),
1352
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1353
    );
1354
}
1355
1356
function sendingStatistics(
1357
    array $SETTINGS
1358
): void
1359
{
1360
    $session = SessionManager::getSession();
1361
    if (
1362
        isSetArrayOfValues([$SETTINGS['send_statistics_items'], $SETTINGS['send_stats_time']]) === true
1363
        && isKeyExistingAndEqual('send_stats', 1, $SETTINGS) === true
1364
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1365
    ) {
1366
        // get statistics data
1367
        $stats_data = getStatisticsData($SETTINGS);
1368
1369
        // get statistics items to share
1370
        $statsToSend = [];
1371
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1372
        $statsToSend['timestamp'] = time();
1373
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1374
            if ($data === 'stat_languages') {
1375
                $tmp = '';
1376
                foreach ($stats_data[$data] as $key => $value) {
1377
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1378
                }
1379
                $statsToSend[$data] = $tmp;
1380
            } elseif ($data === 'stat_country') {
1381
                $tmp = '';
1382
                foreach ($stats_data[$data] as $key => $value) {
1383
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1384
                }
1385
                $statsToSend[$data] = $tmp;
1386
            } else {
1387
                $statsToSend[$data] = $stats_data[$data];
1388
            }
1389
        }
1390
1391
        // connect to Teampass Statistics database
1392
        $link2 = new MeekroDB(
1393
            'teampass.pw',
1394
            'teampass_user',
1395
            'ZMlEfRzKzFLZNzie',
1396
            'teampass_followup',
1397
            '3306',
1398
            'utf8'
1399
        );
1400
1401
        $link2->insert(
1402
            'statistics',
1403
            $statsToSend
1404
        );
1405
1406
        // update table misc with current timestamp
1407
        DB::update(
1408
            prefixTable('misc'),
1409
            array(
1410
                'valeur' => time(),
1411
                'updated_at' => time(),
1412
            ),
1413
            'type = %s AND intitule = %s',
1414
            'admin',
1415
            'send_stats_time'
1416
        );
1417
1418
        //permits to test only once by session
1419
        $session->set('system-send_stats_done', 1);
1420
        $SETTINGS['send_stats_time'] = time();
1421
1422
        // save change in config file
1423
        handleConfigFile('update', $SETTINGS, 'send_stats_time', $SETTINGS['send_stats_time']);
1424
    }
1425
}
1426
1427
function generateBugReport(
1428
    array $data,
1429
    array $SETTINGS
1430
): string
1431
{
1432
    $config_exclude_vars = array(
1433
        'bck_script_passkey',
1434
        'email_smtp_server',
1435
        'email_auth_username',
1436
        'email_auth_pwd',
1437
        'email_from',
1438
        'onthefly-restore-key',
1439
        'onthefly-backup-key',
1440
        'ldap_password',
1441
        'ldap_hosts',
1442
        'proxy_ip',
1443
        'ldap_bind_passwd',
1444
        'syslog_host',
1445
        'duo_akey',
1446
        'duo_ikey',
1447
        'duo_skey',
1448
        'duo_host'
1449
    );
1450
1451
    // Load user's language
1452
    $session = SessionManager::getSession();
1453
    $lang = new Language($session->get('user-language') ?? 'english');
1454
    
1455
    // Only administrators can see this confidential informations.
1456
    if ($session->get('user-admin') !== 1) {
1457
        http_response_code(403);
1458
        exit;
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1459
    }
1460
1461
    // Read config file
1462
    $list_of_options = '';
1463
    $url_found = '';
1464
    $anonym_url = '';
1465
    $tp_config_file = '../includes/config/tp.config.php';
1466
    $data = file($tp_config_file);
1467
    foreach ($data as $line) {
1468
        if (substr($line, 0, 4) === '    ') {
1469
            // Remove extra spaces
1470
            $line = str_replace('    ', '', $line);
1471
1472
            // Identify url to anonymize it
1473
            if (strpos($line, 'cpassman_url') > 0 && empty($url_found) === true) {
1474
                $url_found = substr($line, 19, strlen($line) - 22);
1475
                if (empty($url_found) === false) {
1476
                    $tmp = parse_url($url_found);
1477
                    $anonym_url = $tmp['scheme'] . '://<anonym_url>' . (isset($tmp['path']) === true ? $tmp['path'] : '');
1478
                    $line = "'cpassman_url' => '" . $anonym_url . "\n";
1479
                } else {
1480
                    $line = "'cpassman_url' => \n";
1481
                }
1482
            }
1483
1484
            // Anonymize all urls
1485
            if (empty($anonym_url) === false) {
1486
                $line = str_replace($url_found, $anonym_url, $line);
1487
            }
1488
1489
            // Clear some vars
1490
            foreach ($config_exclude_vars as $var) {
1491
                if (strpos($line, $var) > 0) {
1492
                    $line = "'".$var."' => '<removed>'\n";
1493
                }
1494
            }
1495
1496
            // Complete line to display
1497
            $list_of_options .= $line;
1498
        }
1499
    }
1500
1501
    // Get error
1502
    $err = error_get_last();
1503
1504
    // Get 10 latest errors in Teampass
1505
    $teampass_errors = '';
1506
    $rows = DB::query(
1507
        'SELECT label, date AS error_date
1508
        FROM ' . prefixTable('log_system') . "
1509
        WHERE `type` LIKE 'error'
1510
        ORDER BY `date` DESC
1511
        LIMIT 0, 10"
1512
    );
1513
    if (DB::count() > 0) {
1514
        foreach ($rows as $record) {
1515
            if (empty($teampass_errors) === true) {
1516
                $teampass_errors = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1517
            } else {
1518
                $teampass_errors .= ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1519
            }
1520
        }
1521
    }
1522
1523
    $link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1524
1525
    // Now prepare text
1526
    $txt = '### Page on which it happened
1527
' . $data['current_page'] . '
1528
1529
### Steps to reproduce
1530
1.
1531
2.
1532
3.
1533
1534
### Expected behaviour
1535
Tell us what should happen
1536
1537
1538
### Actual behaviour
1539
Tell us what happens instead
1540
1541
### Server configuration
1542
**Operating system**: ' . php_uname() . '
1543
1544
**Web server:** ' . $_SERVER['SERVER_SOFTWARE'] . '
1545
1546
**Database:** ' . ($link === false ? $lang->get('undefined') : mysqli_get_server_info($link)) . '
1547
1548
**PHP version:** ' . PHP_VERSION . '
1549
1550
**Teampass version:** ' . TP_VERSION . '
1551
1552
**Teampass configuration file:**
1553
```
1554
' . $list_of_options . '
1555
```
1556
1557
**Updated from an older Teampass or fresh install:**
1558
1559
### Client configuration
1560
1561
**Browser:** ' . $data['browser_name'] . ' - ' . $data['browser_version'] . '
1562
1563
**Operating system:** ' . $data['os'] . ' - ' . $data['os_archi'] . 'bits
1564
1565
### Logs
1566
1567
#### Web server error log
1568
```
1569
' . $err['message'] . ' - ' . $err['file'] . ' (' . $err['line'] . ')
1570
```
1571
1572
#### Teampass 10 last system errors
1573
```
1574
' . $teampass_errors . '
1575
```
1576
1577
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1578
```
1579
Insert the log here and especially the answer of the query that failed.
1580
```
1581
';
1582
1583
    return prepareExchangedData(
1584
        array(
1585
            'html' => $txt,
1586
            'error' => '',
1587
        ),
1588
        'encode'
1589
    );
1590
}
1591
1592
/**
1593
 * Check that the user password is valid
1594
 *
1595
 * @param integer $post_user_id
1596
 * @param string $post_user_password
1597
 * @param array $SETTINGS
1598
 * @return string
1599
 */
1600
function isUserPasswordCorrect(
1601
    int $post_user_id,
1602
    string $post_user_password,
1603
    array $SETTINGS
1604
): string
1605
{
1606
    $session = SessionManager::getSession();
1607
    // Load user's language
1608
    $lang = new Language($session->get('user-language') ?? 'english');
1609
    
1610
    if (isUserIdValid($post_user_id) === true) {
1611
        // Check if user exists
1612
        $userInfo = DB::queryFirstRow(
1613
            'SELECT public_key, private_key, pw, auth_type
1614
            FROM ' . prefixTable('users') . '
1615
            WHERE id = %i',
1616
            $post_user_id
1617
        );
1618
        if (DB::count() > 0 && empty($userInfo['private_key']) === false) {
1619
            // Get itemKey from current user
1620
            // Get one item
1621
            $currentUserKey = DB::queryFirstRow(
1622
                'SELECT object_id, share_key, increment_id
1623
                FROM ' . prefixTable('sharekeys_items') . ' AS si
1624
                INNER JOIN ' . prefixTable('items') . ' AS i ON  (i.id = si.object_id)
1625
                INNER JOIN ' . prefixTable('nested_tree') . ' AS nt ON  (i.id_tree = nt.id)
1626
                WHERE user_id = %i AND nt.personal_folder = %i',
1627
                $post_user_id,
1628
                0
1629
            );
1630
            
1631
            if (DB::count() === 0) {
1632
                // This user has no items
1633
                // let's consider no items in DB
1634
                return prepareExchangedData(
1635
                    array(
1636
                        'error' => false,
1637
                        'message' => '',
1638
                        'debug' => '',
1639
                    ),
1640
                    'encode'
1641
                );
1642
            }
1643
1644
            if ($currentUserKey !== null) {
1645
                // Decrypt itemkey with user key
1646
                // use old password to decrypt private_key
1647
                $session->set('user-private_key', decryptPrivateKey($post_user_password, $userInfo['private_key']));
1648
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
1649
1650
                //echo $post_user_password."  --  ".$userInfo['private_key']. ";;";
1651
1652
                if (empty(base64_decode($itemKey)) === false) {
1653
                    // GOOD password
1654
                    return prepareExchangedData(
1655
                        array(
1656
                            'error' => false,
1657
                            'message' => '',
1658
                            'debug' => '',
1659
                        ),
1660
                        'encode'
1661
                    );
1662
                }
1663
            }
1664
1665
            // use the password check
1666
            $passwordManager = new PasswordManager();
1667
            
1668
            if ($passwordManager->verifyPassword($userInfo['pw'], htmlspecialchars_decode($post_user_password)) === true) {
1669
                // GOOD password
1670
                return prepareExchangedData(
1671
                    array(
1672
                        'error' => false,
1673
                        'message' => '',
1674
                        'debug' => '',
1675
                    ),
1676
                    'encode'
1677
                );
1678
            }
1679
        }
1680
    }
1681
1682
    return prepareExchangedData(
1683
        array(
1684
            'error' => true,
1685
            'message' => $lang->get('password_is_not_correct'),
1686
        ),
1687
        'encode'
1688
    );
1689
}
1690
1691
function changePrivateKeyEncryptionPassword(
1692
    int $post_user_id,
1693
    string $post_current_code,
1694
    string $post_new_code,
1695
    string $post_action_type,
1696
    array $SETTINGS
1697
): string
1698
{
1699
    $session = SessionManager::getSession();
1700
    // Load user's language
1701
    $lang = new Language($session->get('user-language') ?? 'english');
1702
    
1703
    if (empty($post_new_code) === true) {
1704
        if (empty($session->get('user-password')) === false) {
1705
            $post_new_code = $session->get('user-password');
1706
        } else {
1707
            // no user password???
1708
            return prepareExchangedData(
1709
                array(
1710
                    'error' => true,
1711
                    'message' => $lang->get('error_no_user_password_exists'),
1712
                    'debug' => '',
1713
                ),
1714
                'encode'
1715
            );
1716
        }
1717
    }
1718
1719
    if (isUserIdValid($post_user_id) === true) {
1720
        // Get user info
1721
        $userData = DB::queryFirstRow(
1722
            'SELECT private_key
1723
            FROM ' . prefixTable('users') . '
1724
            WHERE id = %i',
1725
            $post_user_id
1726
        );
1727
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
1728
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1729
                // Here the user has his private key encrypted with an OTC.
1730
                // We need to encrypt it with his real password
1731
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1732
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1733
            } else {
1734
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1735
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1736
            }
1737
1738
            // Should fail here to avoid break user private key.
1739
            if (strlen($privateKey) === 0 || strlen($hashedPrivateKey) < 30) {
1740
                error_log("Error reencrypt user private key. User ID: {$post_user_id}, Given OTP: '{$post_current_code}'");
1741
                return prepareExchangedData(
1742
                    array(
1743
                        'error' => true,
1744
                        'message' => $lang->get('error_otp_secret'),
1745
                        'debug' => '',
1746
                    ),
1747
                    'encode'
1748
                );
1749
            }
1750
1751
            // Update user account
1752
            DB::update(
1753
                prefixTable('users'),
1754
                array(
1755
                    'private_key' => $hashedPrivateKey,
1756
                    'special' => 'none',
1757
                    'otp_provided' => 1,
1758
                ),
1759
                'id = %i',
1760
                $post_user_id
1761
            );
1762
1763
            $session->set('user-private_key', $privateKey);
1764
        }
1765
1766
        // Return
1767
        return prepareExchangedData(
1768
            array(
1769
                'error' => false,
1770
                'message' => '',
1771
            ),
1772
            'encode'
1773
        );
1774
    }
1775
    
1776
    return prepareExchangedData(
1777
        array(
1778
            'error' => true,
1779
            'message' => $lang->get('error_no_user'),
1780
            'debug' => '',
1781
        ),
1782
        'encode'
1783
    );
1784
}
1785
1786
function initializeUserPassword(
1787
    int $post_user_id,
1788
    string $post_special,
1789
    string $post_user_password,
1790
    bool $post_self_change,
1791
    array $SETTINGS
1792
): string
1793
{
1794
    // Load user's language
1795
    $session = SessionManager::getSession();
1796
    $lang = new Language($session->get('user-language') ?? 'english');
1797
    
1798
    if (isUserIdValid($post_user_id) === true) {
1799
        // Get user info
1800
        $userData = DB::queryFirstRow(
1801
            'SELECT email, auth_type, login
1802
            FROM ' . prefixTable('users') . '
1803
            WHERE id = %i',
1804
            $post_user_id
1805
        );
1806
        if (DB::count() > 0 && empty($userData['email']) === false) {
1807
            // If user pwd is empty then generate a new one and send it to user
1808
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1809
                // Generate new password
1810
                $post_user_password = generateQuickPassword();
1811
            }
1812
1813
            // If LDAP enabled, then
1814
            // check that this password is correct
1815
            $continue = true;
1816
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1817
                $continue = ldapCheckUserPassword(
1818
                    $userData['login'],
1819
                    $post_user_password,
1820
                    $SETTINGS
1821
                );
1822
            }
1823
1824
            if ($continue === true) {
1825
                // Only change if email is successfull
1826
                $passwordManager = new PasswordManager();
1827
                // GEnerate new keys
1828
                $userKeys = generateUserKeys($post_user_password);
1829
1830
                // Update user account
1831
                DB::update(
1832
                    prefixTable('users'),
1833
                    array(
1834
                        'special' => $post_special,
1835
                        'pw' => $passwordManager->hashPassword($post_user_password),
1836
                        'public_key' => $userKeys['public_key'],
1837
                        'private_key' => $userKeys['private_key'],
1838
                        'last_pw_change' => time(),
1839
                    ),
1840
                    'id = %i',
1841
                    $post_user_id
1842
                );
1843
1844
                // Return
1845
                return prepareExchangedData(
1846
                    array(
1847
                        'error' => false,
1848
                        'message' => '',
1849
                        'user_pwd' => $post_user_password,
1850
                        'user_email' => $userData['email'],
1851
                    ),
1852
                    'encode'
1853
                );
1854
            }
1855
            // Return error
1856
            return prepareExchangedData(
1857
                array(
1858
                    'error' => true,
1859
                    'message' => $lang->get('no_email_set'),
1860
                    'debug' => '',
1861
                    'self_change' => $post_self_change,
1862
                ),
1863
                'encode'
1864
            );
1865
        }
1866
1867
        // Error
1868
        return prepareExchangedData(
1869
            array(
1870
                'error' => true,
1871
                'message' => $lang->get('no_email_set'),
1872
                'debug' => '',
1873
            ),
1874
            'encode'
1875
        );
1876
    }
1877
    
1878
    return prepareExchangedData(
1879
        array(
1880
            'error' => true,
1881
            'message' => $lang->get('error_no_user'),
1882
            'debug' => '',
1883
        ),
1884
        'encode'
1885
    );
1886
}
1887
1888
function generateOneTimeCode(
1889
    int $post_user_id
1890
): string
1891
{
1892
    // Load user's language
1893
    $session = SessionManager::getSession();
1894
    $lang = new Language($session->get('user-language') ?? 'english');
1895
    
1896
    if (isUserIdValid($post_user_id) === true) {
1897
        // Get user info
1898
        $userData = DB::queryFirstRow(
1899
            'SELECT email, auth_type, login
1900
            FROM ' . prefixTable('users') . '
1901
            WHERE id = %i',
1902
            $post_user_id
1903
        );
1904
        if (DB::count() > 0 && empty($userData['email']) === false) {
1905
            // Generate pwd
1906
            $password = generateQuickPassword();
1907
1908
            // GEnerate new keys
1909
            $userKeys = generateUserKeys($password);
1910
1911
            // Save in DB
1912
            DB::update(
1913
                prefixTable('users'),
1914
                array(
1915
                    'public_key' => $userKeys['public_key'],
1916
                    'private_key' => $userKeys['private_key'],
1917
                    'special' => 'generate-keys',
1918
                ),
1919
                'id=%i',
1920
                $post_user_id
1921
            );
1922
1923
            return prepareExchangedData(
1924
                array(
1925
                    'error' => false,
1926
                    'message' => '',
1927
                    'code' => $password,
1928
                    'visible_otp' => ADMIN_VISIBLE_OTP_ON_LDAP_IMPORT,
1929
                ),
1930
                'encode'
1931
            );
1932
        }
1933
        
1934
        return prepareExchangedData(
1935
            array(
1936
                'error' => true,
1937
                'message' => $lang->get('no_email_set'),
1938
            ),
1939
            'encode'
1940
        );
1941
    }
1942
        
1943
    return prepareExchangedData(
1944
        array(
1945
            'error' => true,
1946
            'message' => $lang->get('error_no_user'),
1947
        ),
1948
        'encode'
1949
    );
1950
}
1951
1952
function startReEncryptingUserSharekeys(
1953
    int $post_user_id,
1954
    bool $post_self_change,
1955
    array $SETTINGS
1956
): string
1957
{
1958
    // Load user's language
1959
    $session = SessionManager::getSession();
1960
    $lang = new Language($session->get('user-language') ?? 'english');
1961
    
1962
    if (isUserIdValid($post_user_id) === true) {
1963
        // Check if user exists
1964
        DB::queryFirstRow(
1965
            'SELECT *
1966
            FROM ' . prefixTable('users') . '
1967
            WHERE id = %i',
1968
            $post_user_id
1969
        );
1970
        if (DB::count() > 0) {
1971
            // CLear old sharekeys
1972
            if ($post_self_change === false) {
1973
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
1974
            }
1975
1976
            // Continu with next step
1977
            return prepareExchangedData(
1978
                array(
1979
                    'error' => false,
1980
                    'message' => '',
1981
                    'step' => 'step1',
1982
                    'userId' => $post_user_id,
1983
                    'start' => 0,
1984
                    'self_change' => $post_self_change,
1985
                ),
1986
                'encode'
1987
            );
1988
        }
1989
        // Nothing to do
1990
        return prepareExchangedData(
1991
            array(
1992
                'error' => true,
1993
                'message' => $lang->get('error_no_user'),
1994
            ),
1995
            'encode'
1996
        );
1997
    }
1998
1999
    return prepareExchangedData(
2000
        array(
2001
            'error' => true,
2002
            'message' => $lang->get('error_no_user'),
2003
        ),
2004
        'encode'
2005
    );
2006
}
2007
2008
/**
2009
 * Permits to encrypt user's keys
2010
 *
2011
 * @param integer $post_user_id
2012
 * @param boolean $post_self_change
2013
 * @param string $post_action
2014
 * @param integer $post_start
2015
 * @param integer $post_length
2016
 * @param array $SETTINGS
2017
 * @return string
2018
 */
2019
function continueReEncryptingUserSharekeys(
2020
    int     $post_user_id,
2021
    bool    $post_self_change,
2022
    string  $post_action,
2023
    int     $post_start,
2024
    int     $post_length,
2025
    array   $SETTINGS
2026
): string
2027
{
2028
    // Load user's language
2029
    $session = SessionManager::getSession();
2030
    $lang = new Language($session->get('user-language') ?? 'english');
2031
    
2032
    if (isUserIdValid($post_user_id) === true) {
2033
        // Check if user exists
2034
        $userInfo = DB::queryFirstRow(
2035
            'SELECT public_key
2036
            FROM ' . prefixTable('users') . '
2037
            WHERE id = %i',
2038
            $post_user_id
2039
        );
2040
        if (isset($userInfo['public_key']) === true) {
2041
            $return = [];
2042
2043
            // WHAT STEP TO PERFORM?
2044
            if ($post_action === 'step0') {
2045
                // CLear old sharekeys
2046
                if ($post_self_change === false) {
2047
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
2048
                }
2049
2050
                $return['post_action'] = 'step10';
2051
            }
2052
            
2053
            // STEP 1 - ITEMS
2054
            elseif ($post_action === 'step10') {
2055
                $return = continueReEncryptingUserSharekeysStep10(
2056
                    $post_user_id,
2057
                    $post_self_change,
2058
                    $post_action,
2059
                    $post_start,
2060
                    $post_length,
2061
                    $userInfo['public_key'],
2062
                    $SETTINGS
2063
                );
2064
            }
2065
2066
            // STEP 2 - LOGS
2067
            elseif ($post_action === 'step20') {
2068
                $return = continueReEncryptingUserSharekeysStep20(
2069
                    $post_user_id,
2070
                    $post_self_change,
2071
                    $post_action,
2072
                    $post_start,
2073
                    $post_length,
2074
                    $userInfo['public_key'],
2075
                    $SETTINGS
2076
                );
2077
            }
2078
2079
            // STEP 3 - FIELDS
2080
            elseif ($post_action === 'step30') {
2081
                $return = continueReEncryptingUserSharekeysStep30(
2082
                    $post_user_id,
2083
                    $post_self_change,
2084
                    $post_action,
2085
                    $post_start,
2086
                    $post_length,
2087
                    $userInfo['public_key'],
2088
                    $SETTINGS
2089
                );
2090
            }
2091
            
2092
            // STEP 4 - SUGGESTIONS
2093
            elseif ($post_action === 'step40') {
2094
                $return = continueReEncryptingUserSharekeysStep40(
2095
                    $post_user_id,
2096
                    $post_self_change,
2097
                    $post_action,
2098
                    $post_start,
2099
                    $post_length,
2100
                    $userInfo['public_key'],
2101
                    $SETTINGS
2102
                );
2103
            }
2104
            
2105
            // STEP 5 - FILES
2106
            elseif ($post_action === 'step50') {
2107
                $return = continueReEncryptingUserSharekeysStep50(
2108
                    $post_user_id,
2109
                    $post_self_change,
2110
                    $post_action,
2111
                    $post_start,
2112
                    $post_length,
2113
                    $userInfo['public_key'],
2114
                    $SETTINGS
2115
                );
2116
            }
2117
            
2118
            // STEP 6 - PERSONAL ITEMS
2119
            elseif ($post_action === 'step60') {
2120
                $return = continueReEncryptingUserSharekeysStep60(
2121
                    $post_user_id,
2122
                    $post_self_change,
2123
                    $post_action,
2124
                    $post_start,
2125
                    $post_length,
2126
                    $userInfo['public_key'],
2127
                    $SETTINGS
2128
                );
2129
            }
2130
            
2131
            // Continu with next step
2132
            return prepareExchangedData(
2133
                array(
2134
                    'error' => false,
2135
                    'message' => '',
2136
                    'step' => isset($return['post_action']) === true ? $return['post_action'] : '',
2137
                    'start' => isset($return['next_start']) === true ? $return['next_start'] : 0,
2138
                    'userId' => $post_user_id,
2139
                    'self_change' => $post_self_change,
2140
                ),
2141
                'encode'
2142
            );
2143
        }
2144
        
2145
        // Nothing to do
2146
        return prepareExchangedData(
2147
            array(
2148
                'error' => false,
2149
                'message' => '',
2150
                'step' => 'finished',
2151
                'start' => 0,
2152
                'userId' => $post_user_id,
2153
                'self_change' => $post_self_change,
2154
            ),
2155
            'encode'
2156
        );
2157
    }
2158
    
2159
    // Nothing to do
2160
    return prepareExchangedData(
2161
        array(
2162
            'error' => true,
2163
            'message' => $lang->get('error_no_user'),
2164
            'extra' => $post_user_id,
2165
        ),
2166
        'encode'
2167
    );
2168
}
2169
2170
function continueReEncryptingUserSharekeysStep10(
2171
    int $post_user_id,
2172
    bool $post_self_change,
2173
    string $post_action,
2174
    int $post_start,
2175
    int $post_length,
2176
    string $user_public_key,
2177
    array $SETTINGS
2178
): array 
2179
{
2180
    $session = SessionManager::getSession();
2181
    // Loop on items
2182
    $rows = DB::query(
2183
        'SELECT id, pw
2184
        FROM ' . prefixTable('items') . '
2185
        WHERE perso = 0
2186
        LIMIT ' . $post_start . ', ' . $post_length
2187
    );
2188
    foreach ($rows as $record) {
2189
        // Get itemKey from current user
2190
        $currentUserKey = DB::queryFirstRow(
2191
            'SELECT share_key, increment_id
2192
            FROM ' . prefixTable('sharekeys_items') . '
2193
            WHERE object_id = %i AND user_id = %i',
2194
            $record['id'],
2195
            $session->get('user-id')
2196
        );
2197
2198
        // do we have any input? (#3481)
2199
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2200
            continue;
2201
        }
2202
2203
        // Decrypt itemkey with admin key
2204
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2205
        
2206
        // Encrypt Item key
2207
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2208
        
2209
        // Save the key in DB
2210
        if ($post_self_change === false) {
2211
            DB::insert(
2212
                prefixTable('sharekeys_items'),
2213
                array(
2214
                    'object_id' => (int) $record['id'],
2215
                    'user_id' => (int) $post_user_id,
2216
                    'share_key' => $share_key_for_item,
2217
                )
2218
            );
2219
        } else {
2220
            // Get itemIncrement from selected user
2221
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2222
                $currentUserKey = DB::queryFirstRow(
2223
                    'SELECT increment_id
2224
                    FROM ' . prefixTable('sharekeys_items') . '
2225
                    WHERE object_id = %i AND user_id = %i',
2226
                    $record['id'],
2227
                    $post_user_id
2228
                );
2229
2230
                if (DB::count() > 0) {
2231
                    // NOw update
2232
                    DB::update(
2233
                        prefixTable('sharekeys_items'),
2234
                        array(
2235
                            'share_key' => $share_key_for_item,
2236
                        ),
2237
                        'increment_id = %i',
2238
                        $currentUserKey['increment_id']
2239
                    );
2240
                } else {
2241
                    DB::insert(
2242
                        prefixTable('sharekeys_items'),
2243
                        array(
2244
                            'object_id' => (int) $record['id'],
2245
                            'user_id' => (int) $post_user_id,
2246
                            'share_key' => $share_key_for_item,
2247
                        )
2248
                    );
2249
                }
2250
            }
2251
        }
2252
    }
2253
2254
    // SHould we change step?
2255
    DB::query(
2256
        'SELECT *
2257
        FROM ' . prefixTable('items') . '
2258
        WHERE perso = 0'
2259
    );
2260
2261
    $next_start = (int) $post_start + (int) $post_length;
2262
    return [
2263
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2264
        'post_action' => $next_start > DB::count() ? 'step20' : 'step10',
2265
    ];
2266
}
2267
2268
function continueReEncryptingUserSharekeysStep20(
2269
    int $post_user_id,
2270
    bool $post_self_change,
2271
    string $post_action,
2272
    int $post_start,
2273
    int $post_length,
2274
    string $user_public_key,
2275
    array $SETTINGS
2276
): array
2277
{
2278
    $session = SessionManager::getSession();
2279
    // Loop on logs
2280
    $rows = DB::query(
2281
        'SELECT increment_id
2282
        FROM ' . prefixTable('log_items') . '
2283
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
2284
        LIMIT ' . $post_start . ', ' . $post_length
2285
    );
2286
    foreach ($rows as $record) {
2287
        // Get itemKey from current user
2288
        $currentUserKey = DB::queryFirstRow(
2289
            'SELECT share_key
2290
            FROM ' . prefixTable('sharekeys_logs') . '
2291
            WHERE object_id = %i AND user_id = %i',
2292
            $record['increment_id'],
2293
            $session->get('user-id')
2294
        );
2295
2296
        // do we have any input? (#3481)
2297
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2298
            continue;
2299
        }
2300
2301
        // Decrypt itemkey with admin key
2302
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2303
2304
        // Encrypt Item key
2305
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2306
2307
        // Save the key in DB
2308
        if ($post_self_change === false) {
2309
            DB::insert(
2310
                prefixTable('sharekeys_logs'),
2311
                array(
2312
                    'object_id' => (int) $record['increment_id'],
2313
                    'user_id' => (int) $post_user_id,
2314
                    'share_key' => $share_key_for_item,
2315
                )
2316
            );
2317
        } else {
2318
            // Get itemIncrement from selected user
2319
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2320
                $currentUserKey = DB::queryFirstRow(
2321
                    'SELECT increment_id
2322
                    FROM ' . prefixTable('sharekeys_items') . '
2323
                    WHERE object_id = %i AND user_id = %i',
2324
                    $record['id'],
2325
                    $post_user_id
2326
                );
2327
            }
2328
2329
            // NOw update
2330
            DB::update(
2331
                prefixTable('sharekeys_logs'),
2332
                array(
2333
                    'share_key' => $share_key_for_item,
2334
                ),
2335
                'increment_id = %i',
2336
                $currentUserKey['increment_id']
2337
            );
2338
        }
2339
    }
2340
2341
    // SHould we change step?
2342
    DB::query(
2343
        'SELECT increment_id
2344
        FROM ' . prefixTable('log_items') . '
2345
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
2346
    );
2347
2348
    $next_start = (int) $post_start + (int) $post_length;
2349
    return [
2350
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2351
        'post_action' => $next_start > DB::count() ? 'step30' : 'step20',
2352
    ];
2353
}
2354
2355
function continueReEncryptingUserSharekeysStep30(
2356
    int $post_user_id,
2357
    bool $post_self_change,
2358
    string $post_action,
2359
    int $post_start,
2360
    int $post_length,
2361
    string $user_public_key,
2362
    array $SETTINGS
2363
): array
2364
{
2365
    $session = SessionManager::getSession();
2366
    // Loop on fields
2367
    $rows = DB::query(
2368
        'SELECT id
2369
        FROM ' . prefixTable('categories_items') . '
2370
        WHERE encryption_type = "teampass_aes"
2371
        LIMIT ' . $post_start . ', ' . $post_length
2372
    );
2373
    foreach ($rows as $record) {
2374
        // Get itemKey from current user
2375
        $currentUserKey = DB::queryFirstRow(
2376
            'SELECT share_key
2377
            FROM ' . prefixTable('sharekeys_fields') . '
2378
            WHERE object_id = %i AND user_id = %i',
2379
            $record['id'],
2380
            $session->get('user-id')
2381
        );
2382
2383
        // do we have any input? (#3481)
2384
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2385
            continue;
2386
        }
2387
2388
        // Decrypt itemkey with admin key
2389
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2390
2391
        // Encrypt Item key
2392
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2393
2394
        // Save the key in DB
2395
        if ($post_self_change === false) {
2396
            DB::insert(
2397
                prefixTable('sharekeys_fields'),
2398
                array(
2399
                    'object_id' => (int) $record['id'],
2400
                    'user_id' => (int) $post_user_id,
2401
                    'share_key' => $share_key_for_item,
2402
                )
2403
            );
2404
        } else {
2405
            // Get itemIncrement from selected user
2406
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2407
                $currentUserKey = DB::queryFirstRow(
2408
                    'SELECT increment_id
2409
                    FROM ' . prefixTable('sharekeys_items') . '
2410
                    WHERE object_id = %i AND user_id = %i',
2411
                    $record['id'],
2412
                    $post_user_id
2413
                );
2414
            }
2415
2416
            // NOw update
2417
            DB::update(
2418
                prefixTable('sharekeys_fields'),
2419
                array(
2420
                    'share_key' => $share_key_for_item,
2421
                ),
2422
                'increment_id = %i',
2423
                $currentUserKey['increment_id']
2424
            );
2425
        }
2426
    }
2427
2428
    // SHould we change step?
2429
    DB::query(
2430
        'SELECT *
2431
        FROM ' . prefixTable('categories_items') . '
2432
        WHERE encryption_type = "teampass_aes"'
2433
    );
2434
2435
    $next_start = (int) $post_start + (int) $post_length;
2436
    return [
2437
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2438
        'post_action' => $next_start > DB::count() ? 'step40' : 'step30',
2439
    ];
2440
}
2441
2442
function continueReEncryptingUserSharekeysStep40(
2443
    int $post_user_id,
2444
    bool $post_self_change,
2445
    string $post_action,
2446
    int $post_start,
2447
    int $post_length,
2448
    string $user_public_key,
2449
    array $SETTINGS
2450
): array
2451
{
2452
    $session = SessionManager::getSession();
2453
    // Loop on suggestions
2454
    $rows = DB::query(
2455
        'SELECT id
2456
        FROM ' . prefixTable('suggestion') . '
2457
        LIMIT ' . $post_start . ', ' . $post_length
2458
    );
2459
    foreach ($rows as $record) {
2460
        // Get itemKey from current user
2461
        $currentUserKey = DB::queryFirstRow(
2462
            'SELECT share_key
2463
            FROM ' . prefixTable('sharekeys_suggestions') . '
2464
            WHERE object_id = %i AND user_id = %i',
2465
            $record['id'],
2466
            $session->get('user-id')
2467
        );
2468
2469
        // do we have any input? (#3481)
2470
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2471
            continue;
2472
        }
2473
2474
        // Decrypt itemkey with admin key
2475
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2476
2477
        // Encrypt Item key
2478
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2479
2480
        // Save the key in DB
2481
        if ($post_self_change === false) {
2482
            DB::insert(
2483
                prefixTable('sharekeys_suggestions'),
2484
                array(
2485
                    'object_id' => (int) $record['id'],
2486
                    'user_id' => (int) $post_user_id,
2487
                    'share_key' => $share_key_for_item,
2488
                )
2489
            );
2490
        } else {
2491
            // Get itemIncrement from selected user
2492
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2493
                $currentUserKey = DB::queryFirstRow(
2494
                    'SELECT increment_id
2495
                    FROM ' . prefixTable('sharekeys_items') . '
2496
                    WHERE object_id = %i AND user_id = %i',
2497
                    $record['id'],
2498
                    $post_user_id
2499
                );
2500
            }
2501
2502
            // NOw update
2503
            DB::update(
2504
                prefixTable('sharekeys_suggestions'),
2505
                array(
2506
                    'share_key' => $share_key_for_item,
2507
                ),
2508
                'increment_id = %i',
2509
                $currentUserKey['increment_id']
2510
            );
2511
        }
2512
    }
2513
2514
    // SHould we change step?
2515
    DB::query(
2516
        'SELECT *
2517
        FROM ' . prefixTable('suggestion')
2518
    );
2519
2520
    $next_start = (int) $post_start + (int) $post_length;
2521
    return [
2522
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2523
        'post_action' => $next_start > DB::count() ? 'step50' : 'step40',
2524
    ];
2525
}
2526
2527
function continueReEncryptingUserSharekeysStep50(
2528
    int $post_user_id,
2529
    bool $post_self_change,
2530
    string $post_action,
2531
    int $post_start,
2532
    int $post_length,
2533
    string $user_public_key,
2534
    array $SETTINGS
2535
): array
2536
{
2537
    $session = SessionManager::getSession();
2538
    // Loop on files
2539
    $rows = DB::query(
2540
        'SELECT id
2541
        FROM ' . prefixTable('files') . '
2542
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2543
        LIMIT ' . $post_start . ', ' . $post_length
2544
    ); //aes_encryption
2545
    foreach ($rows as $record) {
2546
        // Get itemKey from current user
2547
        $currentUserKey = DB::queryFirstRow(
2548
            'SELECT share_key
2549
            FROM ' . prefixTable('sharekeys_files') . '
2550
            WHERE object_id = %i AND user_id = %i',
2551
            $record['id'],
2552
            $session->get('user-id')
2553
        );
2554
2555
        // do we have any input? (#3481)
2556
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2557
            continue;
2558
        }
2559
2560
        // Decrypt itemkey with admin key
2561
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2562
2563
        // Encrypt Item key
2564
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2565
2566
        // Save the key in DB
2567
        if ($post_self_change === false) {
2568
            DB::insert(
2569
                prefixTable('sharekeys_files'),
2570
                array(
2571
                    'object_id' => (int) $record['id'],
2572
                    'user_id' => (int) $post_user_id,
2573
                    'share_key' => $share_key_for_item,
2574
                )
2575
            );
2576
        } else {
2577
            // Get itemIncrement from selected user
2578
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2579
                $currentUserKey = DB::queryFirstRow(
2580
                    'SELECT increment_id
2581
                    FROM ' . prefixTable('sharekeys_items') . '
2582
                    WHERE object_id = %i AND user_id = %i',
2583
                    $record['id'],
2584
                    $post_user_id
2585
                );
2586
            }
2587
2588
            // NOw update
2589
            DB::update(
2590
                prefixTable('sharekeys_files'),
2591
                array(
2592
                    'share_key' => $share_key_for_item,
2593
                ),
2594
                'increment_id = %i',
2595
                $currentUserKey['increment_id']
2596
            );
2597
        }
2598
    }
2599
2600
    // SHould we change step?
2601
    DB::query(
2602
        'SELECT *
2603
        FROM ' . prefixTable('files') . '
2604
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2605
    );
2606
2607
    $next_start = (int) $post_start + (int) $post_length;
2608
    return [
2609
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2610
        'post_action' => $next_start > DB::count() ? 'step60' : 'step50',
2611
    ];
2612
}
2613
2614
function continueReEncryptingUserSharekeysStep60(
2615
    int $post_user_id,
2616
    bool $post_self_change,
2617
    string $post_action,
2618
    int $post_start,
2619
    int $post_length,
2620
    string $user_public_key,
2621
    array $SETTINGS
2622
): array
2623
{
2624
    $session = SessionManager::getSession();
2625
    // IF USER IS NOT THE SAME
2626
    if ((int) $post_user_id === (int) $session->get('user-id')) {
2627
        return [
2628
            'next_start' => 0,
2629
            'post_action' => 'finished',
2630
        ];
2631
    }
2632
    
2633
    // Loop on persoanl items
2634
    if (count($session->get('user-personal_folders')) > 0) {
2635
        $rows = DB::query(
2636
            'SELECT id, pw
2637
            FROM ' . prefixTable('items') . '
2638
            WHERE perso = 1 AND id_tree IN %ls AND encryption_type = %s
2639
            LIMIT ' . $post_start . ', ' . $post_length,
2640
            $session->get('user-personal_folders'),
2641
            "defuse"
2642
        );
2643
        foreach ($rows as $record) {
2644
            // Get itemKey from current user
2645
            $currentUserKey = DB::queryFirstRow(
2646
                'SELECT share_key, increment_id
2647
                FROM ' . prefixTable('sharekeys_items') . '
2648
                WHERE object_id = %i AND user_id = %i',
2649
                $record['id'],
2650
                $session->get('user-id')
2651
            );
2652
2653
            // Decrypt itemkey with admin key
2654
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2655
2656
            // Encrypt Item key
2657
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2658
2659
            // Save the key in DB
2660
            if ($post_self_change === false) {
2661
                DB::insert(
2662
                    prefixTable('sharekeys_items'),
2663
                    array(
2664
                        'object_id' => (int) $record['id'],
2665
                        'user_id' => (int) $post_user_id,
2666
                        'share_key' => $share_key_for_item,
2667
                    )
2668
                );
2669
            } else {
2670
                // Get itemIncrement from selected user
2671
                if ((int) $post_user_id !== (int) $session->get('user-id')) {
2672
                    $currentUserKey = DB::queryFirstRow(
2673
                        'SELECT increment_id
2674
                        FROM ' . prefixTable('sharekeys_items') . '
2675
                        WHERE object_id = %i AND user_id = %i',
2676
                        $record['id'],
2677
                        $post_user_id
2678
                    );
2679
                }
2680
2681
                // NOw update
2682
                DB::update(
2683
                    prefixTable('sharekeys_items'),
2684
                    array(
2685
                        'share_key' => $share_key_for_item,
2686
                    ),
2687
                    'increment_id = %i',
2688
                    $currentUserKey['increment_id']
2689
                );
2690
            }
2691
        }
2692
    }
2693
2694
    // SHould we change step?
2695
    DB::query(
2696
        'SELECT *
2697
        FROM ' . prefixTable('items') . '
2698
        WHERE perso = 0'
2699
    );
2700
2701
    $next_start = (int) $post_start + (int) $post_length;
2702
    return [
2703
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2704
        'post_action' => $next_start > DB::count() ? 'finished' : 'step60',
2705
    ];
2706
}
2707
2708
function migrateTo3_DoUserPersonalItemsEncryption(
2709
    int $post_user_id,
2710
    int $post_start,
2711
    int $post_length,
2712
    int $post_counterItemsToTreat,
2713
    string $post_user_psk,
2714
    array $SETTINGS
2715
) {
2716
    $next_step = 'psk';
2717
    
2718
    $session = SessionManager::getSession();
2719
    $lang = new Language($session->get('user-language') ?? 'english');
2720
    
2721
    if (isUserIdValid($post_user_id) === true) {
2722
        // Check if user exists
2723
        $userInfo = DB::queryFirstRow(
2724
            'SELECT public_key, encrypted_psk
2725
            FROM ' . prefixTable('users') . '
2726
            WHERE id = %i',
2727
            $post_user_id
2728
        );
2729
        if (DB::count() > 0) {
2730
            // check if psk is correct.
2731
            if (empty($userInfo['encrypted_psk']) === false) {//echo $post_user_psk." ;; ".$userInfo['encrypted_psk']." ;; ";
2732
                $user_key_encoded = defuse_validate_personal_key(
2733
                    html_entity_decode($post_user_psk), // convert tspecial string back to their original characters due to FILTER_SANITIZE_FULL_SPECIAL_CHARS
2734
                    $userInfo['encrypted_psk']
2735
                );
2736
2737
                if (strpos($user_key_encoded, "Error ") !== false) {
2738
                    return prepareExchangedData(
2739
                        array(
2740
                            'error' => true,
2741
                            'message' => $lang->get('bad_psk'),
2742
                        ),
2743
                        'encode'
2744
                    );
2745
                }
2746
2747
                // Get number of user's personal items with no AES encryption
2748
                if ($post_counterItemsToTreat === -1) {
2749
                    DB::query(
2750
                        'SELECT id
2751
                        FROM ' . prefixTable('items') . '
2752
                        WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2753
                        $session->get('user-personal_folders'),
2754
                        'teampass_aes'
2755
                    );
2756
                    $countUserPersonalItems = DB::count();
2757
                } else {
2758
                    $countUserPersonalItems = $post_counterItemsToTreat;
2759
                }
2760
2761
                // Loop on persoanl items
2762
                $rows = DB::query(
2763
                    'SELECT id, pw
2764
                    FROM ' . prefixTable('items') . '
2765
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s
2766
                    LIMIT ' . $post_length,
2767
                    $session->get('user-personal_folders'),
2768
                    'teampass_aes'
2769
                );
2770
                foreach ($rows as $record) {
2771
                    // Decrypt with Defuse
2772
                    $passwd = cryption(
2773
                        $record['pw'],
2774
                        $user_key_encoded,
2775
                        'decrypt',
2776
                        $SETTINGS
2777
                    );
2778
2779
                    // Encrypt with Object Key
2780
                    $cryptedStuff = doDataEncryption(html_entity_decode($passwd['string']));
2781
2782
                    // Store new password in DB
2783
                    DB::update(
2784
                        prefixTable('items'),
2785
                        array(
2786
                            'pw' => $cryptedStuff['encrypted'],
2787
                            'encryption_type' => 'teampass_aes',
2788
                        ),
2789
                        'id = %i',
2790
                        $record['id']
2791
                    );
2792
2793
                    // Insert in DB the new object key for this item by user
2794
                    DB::insert(
2795
                        prefixTable('sharekeys_items'),
2796
                        array(
2797
                            'object_id' => (int) $record['id'],
2798
                            'user_id' => (int) $post_user_id,
2799
                            'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2800
                        )
2801
                    );
2802
2803
2804
                    // Does this item has Files?
2805
                    // Loop on files
2806
                    $rows = DB::query(
2807
                        'SELECT id, file
2808
                        FROM ' . prefixTable('files') . '
2809
                        WHERE status != %s
2810
                        AND id_item = %i',
2811
                        TP_ENCRYPTION_NAME,
2812
                        $record['id']
2813
                    );
2814
                    //aes_encryption
2815
                    foreach ($rows as $record2) {
2816
                        // Now decrypt the file
2817
                        prepareFileWithDefuse(
2818
                            'decrypt',
2819
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2820
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2821
                            $SETTINGS,
2822
                            $post_user_psk
2823
                        );
2824
2825
                        // Encrypt the file
2826
                        $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2827
2828
                        DB::update(
2829
                            prefixTable('files'),
2830
                            array(
2831
                                'file' => $encryptedFile['fileHash'],
2832
                                'status' => TP_ENCRYPTION_NAME,
2833
                            ),
2834
                            'id = %i',
2835
                            $record2['id']
2836
                        );
2837
2838
                        // Save key
2839
                        DB::insert(
2840
                            prefixTable('sharekeys_files'),
2841
                            array(
2842
                                'object_id' => (int) $record2['id'],
2843
                                'user_id' => (int) $session->get('user-id'),
2844
                                'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $session->get('user-public_key')),
2845
                            )
2846
                        );
2847
2848
                        // Unlink original file
2849
                        unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2850
                    }
2851
                }
2852
2853
                // SHould we change step?
2854
                $next_start = (int) $post_start + (int) $post_length;
2855
                DB::query(
2856
                    'SELECT id
2857
                    FROM ' . prefixTable('items') . '
2858
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2859
                    $session->get('user-personal_folders'),
2860
                    'teampass_aes'
2861
                );
2862
                if (DB::count() === 0 || ($next_start - $post_length) >= $countUserPersonalItems) {
2863
                    // Now update user
2864
                    DB::update(
2865
                        prefixTable('users'),
2866
                        array(
2867
                            'special' => 'none',
2868
                            'upgrade_needed' => 0,
2869
                            'encrypted_psk' => '',
2870
                        ),
2871
                        'id = %i',
2872
                        $post_user_id
2873
                    );
2874
2875
                    $next_step = 'finished';
2876
                    $next_start = 0;
2877
                }
2878
2879
                // Continu with next step
2880
                return prepareExchangedData(
2881
                    array(
2882
                        'error' => false,
2883
                        'message' => '',
2884
                        'step' => $next_step,
2885
                        'start' => $next_start,
2886
                        'userId' => $post_user_id
2887
                    ),
2888
                    'encode'
2889
                );
2890
            }
2891
        }
2892
        
2893
        // Nothing to do
2894
        return prepareExchangedData(
2895
            array(
2896
                'error' => true,
2897
                'message' => $lang->get('error_no_user'),
2898
            ),
2899
            'encode'
2900
        );
2901
    }
2902
    
2903
    // Nothing to do
2904
    return prepareExchangedData(
2905
        array(
2906
            'error' => true,
2907
            'message' => $lang->get('error_no_user'),
2908
        ),
2909
        'encode'
2910
    );
2911
}
2912
2913
2914
function getUserInfo(
2915
    int $post_user_id,
2916
    string $post_fields,
2917
    array $SETTINGS
2918
)
2919
{
2920
    // Load user's language
2921
    $session = SessionManager::getSession();
2922
    $lang = new Language($session->get('user-language') ?? 'english');
2923
    
2924
    if (isUserIdValid($post_user_id) === true) {
2925
        // Get user info
2926
        $userData = DB::queryFirstRow(
2927
            'SELECT '.$post_fields.'
2928
            FROM ' . prefixTable('users') . '
2929
            WHERE id = %i',
2930
            $post_user_id
2931
        );
2932
        if (DB::count() > 0) {
2933
            return prepareExchangedData(
2934
                array(
2935
                    'error' => false,
2936
                    'message' => '',
2937
                    'queryResults' => $userData,
2938
                ),
2939
                'encode'
2940
            );
2941
        }
2942
    }
2943
    return prepareExchangedData(
2944
        array(
2945
            'error' => true,
2946
            'message' => $lang->get('error_no_user'),
2947
        ),
2948
        'encode'
2949
    );
2950
}
2951
2952
/**
2953
 * Change user auth password
2954
 *
2955
 * @param integer $post_user_id
2956
 * @param string $post_current_pwd
2957
 * @param string $post_new_pwd
2958
 * @param array $SETTINGS
2959
 * @return string
2960
 */
2961
function changeUserAuthenticationPassword(
2962
    int $post_user_id,
2963
    string $post_current_pwd,
2964
    string $post_new_pwd,
2965
    array $SETTINGS
2966
)
2967
{
2968
    $session = SessionManager::getSession();
2969
    $lang = new Language($session->get('user-language') ?? 'english');
2970
    
2971
    if (isUserIdValid($post_user_id) === true) {
2972
        // Get user info
2973
        $userData = DB::queryFirstRow(
2974
            'SELECT auth_type, login, private_key
2975
            FROM ' . prefixTable('users') . '
2976
            WHERE id = %i',
2977
            $post_user_id
2978
        );
2979
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
2980
            // Now check if current password is correct
2981
            // For this, just check if it is possible to decrypt the privatekey
2982
            // And compare it to the one in session
2983
            try {
2984
                $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
2985
            } catch (Exception $e) {
2986
                return prepareExchangedData(
2987
                    array(
2988
                        'error' => true,
2989
                        'message' => $lang->get('bad_password'),
2990
                    ),
2991
                    'encode'
2992
                );
2993
            }
2994
2995
            $lang = new Language($session->get('user-language') ?? 'english');
2996
2997
            if ($session->get('user-private_key') === $privateKey) {
2998
                // Encrypt it with new password
2999
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
3000
3001
                // Generate new hash for auth password
3002
                $passwordManager = new PasswordManager();
3003
3004
                // Prepare variables
3005
                $newPw = $passwordManager->hashPassword($post_new_pwd);
3006
3007
                // Update user account
3008
                DB::update(
3009
                    prefixTable('users'),
3010
                    array(
3011
                        'private_key' => $hashedPrivateKey,
3012
                        'pw' => $newPw,
3013
                        'special' => 'none',
3014
                    ),
3015
                    'id = %i',
3016
                    $post_user_id
3017
                );
3018
3019
                $session->set('user-private_key', $privateKey);
3020
3021
                return prepareExchangedData(
3022
                    array(
3023
                        'error' => false,
3024
                        'message' => $lang->get('done'),'',
3025
                    ),
3026
                    'encode'
3027
                );
3028
            }
3029
            
3030
            // ERROR
3031
            return prepareExchangedData(
3032
                array(
3033
                    'error' => true,
3034
                    'message' => $lang->get('bad_password'),
3035
                ),
3036
                'encode'
3037
            );
3038
        }
3039
    }
3040
        
3041
    return prepareExchangedData(
3042
        array(
3043
            'error' => true,
3044
            'message' => $lang->get('error_no_user'),
3045
        ),
3046
        'encode'
3047
    );
3048
}
3049
3050
/**
3051
 * Change user LDAP auth password
3052
 *
3053
 * @param integer $post_user_id
3054
 * @param string $post_previous_pwd
3055
 * @param string $post_current_pwd
3056
 * @param array $SETTINGS
3057
 * @return string
3058
 */            
3059
function changeUserLDAPAuthenticationPassword(
3060
    int $post_user_id,
3061
    string $post_previous_pwd,
3062
    string $post_current_pwd,
3063
    array $SETTINGS
3064
)
3065
{
3066
    $session = SessionManager::getSession();
3067
    // Load user's language
3068
    $lang = new Language($session->get('user-language') ?? 'english');
3069
    
3070
    if (isUserIdValid($post_user_id) === true) {
3071
        // Get user info
3072
        $userData = DB::queryFirstRow(
3073
            'SELECT auth_type, login, private_key, special
3074
            FROM ' . prefixTable('users') . '
3075
            WHERE id = %i',
3076
            $post_user_id
3077
        );
3078
        
3079
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
3080
            // Now check if current password is correct (only if not ldap)
3081
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
3082
                // As it is a change for an LDAP user
3083
                
3084
                // Now check if current password is correct
3085
                // For this, just check if it is possible to decrypt the privatekey
3086
                // And compare it to the one in session
3087
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3088
3089
                // Encrypt it with new password
3090
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3091
3092
                // Update user account
3093
                DB::update(
3094
                    prefixTable('users'),
3095
                    array(
3096
                        'private_key' => $hashedPrivateKey,
3097
                        'special' => 'none',
3098
                    ),
3099
                    'id = %i',
3100
                    $post_user_id
3101
                );
3102
3103
                $session->set('user-private_key', $privateKey);
3104
3105
                return prepareExchangedData(
3106
                    array(
3107
                        'error' => false,
3108
                        'message' => $lang->get('done'),'',
3109
                    ),
3110
                    'encode'
3111
                );
3112
            }
3113
3114
            // For this, just check if it is possible to decrypt the privatekey
3115
            // And try to decrypt one existing key
3116
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3117
3118
            if (empty($privateKey) === true) {
3119
                return prepareExchangedData(
3120
                    array(
3121
                        'error' => true,
3122
                        'message' => $lang->get('password_is_not_correct'),
3123
                    ),
3124
                    'encode'
3125
                );
3126
            }
3127
3128
            // Test if possible to decvrypt one key
3129
            // Get one item
3130
            $record = DB::queryFirstRow(
3131
                'SELECT id, pw
3132
                FROM ' . prefixTable('items') . '
3133
                WHERE perso = 0'
3134
            );
3135
3136
            // Get itemKey from current user
3137
            $currentUserKey = DB::queryFirstRow(
3138
                'SELECT share_key, increment_id
3139
                FROM ' . prefixTable('sharekeys_items') . '
3140
                WHERE object_id = %i AND user_id = %i',
3141
                $record['id'],
3142
                $post_user_id
3143
            );
3144
3145
            if (count($currentUserKey) > 0) {
3146
                // Decrypt itemkey with user key
3147
                // use old password to decrypt private_key
3148
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
3149
                
3150
                if (empty(base64_decode($itemKey)) === false) {
3151
                    // GOOD password
3152
                    // Encrypt it with current password
3153
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3154
                    
3155
                    // Update user account
3156
                    DB::update(
3157
                        prefixTable('users'),
3158
                        array(
3159
                            'private_key' => $hashedPrivateKey,
3160
                            'special' => 'none',
3161
                        ),
3162
                        'id = %i',
3163
                        $post_user_id
3164
                    );
3165
                    
3166
                    $lang = new Language($session->get('user-language') ?? 'english');
3167
                    $session->set('user-private_key', $privateKey);
3168
3169
                    return prepareExchangedData(
3170
                        array(
3171
                            'error' => false,
3172
                            'message' => $lang->get('done'),
3173
                        ),
3174
                        'encode'
3175
                    );
3176
                }
3177
            }
3178
            
3179
            // ERROR
3180
            return prepareExchangedData(
3181
                array(
3182
                    'error' => true,
3183
                    'message' => $lang->get('bad_password'),
3184
                ),
3185
                'encode'
3186
            );
3187
        }
3188
    }
3189
3190
    // ERROR
3191
    return prepareExchangedData(
3192
        array(
3193
            'error' => true,
3194
            'message' => $lang->get('error_no_user'),
3195
        ),
3196
        'encode'
3197
    );
3198
}
3199
3200
/**
3201
 * Change user LDAP auth password
3202
 *
3203
 * @param integer $post_user_id
3204
 * @param string $post_current_pwd
3205
 * @param string $post_new_pwd
3206
 * @param array $SETTINGS
3207
 * @return string
3208
 */
3209
function increaseSessionDuration(
3210
    int $duration
3211
): string
3212
{
3213
    $session = SessionManager::getSession();
3214
    // check if session is not already expired.
3215
    if ($session->get('user-session_duration') > time()) {
3216
        // Calculate end of session
3217
        $session->set('user-session_duration', (int) $session->get('user-session_duration') + $duration);
3218
        // Update table
3219
        DB::update(
3220
            prefixTable('users'),
3221
            array(
3222
                'session_end' => $session->get('user-session_duration'),
3223
            ),
3224
            'id = %i',
3225
            $session->get('user-id')
3226
        );
3227
        // Return data
3228
        return '[{"new_value":"' . $session->get('user-session_duration') . '"}]';
3229
    }
3230
    
3231
    return '[{"new_value":"expired"}]';
3232
}
3233
3234
function generateAnOTP(string $label, bool $with_qrcode = false, string $secretKey = ''): string
3235
{
3236
    // generate new secret
3237
    $tfa = new TwoFactorAuth();
3238
    if ($secretKey === '') {
3239
        $secretKey = $tfa->createSecret();
3240
    }
3241
3242
    // generate new QR
3243
    if ($with_qrcode === true) {
3244
        $qrcode = $tfa->getQRCodeImageAsDataUri(
3245
            $label,
3246
            $secretKey
3247
        );
3248
    }
3249
3250
    // ERROR
3251
    return prepareExchangedData(
3252
        array(
3253
            'error' => false,
3254
            'message' => '',
3255
            'secret' => $secretKey,
3256
            'qrcode' => $qrcode ?? '',
3257
        ),
3258
        'encode'
3259
    );
3260
}