1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Teampass - a collaborative passwords manager. |
7
|
|
|
* --- |
8
|
|
|
* This library is distributed in the hope that it will be useful, |
9
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
10
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
11
|
|
|
* --- |
12
|
|
|
* |
13
|
|
|
* @project Teampass |
14
|
|
|
* @file main.functions.php |
15
|
|
|
* --- |
16
|
|
|
* |
17
|
|
|
* @author Nils Laumaillé ([email protected]) |
18
|
|
|
* |
19
|
|
|
* @copyright 2009-2023 Teampass.net |
20
|
|
|
* |
21
|
|
|
* @license https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0 |
22
|
|
|
* --- |
23
|
|
|
* |
24
|
|
|
* @see https://www.teampass.net |
25
|
|
|
*/ |
26
|
|
|
|
27
|
|
|
use LdapRecord\Connection; |
28
|
|
|
use ForceUTF8\Encoding; |
29
|
|
|
use Elegant\Sanitizer\Sanitizer; |
30
|
|
|
use voku\helper\AntiXSS; |
31
|
|
|
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator; |
32
|
|
|
use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator; |
33
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
|
|
|
34
|
|
|
use TeampassClasses\SessionManager\SessionManager; |
35
|
|
|
use TeampassClasses\Language\Language; |
36
|
|
|
use TeampassClasses\NestedTree\NestedTree; |
37
|
|
|
use Defuse\Crypto\Key; |
38
|
|
|
use Defuse\Crypto\Crypto; |
39
|
|
|
use Defuse\Crypto\KeyProtectedByPassword; |
40
|
|
|
use Defuse\Crypto\File as CryptoFile; |
41
|
|
|
use Defuse\Crypto\Exception as CryptoException; |
42
|
|
|
use Elegant\Sanitizer\Filters\Uppercase; |
43
|
|
|
use PHPMailer\PHPMailer\PHPMailer; |
44
|
|
|
use PasswordLib\PasswordLib; |
45
|
|
|
use Symfony\Component\Process\Exception\ProcessFailedException; |
46
|
|
|
use Symfony\Component\Process\Process; |
47
|
|
|
use Symfony\Component\Process\PhpExecutableFinder; |
48
|
|
|
use TeampassClasses\Encryption\Encryption; |
49
|
|
|
|
50
|
|
|
|
51
|
|
|
// Load config if $SETTINGS not defined |
52
|
|
|
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) { |
53
|
|
|
include_once __DIR__ . '/../includes/config/tp.config.php'; |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
header('Content-type: text/html; charset=utf-8'); |
57
|
|
|
header('Cache-Control: no-cache, must-revalidate'); |
58
|
|
|
|
59
|
|
|
loadClasses('DB'); |
60
|
|
|
$session = SessionManager::getSession(); |
61
|
|
|
|
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* genHash(). |
65
|
|
|
* |
66
|
|
|
* Generate a hash for user login |
67
|
|
|
* |
68
|
|
|
* @param string $password What password |
69
|
|
|
* @param string $cost What cost |
70
|
|
|
* |
71
|
|
|
* @return string|void |
72
|
|
|
*/ |
73
|
|
|
function bCrypt( |
74
|
|
|
string $password, |
75
|
|
|
string $cost |
76
|
|
|
): ?string |
77
|
|
|
{ |
78
|
|
|
$salt = sprintf('$2y$%02d$', $cost); |
79
|
|
|
if (function_exists('openssl_random_pseudo_bytes')) { |
80
|
|
|
$salt .= bin2hex(openssl_random_pseudo_bytes(11)); |
81
|
|
|
} else { |
82
|
|
|
$chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
83
|
|
|
for ($i = 0; $i < 22; ++$i) { |
84
|
|
|
$salt .= $chars[mt_rand(0, 63)]; |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
return crypt($password, $salt); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Checks if a string is hex encoded |
93
|
|
|
* |
94
|
|
|
* @param string $str |
95
|
|
|
* @return boolean |
96
|
|
|
*/ |
97
|
|
|
function isHex(string $str): bool |
98
|
|
|
{ |
99
|
|
|
if (str_starts_with(strtolower($str), '0x')) { |
100
|
|
|
$str = substr($str, 2); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
return ctype_xdigit($str); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Defuse cryption function. |
108
|
|
|
* |
109
|
|
|
* @param string $message what to de/crypt |
110
|
|
|
* @param string $ascii_key key to use |
111
|
|
|
* @param string $type operation to perform |
112
|
|
|
* @param array $SETTINGS Teampass settings |
113
|
|
|
* |
114
|
|
|
* @return array |
115
|
|
|
*/ |
116
|
|
|
function cryption(string $message, string $ascii_key, string $type, ?array $SETTINGS = []): array |
117
|
|
|
{ |
118
|
|
|
$ascii_key = empty($ascii_key) === true ? file_get_contents(SECUREPATH.'/'.SECUREFILE) : $ascii_key; |
119
|
|
|
$err = false; |
120
|
|
|
|
121
|
|
|
// convert KEY |
122
|
|
|
$key = Key::loadFromAsciiSafeString($ascii_key); |
123
|
|
|
try { |
124
|
|
|
if ($type === 'encrypt') { |
125
|
|
|
$text = Crypto::encrypt($message, $key); |
126
|
|
|
} elseif ($type === 'decrypt') { |
127
|
|
|
$text = Crypto::decrypt($message, $key); |
128
|
|
|
} |
129
|
|
|
} catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) { |
130
|
|
|
$err = 'an attack! either the wrong key was loaded, or the ciphertext has changed since it was created either corrupted in the database or intentionally modified by someone trying to carry out an attack.'; |
131
|
|
|
} catch (CryptoException\BadFormatException $ex) { |
132
|
|
|
$err = $ex; |
133
|
|
|
} catch (CryptoException\EnvironmentIsBrokenException $ex) { |
134
|
|
|
$err = $ex; |
135
|
|
|
} catch (CryptoException\CryptoException $ex) { |
136
|
|
|
$err = $ex; |
137
|
|
|
} catch (CryptoException\IOException $ex) { |
138
|
|
|
$err = $ex; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return [ |
142
|
|
|
'string' => $text ?? '', |
143
|
|
|
'error' => $err, |
144
|
|
|
]; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Generating a defuse key. |
149
|
|
|
* |
150
|
|
|
* @return string |
151
|
|
|
*/ |
152
|
|
|
function defuse_generate_key() |
153
|
|
|
{ |
154
|
|
|
$key = Key::createNewRandomKey(); |
155
|
|
|
$key = $key->saveToAsciiSafeString(); |
156
|
|
|
return $key; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Generate a Defuse personal key. |
161
|
|
|
* |
162
|
|
|
* @param string $psk psk used |
163
|
|
|
* |
164
|
|
|
* @return string |
165
|
|
|
*/ |
166
|
|
|
function defuse_generate_personal_key(string $psk): string |
167
|
|
|
{ |
168
|
|
|
$protected_key = KeyProtectedByPassword::createRandomPasswordProtectedKey($psk); |
169
|
|
|
return $protected_key->saveToAsciiSafeString(); // save this in user table |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Validate persoanl key with defuse. |
174
|
|
|
* |
175
|
|
|
* @param string $psk the user's psk |
176
|
|
|
* @param string $protected_key_encoded special key |
177
|
|
|
* |
178
|
|
|
* @return string |
179
|
|
|
*/ |
180
|
|
|
function defuse_validate_personal_key(string $psk, string $protected_key_encoded): string |
181
|
|
|
{ |
182
|
|
|
try { |
183
|
|
|
$protected_key_encoded = KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded); |
184
|
|
|
$user_key = $protected_key_encoded->unlockKey($psk); |
185
|
|
|
$user_key_encoded = $user_key->saveToAsciiSafeString(); |
186
|
|
|
} catch (CryptoException\EnvironmentIsBrokenException $ex) { |
187
|
|
|
return 'Error - Major issue as the encryption is broken.'; |
188
|
|
|
} catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) { |
189
|
|
|
return 'Error - The saltkey is not the correct one.'; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return $user_key_encoded; |
193
|
|
|
// store it in session once user has entered his psk |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Decrypt a defuse string if encrypted. |
198
|
|
|
* |
199
|
|
|
* @param string $value Encrypted string |
200
|
|
|
* |
201
|
|
|
* @return string Decrypted string |
202
|
|
|
*/ |
203
|
|
|
function defuseReturnDecrypted(string $value, $SETTINGS): string |
204
|
|
|
{ |
205
|
|
|
if (substr($value, 0, 3) === 'def') { |
206
|
|
|
$value = cryption($value, '', 'decrypt', $SETTINGS)['string']; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
return $value; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Trims a string depending on a specific string. |
214
|
|
|
* |
215
|
|
|
* @param string|array $chaine what to trim |
216
|
|
|
* @param string $element trim on what |
217
|
|
|
* |
218
|
|
|
* @return string |
219
|
|
|
*/ |
220
|
|
|
function trimElement($chaine, string $element): string |
221
|
|
|
{ |
222
|
|
|
if (! empty($chaine)) { |
223
|
|
|
if (is_array($chaine) === true) { |
224
|
|
|
$chaine = implode(';', $chaine); |
225
|
|
|
} |
226
|
|
|
$chaine = trim($chaine); |
227
|
|
|
if (substr($chaine, 0, 1) === $element) { |
228
|
|
|
$chaine = substr($chaine, 1); |
229
|
|
|
} |
230
|
|
|
if (substr($chaine, strlen($chaine) - 1, 1) === $element) { |
231
|
|
|
$chaine = substr($chaine, 0, strlen($chaine) - 1); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
return $chaine; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Permits to suppress all "special" characters from string. |
240
|
|
|
* |
241
|
|
|
* @param string $string what to clean |
242
|
|
|
* @param bool $special use of special chars? |
243
|
|
|
* |
244
|
|
|
* @return string |
245
|
|
|
*/ |
246
|
|
|
function cleanString(string $string, bool $special = false): string |
247
|
|
|
{ |
248
|
|
|
// Create temporary table for special characters escape |
249
|
|
|
$tabSpecialChar = []; |
250
|
|
|
for ($i = 0; $i <= 31; ++$i) { |
251
|
|
|
$tabSpecialChar[] = chr($i); |
252
|
|
|
} |
253
|
|
|
array_push($tabSpecialChar, '<br />'); |
254
|
|
|
if ((int) $special === 1) { |
255
|
|
|
$tabSpecialChar = array_merge($tabSpecialChar, ['</li>', '<ul>', '<ol>']); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
return str_replace($tabSpecialChar, "\n", $string); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Erro manager for DB. |
263
|
|
|
* |
264
|
|
|
* @param array $params output from query |
265
|
|
|
* |
266
|
|
|
* @return void |
267
|
|
|
*/ |
268
|
|
|
function db_error_handler(array $params): void |
269
|
|
|
{ |
270
|
|
|
echo 'Error: ' . $params['error'] . "<br>\n"; |
271
|
|
|
echo 'Query: ' . $params['query'] . "<br>\n"; |
272
|
|
|
throw new Exception('Error - Query', 1); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Identify user's rights |
277
|
|
|
* |
278
|
|
|
* @param string|array $groupesVisiblesUser [description] |
279
|
|
|
* @param string|array $groupesInterditsUser [description] |
280
|
|
|
* @param string $isAdmin [description] |
281
|
|
|
* @param string $idFonctions [description] |
282
|
|
|
* |
283
|
|
|
* @return bool |
284
|
|
|
*/ |
285
|
|
|
function identifyUserRights( |
286
|
|
|
$groupesVisiblesUser, |
287
|
|
|
$groupesInterditsUser, |
288
|
|
|
$isAdmin, |
289
|
|
|
$idFonctions, |
290
|
|
|
$SETTINGS |
291
|
|
|
) { |
292
|
|
|
$session = SessionManager::getSession(); |
293
|
|
|
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
294
|
|
|
|
295
|
|
|
// Check if user is ADMINISTRATOR |
296
|
|
|
(int) $isAdmin === 1 ? |
297
|
|
|
identAdmin( |
298
|
|
|
$idFonctions, |
299
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
300
|
|
|
$tree |
301
|
|
|
) |
302
|
|
|
: |
303
|
|
|
identUser( |
304
|
|
|
$groupesVisiblesUser, |
305
|
|
|
$groupesInterditsUser, |
306
|
|
|
$idFonctions, |
307
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
308
|
|
|
$tree |
309
|
|
|
); |
310
|
|
|
|
311
|
|
|
// update user's timestamp |
312
|
|
|
DB::update( |
313
|
|
|
prefixTable('users'), |
314
|
|
|
[ |
315
|
|
|
'timestamp' => time(), |
316
|
|
|
], |
317
|
|
|
'id=%i', |
318
|
|
|
$session->get('user-id') |
319
|
|
|
); |
320
|
|
|
|
321
|
|
|
return true; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Identify administrator. |
326
|
|
|
* |
327
|
|
|
* @param string $idFonctions Roles of user |
328
|
|
|
* @param array $SETTINGS Teampass settings |
329
|
|
|
* @param object $tree Tree of folders |
330
|
|
|
* |
331
|
|
|
* @return bool |
332
|
|
|
*/ |
333
|
|
|
function identAdmin($idFonctions, $SETTINGS, $tree) |
334
|
|
|
{ |
335
|
|
|
|
336
|
|
|
$session = SessionManager::getSession(); |
337
|
|
|
$groupesVisibles = []; |
338
|
|
|
$session->set('user-personal_folders', []); |
339
|
|
|
$session->set('user-accessible_folders', []); |
340
|
|
|
$session->set('user-no_access_folders', []); |
341
|
|
|
$session->set('user-personal_visible_folders', []); |
342
|
|
|
$session->set('user-read_only_folders', []); |
343
|
|
|
$session->set('system-list_restricted_folders_for_items', []); |
344
|
|
|
$session->set('system-list_folders_editable_by_role', []); |
345
|
|
|
$session->set('user-list_folders_limited', []); |
346
|
|
|
$session->set('user-forbiden_personal_folders', []); |
347
|
|
|
$globalsUserId = $session->get('user-id'); |
348
|
|
|
$globalsVisibleFolders = $session->get('user-accessible_folders'); |
349
|
|
|
$globalsPersonalVisibleFolders = $session->get('user-personal_visible_folders'); |
350
|
|
|
// Get list of Folders |
351
|
|
|
$rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0); |
352
|
|
|
foreach ($rows as $record) { |
353
|
|
|
array_push($groupesVisibles, $record['id']); |
354
|
|
|
} |
355
|
|
|
$session->set('user-accessible_folders', $groupesVisibles); |
356
|
|
|
$session->set('user-all_non_personal_folders', $groupesVisibles); |
357
|
|
|
// Exclude all PF |
358
|
|
|
$where = new WhereClause('and'); |
359
|
|
|
// create a WHERE statement of pieces joined by ANDs |
360
|
|
|
$where->add('personal_folder=%i', 1); |
361
|
|
|
if ( |
362
|
|
|
isset($SETTINGS['enable_pf_feature']) === true |
363
|
|
|
&& (int) $SETTINGS['enable_pf_feature'] === 1 |
364
|
|
|
) { |
365
|
|
|
$where->add('title=%s', $globalsUserId); |
366
|
|
|
$where->negateLast(); |
367
|
|
|
} |
368
|
|
|
// Get ID of personal folder |
369
|
|
|
$persfld = DB::queryfirstrow( |
370
|
|
|
'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s', |
371
|
|
|
$globalsUserId |
372
|
|
|
); |
373
|
|
|
if (empty($persfld['id']) === false) { |
374
|
|
|
if (in_array($persfld['id'], $globalsVisibleFolders) === false) { |
375
|
|
|
array_push($globalsVisibleFolders, $persfld['id']); |
376
|
|
|
array_push($globalsPersonalVisibleFolders, $persfld['id']); |
377
|
|
|
// get all descendants |
378
|
|
|
$tree->rebuild(); |
379
|
|
|
$tst = $tree->getDescendants($persfld['id']); |
380
|
|
|
foreach ($tst as $t) { |
381
|
|
|
array_push($globalsVisibleFolders, $t->id); |
382
|
|
|
array_push($globalsPersonalVisibleFolders, $t->id); |
383
|
|
|
} |
384
|
|
|
} |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
// get complete list of ROLES |
388
|
|
|
$tmp = explode(';', $idFonctions); |
389
|
|
|
$rows = DB::query( |
390
|
|
|
'SELECT * FROM ' . prefixTable('roles_title') . ' |
391
|
|
|
ORDER BY title ASC' |
392
|
|
|
); |
393
|
|
|
foreach ($rows as $record) { |
394
|
|
|
if (! empty($record['id']) && ! in_array($record['id'], $tmp)) { |
395
|
|
|
array_push($tmp, $record['id']); |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
$session->set('user-roles', implode(';', $tmp)); |
399
|
|
|
$session->set('user-admin', 1); |
400
|
|
|
// Check if admin has created Folders and Roles |
401
|
|
|
DB::query('SELECT * FROM ' . prefixTable('nested_tree') . ''); |
402
|
|
|
$session->set('user-nb_folders', DB::count()); |
403
|
|
|
DB::query('SELECT * FROM ' . prefixTable('roles_title')); |
404
|
|
|
$session->set('user-nb_roles', DB::count()); |
405
|
|
|
|
406
|
|
|
return true; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Permits to convert an element to array. |
411
|
|
|
* |
412
|
|
|
* @param string|array $element Any value to be returned as array |
413
|
|
|
* |
414
|
|
|
* @return array |
415
|
|
|
*/ |
416
|
|
|
function convertToArray($element): array |
417
|
|
|
{ |
418
|
|
|
if (is_string($element) === true) { |
419
|
|
|
if (empty($element) === true) { |
420
|
|
|
return []; |
421
|
|
|
} |
422
|
|
|
return explode( |
423
|
|
|
';', |
424
|
|
|
trimElement($element, ';') |
425
|
|
|
); |
426
|
|
|
} |
427
|
|
|
return $element; |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
/** |
431
|
|
|
* Defines the rights the user has. |
432
|
|
|
* |
433
|
|
|
* @param string|array $allowedFolders Allowed folders |
434
|
|
|
* @param string|array $noAccessFolders Not allowed folders |
435
|
|
|
* @param string|array $userRoles Roles of user |
436
|
|
|
* @param array $SETTINGS Teampass settings |
437
|
|
|
* @param object $tree Tree of folders |
438
|
|
|
* |
439
|
|
|
* @return bool |
440
|
|
|
*/ |
441
|
|
|
function identUser( |
442
|
|
|
$allowedFolders, |
443
|
|
|
$noAccessFolders, |
444
|
|
|
$userRoles, |
445
|
|
|
array $SETTINGS, |
446
|
|
|
object $tree |
447
|
|
|
) { |
448
|
|
|
|
449
|
|
|
$session = SessionManager::getSession(); |
450
|
|
|
// Init |
451
|
|
|
$session->set('user-accessible_folders', []); |
452
|
|
|
$session->set('user-personal_folders', []); |
453
|
|
|
$session->set('user-no_access_folders', []); |
454
|
|
|
$session->set('user-personal_visible_folders', []); |
455
|
|
|
$session->set('user-read_only_folders', []); |
456
|
|
|
$session->set('user-user-roles', $userRoles); |
457
|
|
|
$session->set('user-admin', 0); |
458
|
|
|
// init |
459
|
|
|
$personalFolders = []; |
460
|
|
|
$readOnlyFolders = []; |
461
|
|
|
$noAccessPersonalFolders = []; |
462
|
|
|
$restrictedFoldersForItems = []; |
463
|
|
|
$foldersLimited = []; |
464
|
|
|
$foldersLimitedFull = []; |
465
|
|
|
$allowedFoldersByRoles = []; |
466
|
|
|
$globalsUserId = $session->get('user-id'); |
467
|
|
|
$globalsPersonalFolders = $session->get('user-personal_folder_enabled'); |
468
|
|
|
// Ensure consistency in array format |
469
|
|
|
$noAccessFolders = convertToArray($noAccessFolders); |
470
|
|
|
$userRoles = convertToArray($userRoles); |
471
|
|
|
$allowedFolders = convertToArray($allowedFolders); |
472
|
|
|
|
473
|
|
|
// Get list of folders depending on Roles |
474
|
|
|
$arrays = identUserGetFoldersFromRoles( |
475
|
|
|
$userRoles, |
476
|
|
|
$allowedFoldersByRoles, |
477
|
|
|
$readOnlyFolders, |
478
|
|
|
$allowedFolders |
479
|
|
|
); |
480
|
|
|
$allowedFoldersByRoles = $arrays['allowedFoldersByRoles']; |
481
|
|
|
$readOnlyFolders = $arrays['readOnlyFolders']; |
482
|
|
|
|
483
|
|
|
// Does this user is allowed to see other items |
484
|
|
|
$inc = 0; |
485
|
|
|
$rows = DB::query( |
486
|
|
|
'SELECT id, id_tree FROM ' . prefixTable('items') . ' |
487
|
|
|
WHERE restricted_to LIKE %ss AND inactif = %s'. |
488
|
|
|
(count($allowedFolders) > 0 ? ' AND id_tree NOT IN ('.implode(',', $allowedFolders).')' : ''), |
489
|
|
|
$globalsUserId, |
490
|
|
|
'0' |
491
|
|
|
); |
492
|
|
|
foreach ($rows as $record) { |
493
|
|
|
// Exclude restriction on item if folder is fully accessible |
494
|
|
|
//if (in_array($record['id_tree'], $allowedFolders) === false) { |
495
|
|
|
$restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id']; |
496
|
|
|
++$inc; |
497
|
|
|
//} |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
// Check for the users roles if some specific rights exist on items |
501
|
|
|
$rows = DB::query( |
502
|
|
|
'SELECT i.id_tree, r.item_id |
503
|
|
|
FROM ' . prefixTable('items') . ' as i |
504
|
|
|
INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id) |
505
|
|
|
WHERE i.id_tree <> "" '. |
506
|
|
|
(count($userRoles) > 0 ? 'AND r.role_id IN %li ' : ''). |
507
|
|
|
'ORDER BY i.id_tree ASC', |
508
|
|
|
$userRoles |
509
|
|
|
); |
510
|
|
|
$inc = 0; |
511
|
|
|
foreach ($rows as $record) { |
512
|
|
|
//if (isset($record['id_tree'])) { |
513
|
|
|
$foldersLimited[$record['id_tree']][$inc] = $record['item_id']; |
514
|
|
|
array_push($foldersLimitedFull, $record['id_tree']); |
515
|
|
|
++$inc; |
516
|
|
|
//} |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
// Get list of Personal Folders |
520
|
|
|
$arrays = identUserGetPFList( |
521
|
|
|
$globalsPersonalFolders, |
522
|
|
|
$allowedFolders, |
523
|
|
|
$globalsUserId, |
524
|
|
|
$personalFolders, |
525
|
|
|
$noAccessPersonalFolders, |
526
|
|
|
$foldersLimitedFull, |
527
|
|
|
$allowedFoldersByRoles, |
528
|
|
|
array_keys($restrictedFoldersForItems), |
529
|
|
|
$readOnlyFolders, |
530
|
|
|
$noAccessFolders, |
531
|
|
|
isset($SETTINGS['enable_pf_feature']) === true ? $SETTINGS['enable_pf_feature'] : 0, |
532
|
|
|
$tree |
533
|
|
|
); |
534
|
|
|
$allowedFolders = $arrays['allowedFolders']; |
535
|
|
|
$personalFolders = $arrays['personalFolders']; |
536
|
|
|
$noAccessPersonalFolders = $arrays['noAccessPersonalFolders']; |
537
|
|
|
|
538
|
|
|
// Return data |
539
|
|
|
$session->set('user-all_non_personal_folders', $allowedFolders); |
540
|
|
|
$session->set('user-accessible_folders', array_unique(array_merge($allowedFolders, $personalFolders), SORT_NUMERIC)); |
541
|
|
|
$session->set('user-read_only_folders', $readOnlyFolders); |
542
|
|
|
$session->set('user-no_access_folders', $noAccessFolders); |
543
|
|
|
$session->set('user-personal_folders', $personalFolders); |
544
|
|
|
$session->set('user-list_folders_limited', $foldersLimited); |
545
|
|
|
$session->set('system-list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION'); |
546
|
|
|
$session->set('system-list_restricted_folders_for_items', $restrictedFoldersForItems); |
547
|
|
|
$session->set('user-forbiden_personal_folders', $noAccessPersonalFolders); |
548
|
|
|
$session->set( |
549
|
|
|
'all_folders_including_no_access', |
550
|
|
|
array_unique(array_merge( |
551
|
|
|
$allowedFolders, |
552
|
|
|
$personalFolders, |
553
|
|
|
$noAccessFolders, |
554
|
|
|
$readOnlyFolders |
555
|
|
|
), SORT_NUMERIC) |
556
|
|
|
); |
557
|
|
|
// Folders and Roles numbers |
558
|
|
|
DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . ''); |
559
|
|
|
$session->set('user-nb_folders', DB::count()); |
560
|
|
|
DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title')); |
561
|
|
|
$session->set('user-nb_roles', DB::count()); |
562
|
|
|
// check if change proposals on User's items |
563
|
|
|
if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) { |
564
|
|
|
$countNewItems = DB::query( |
565
|
|
|
'SELECT COUNT(*) |
566
|
|
|
FROM ' . prefixTable('items_change') . ' AS c |
567
|
|
|
LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item) |
568
|
|
|
WHERE i.action = %s AND i.id_user = %i', |
569
|
|
|
'at_creation', |
570
|
|
|
$globalsUserId |
571
|
|
|
); |
572
|
|
|
$session->set('user-nb_item_change_proposals', $countNewItems); |
573
|
|
|
} else { |
574
|
|
|
$session->set('user-nb_item_change_proposals', 0); |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
return true; |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Get list of folders depending on Roles |
582
|
|
|
* |
583
|
|
|
* @param array $userRoles |
584
|
|
|
* @param array $allowedFoldersByRoles |
585
|
|
|
* @param array $readOnlyFolders |
586
|
|
|
* @param array $allowedFolders |
587
|
|
|
* |
588
|
|
|
* @return array |
589
|
|
|
*/ |
590
|
|
|
function identUserGetFoldersFromRoles($userRoles, $allowedFoldersByRoles, $readOnlyFolders, $allowedFolders) : array |
591
|
|
|
{ |
592
|
|
|
$rows = DB::query( |
593
|
|
|
'SELECT * |
594
|
|
|
FROM ' . prefixTable('roles_values') . ' |
595
|
|
|
WHERE type IN %ls'.(count($userRoles) > 0 ? ' AND role_id IN %li' : ''), |
596
|
|
|
['W', 'ND', 'NE', 'NDNE', 'R'], |
597
|
|
|
$userRoles, |
598
|
|
|
); |
599
|
|
|
foreach ($rows as $record) { |
600
|
|
|
if ($record['type'] === 'R') { |
601
|
|
|
array_push($readOnlyFolders, $record['folder_id']); |
602
|
|
|
} elseif (in_array($record['folder_id'], $allowedFolders) === false) { |
603
|
|
|
array_push($allowedFoldersByRoles, $record['folder_id']); |
604
|
|
|
} |
605
|
|
|
} |
606
|
|
|
$allowedFoldersByRoles = array_unique($allowedFoldersByRoles); |
607
|
|
|
$readOnlyFolders = array_unique($readOnlyFolders); |
608
|
|
|
|
609
|
|
|
// Clean arrays |
610
|
|
|
foreach ($allowedFoldersByRoles as $value) { |
611
|
|
|
$key = array_search($value, $readOnlyFolders); |
612
|
|
|
if ($key !== false) { |
613
|
|
|
unset($readOnlyFolders[$key]); |
614
|
|
|
} |
615
|
|
|
} |
616
|
|
|
return [ |
617
|
|
|
'readOnlyFolders' => $readOnlyFolders, |
618
|
|
|
'allowedFoldersByRoles' => $allowedFoldersByRoles |
619
|
|
|
]; |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
/** |
623
|
|
|
* Get list of Personal Folders |
624
|
|
|
* |
625
|
|
|
* @param int $globalsPersonalFolders |
626
|
|
|
* @param array $allowedFolders |
627
|
|
|
* @param int $globalsUserId |
628
|
|
|
* @param array $personalFolders |
629
|
|
|
* @param array $noAccessPersonalFolders |
630
|
|
|
* @param array $foldersLimitedFull |
631
|
|
|
* @param array $allowedFoldersByRoles |
632
|
|
|
* @param array $restrictedFoldersForItems |
633
|
|
|
* @param array $readOnlyFolders |
634
|
|
|
* @param array $noAccessFolders |
635
|
|
|
* @param int $enablePfFeature |
636
|
|
|
* @param object $tree |
637
|
|
|
* |
638
|
|
|
* @return array |
639
|
|
|
*/ |
640
|
|
|
function identUserGetPFList( |
641
|
|
|
$globalsPersonalFolders, |
642
|
|
|
$allowedFolders, |
643
|
|
|
$globalsUserId, |
644
|
|
|
$personalFolders, |
645
|
|
|
$noAccessPersonalFolders, |
646
|
|
|
$foldersLimitedFull, |
647
|
|
|
$allowedFoldersByRoles, |
648
|
|
|
$restrictedFoldersForItems, |
649
|
|
|
$readOnlyFolders, |
650
|
|
|
$noAccessFolders, |
651
|
|
|
$enablePfFeature, |
652
|
|
|
$tree |
653
|
|
|
) |
654
|
|
|
{ |
655
|
|
|
if ( |
656
|
|
|
(int) $enablePfFeature === 1 |
657
|
|
|
&& (int) $globalsPersonalFolders === 1 |
658
|
|
|
) { |
659
|
|
|
$persoFld = DB::queryfirstrow( |
660
|
|
|
'SELECT id |
661
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
662
|
|
|
WHERE title = %s AND personal_folder = %i'. |
663
|
|
|
(count($allowedFolders) > 0 ? ' AND id NOT IN ('.implode(',', $allowedFolders).')' : ''), |
664
|
|
|
$globalsUserId, |
665
|
|
|
1 |
666
|
|
|
); |
667
|
|
|
if (empty($persoFld['id']) === false) { |
668
|
|
|
array_push($personalFolders, $persoFld['id']); |
669
|
|
|
array_push($allowedFolders, $persoFld['id']); |
670
|
|
|
// get all descendants |
671
|
|
|
$ids = $tree->getDescendants($persoFld['id'], false, false, true); |
672
|
|
|
foreach ($ids as $id) { |
673
|
|
|
//array_push($allowedFolders, $id); |
674
|
|
|
array_push($personalFolders, $id); |
675
|
|
|
} |
676
|
|
|
} |
677
|
|
|
} |
678
|
|
|
|
679
|
|
|
// Exclude all other PF |
680
|
|
|
$where = new WhereClause('and'); |
681
|
|
|
$where->add('personal_folder=%i', 1); |
682
|
|
|
if (count($personalFolders) > 0) { |
683
|
|
|
$where->add('id NOT IN ('.implode(',', $personalFolders).')'); |
684
|
|
|
} |
685
|
|
|
if ( |
686
|
|
|
(int) $enablePfFeature === 1 |
687
|
|
|
&& (int) $globalsPersonalFolders === 1 |
688
|
|
|
) { |
689
|
|
|
$where->add('title=%s', $globalsUserId); |
690
|
|
|
$where->negateLast(); |
691
|
|
|
} |
692
|
|
|
$persoFlds = DB::query( |
693
|
|
|
'SELECT id |
694
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
695
|
|
|
WHERE %l', |
696
|
|
|
$where |
697
|
|
|
); |
698
|
|
|
foreach ($persoFlds as $persoFldId) { |
699
|
|
|
array_push($noAccessPersonalFolders, $persoFldId['id']); |
700
|
|
|
} |
701
|
|
|
|
702
|
|
|
// All folders visibles |
703
|
|
|
$allowedFolders = array_unique(array_merge( |
704
|
|
|
$allowedFolders, |
705
|
|
|
$foldersLimitedFull, |
706
|
|
|
$allowedFoldersByRoles, |
707
|
|
|
$restrictedFoldersForItems, |
708
|
|
|
$readOnlyFolders |
709
|
|
|
), SORT_NUMERIC); |
710
|
|
|
// Exclude from allowed folders all the specific user forbidden folders |
711
|
|
|
if (count($noAccessFolders) > 0) { |
712
|
|
|
$allowedFolders = array_diff($allowedFolders, $noAccessFolders); |
713
|
|
|
} |
714
|
|
|
|
715
|
|
|
return [ |
716
|
|
|
'allowedFolders' => array_diff(array_diff($allowedFolders, $noAccessPersonalFolders), $personalFolders), |
717
|
|
|
'personalFolders' => $personalFolders, |
718
|
|
|
'noAccessPersonalFolders' => $noAccessPersonalFolders |
719
|
|
|
]; |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
|
723
|
|
|
/** |
724
|
|
|
* Update the CACHE table. |
725
|
|
|
* |
726
|
|
|
* @param string $action What to do |
727
|
|
|
* @param array $SETTINGS Teampass settings |
728
|
|
|
* @param int $ident Ident format |
729
|
|
|
* |
730
|
|
|
* @return void |
731
|
|
|
*/ |
732
|
|
|
function updateCacheTable(string $action, ?int $ident = null): void |
733
|
|
|
{ |
734
|
|
|
if ($action === 'reload') { |
735
|
|
|
// Rebuild full cache table |
736
|
|
|
cacheTableRefresh(); |
737
|
|
|
} elseif ($action === 'update_value' && is_null($ident) === false) { |
738
|
|
|
// UPDATE an item |
739
|
|
|
cacheTableUpdate($ident); |
740
|
|
|
} elseif ($action === 'add_value' && is_null($ident) === false) { |
741
|
|
|
// ADD an item |
742
|
|
|
cacheTableAdd($ident); |
743
|
|
|
} elseif ($action === 'delete_value' && is_null($ident) === false) { |
744
|
|
|
// DELETE an item |
745
|
|
|
DB::delete(prefixTable('cache'), 'id = %i', $ident); |
746
|
|
|
} |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
/** |
750
|
|
|
* Cache table - refresh. |
751
|
|
|
* |
752
|
|
|
* @return void |
753
|
|
|
*/ |
754
|
|
|
function cacheTableRefresh(): void |
755
|
|
|
{ |
756
|
|
|
// Load class DB |
757
|
|
|
loadClasses('DB'); |
758
|
|
|
|
759
|
|
|
//Load Tree |
760
|
|
|
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
761
|
|
|
// truncate table |
762
|
|
|
DB::query('TRUNCATE TABLE ' . prefixTable('cache')); |
763
|
|
|
// reload date |
764
|
|
|
$rows = DB::query( |
765
|
|
|
'SELECT * |
766
|
|
|
FROM ' . prefixTable('items') . ' as i |
767
|
|
|
INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id) |
768
|
|
|
AND l.action = %s |
769
|
|
|
AND i.inactif = %i', |
770
|
|
|
'at_creation', |
771
|
|
|
0 |
772
|
|
|
); |
773
|
|
|
foreach ($rows as $record) { |
774
|
|
|
if (empty($record['id_tree']) === false) { |
775
|
|
|
// Get all TAGS |
776
|
|
|
$tags = ''; |
777
|
|
|
$itemTags = DB::query( |
778
|
|
|
'SELECT tag |
779
|
|
|
FROM ' . prefixTable('tags') . ' |
780
|
|
|
WHERE item_id = %i AND tag != ""', |
781
|
|
|
$record['id'] |
782
|
|
|
); |
783
|
|
|
foreach ($itemTags as $itemTag) { |
784
|
|
|
$tags .= $itemTag['tag'] . ' '; |
785
|
|
|
} |
786
|
|
|
|
787
|
|
|
// Get renewal period |
788
|
|
|
$resNT = DB::queryfirstrow( |
789
|
|
|
'SELECT renewal_period |
790
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
791
|
|
|
WHERE id = %i', |
792
|
|
|
$record['id_tree'] |
793
|
|
|
); |
794
|
|
|
// form id_tree to full foldername |
795
|
|
|
$folder = []; |
796
|
|
|
$arbo = $tree->getPath($record['id_tree'], true); |
797
|
|
|
foreach ($arbo as $elem) { |
798
|
|
|
// Check if title is the ID of a user |
799
|
|
|
if (is_numeric($elem->title) === true) { |
800
|
|
|
// Is this a User id? |
801
|
|
|
$user = DB::queryfirstrow( |
802
|
|
|
'SELECT id, login |
803
|
|
|
FROM ' . prefixTable('users') . ' |
804
|
|
|
WHERE id = %i', |
805
|
|
|
$elem->title |
806
|
|
|
); |
807
|
|
|
if (count($user) > 0) { |
808
|
|
|
$elem->title = $user['login']; |
809
|
|
|
} |
810
|
|
|
} |
811
|
|
|
// Build path |
812
|
|
|
array_push($folder, stripslashes($elem->title)); |
813
|
|
|
} |
814
|
|
|
// store data |
815
|
|
|
DB::insert( |
816
|
|
|
prefixTable('cache'), |
817
|
|
|
[ |
818
|
|
|
'id' => $record['id'], |
819
|
|
|
'label' => $record['label'], |
820
|
|
|
'description' => $record['description'] ?? '', |
821
|
|
|
'url' => isset($record['url']) && ! empty($record['url']) ? $record['url'] : '0', |
822
|
|
|
'tags' => $tags, |
823
|
|
|
'id_tree' => $record['id_tree'], |
824
|
|
|
'perso' => $record['perso'], |
825
|
|
|
'restricted_to' => isset($record['restricted_to']) && ! empty($record['restricted_to']) ? $record['restricted_to'] : '0', |
826
|
|
|
'login' => $record['login'] ?? '', |
827
|
|
|
'folder' => implode(' > ', $folder), |
828
|
|
|
'author' => $record['id_user'], |
829
|
|
|
'renewal_period' => $resNT['renewal_period'] ?? '0', |
830
|
|
|
'timestamp' => $record['date'], |
831
|
|
|
] |
832
|
|
|
); |
833
|
|
|
} |
834
|
|
|
} |
835
|
|
|
} |
836
|
|
|
|
837
|
|
|
/** |
838
|
|
|
* Cache table - update existing value. |
839
|
|
|
* |
840
|
|
|
* @param int $ident Ident format |
841
|
|
|
* |
842
|
|
|
* @return void |
843
|
|
|
*/ |
844
|
|
|
function cacheTableUpdate(?int $ident = null): void |
845
|
|
|
{ |
846
|
|
|
$session = SessionManager::getSession(); |
847
|
|
|
loadClasses('DB'); |
848
|
|
|
|
849
|
|
|
//Load Tree |
850
|
|
|
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
851
|
|
|
// get new value from db |
852
|
|
|
$data = DB::queryfirstrow( |
853
|
|
|
'SELECT label, description, id_tree, perso, restricted_to, login, url |
854
|
|
|
FROM ' . prefixTable('items') . ' |
855
|
|
|
WHERE id=%i', |
856
|
|
|
$ident |
857
|
|
|
); |
858
|
|
|
// Get all TAGS |
859
|
|
|
$tags = ''; |
860
|
|
|
$itemTags = DB::query( |
861
|
|
|
'SELECT tag |
862
|
|
|
FROM ' . prefixTable('tags') . ' |
863
|
|
|
WHERE item_id = %i AND tag != ""', |
864
|
|
|
$ident |
865
|
|
|
); |
866
|
|
|
foreach ($itemTags as $itemTag) { |
867
|
|
|
$tags .= $itemTag['tag'] . ' '; |
868
|
|
|
} |
869
|
|
|
// form id_tree to full foldername |
870
|
|
|
$folder = []; |
871
|
|
|
$arbo = $tree->getPath($data['id_tree'], true); |
872
|
|
|
foreach ($arbo as $elem) { |
873
|
|
|
// Check if title is the ID of a user |
874
|
|
|
if (is_numeric($elem->title) === true) { |
875
|
|
|
// Is this a User id? |
876
|
|
|
$user = DB::queryfirstrow( |
877
|
|
|
'SELECT id, login |
878
|
|
|
FROM ' . prefixTable('users') . ' |
879
|
|
|
WHERE id = %i', |
880
|
|
|
$elem->title |
881
|
|
|
); |
882
|
|
|
if (count($user) > 0) { |
883
|
|
|
$elem->title = $user['login']; |
884
|
|
|
} |
885
|
|
|
} |
886
|
|
|
// Build path |
887
|
|
|
array_push($folder, stripslashes($elem->title)); |
888
|
|
|
} |
889
|
|
|
// finaly update |
890
|
|
|
DB::update( |
891
|
|
|
prefixTable('cache'), |
892
|
|
|
[ |
893
|
|
|
'label' => $data['label'], |
894
|
|
|
'description' => $data['description'], |
895
|
|
|
'tags' => $tags, |
896
|
|
|
'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0', |
897
|
|
|
'id_tree' => $data['id_tree'], |
898
|
|
|
'perso' => $data['perso'], |
899
|
|
|
'restricted_to' => isset($data['restricted_to']) && ! empty($data['restricted_to']) ? $data['restricted_to'] : '0', |
900
|
|
|
'login' => $data['login'] ?? '', |
901
|
|
|
'folder' => implode(' » ', $folder), |
902
|
|
|
'author' => $session->get('user-id'), |
903
|
|
|
], |
904
|
|
|
'id = %i', |
905
|
|
|
$ident |
906
|
|
|
); |
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
/** |
910
|
|
|
* Cache table - add new value. |
911
|
|
|
* |
912
|
|
|
* @param int $ident Ident format |
913
|
|
|
* |
914
|
|
|
* @return void |
915
|
|
|
*/ |
916
|
|
|
function cacheTableAdd(?int $ident = null): void |
917
|
|
|
{ |
918
|
|
|
$session = SessionManager::getSession(); |
919
|
|
|
$globalsUserId = $session->get('user-id'); |
920
|
|
|
|
921
|
|
|
// Load class DB |
922
|
|
|
loadClasses('DB'); |
923
|
|
|
|
924
|
|
|
//Load Tree |
925
|
|
|
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
926
|
|
|
// get new value from db |
927
|
|
|
$data = DB::queryFirstRow( |
928
|
|
|
'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date |
929
|
|
|
FROM ' . prefixTable('items') . ' as i |
930
|
|
|
INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id) |
931
|
|
|
WHERE i.id = %i |
932
|
|
|
AND l.action = %s', |
933
|
|
|
$ident, |
934
|
|
|
'at_creation' |
935
|
|
|
); |
936
|
|
|
// Get all TAGS |
937
|
|
|
$tags = ''; |
938
|
|
|
$itemTags = DB::query( |
939
|
|
|
'SELECT tag |
940
|
|
|
FROM ' . prefixTable('tags') . ' |
941
|
|
|
WHERE item_id = %i AND tag != ""', |
942
|
|
|
$ident |
943
|
|
|
); |
944
|
|
|
foreach ($itemTags as $itemTag) { |
945
|
|
|
$tags .= $itemTag['tag'] . ' '; |
946
|
|
|
} |
947
|
|
|
// form id_tree to full foldername |
948
|
|
|
$folder = []; |
949
|
|
|
$arbo = $tree->getPath($data['id_tree'], true); |
950
|
|
|
foreach ($arbo as $elem) { |
951
|
|
|
// Check if title is the ID of a user |
952
|
|
|
if (is_numeric($elem->title) === true) { |
953
|
|
|
// Is this a User id? |
954
|
|
|
$user = DB::queryfirstrow( |
955
|
|
|
'SELECT id, login |
956
|
|
|
FROM ' . prefixTable('users') . ' |
957
|
|
|
WHERE id = %i', |
958
|
|
|
$elem->title |
959
|
|
|
); |
960
|
|
|
if (count($user) > 0) { |
961
|
|
|
$elem->title = $user['login']; |
962
|
|
|
} |
963
|
|
|
} |
964
|
|
|
// Build path |
965
|
|
|
array_push($folder, stripslashes($elem->title)); |
966
|
|
|
} |
967
|
|
|
// finaly update |
968
|
|
|
DB::insert( |
969
|
|
|
prefixTable('cache'), |
970
|
|
|
[ |
971
|
|
|
'id' => $data['id'], |
972
|
|
|
'label' => $data['label'], |
973
|
|
|
'description' => $data['description'], |
974
|
|
|
'tags' => isset($tags) && empty($tags) === false ? $tags : 'None', |
975
|
|
|
'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0', |
976
|
|
|
'id_tree' => $data['id_tree'], |
977
|
|
|
'perso' => isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None' ? $data['perso'] : '0', |
978
|
|
|
'restricted_to' => isset($data['restricted_to']) && empty($data['restricted_to']) === false ? $data['restricted_to'] : '0', |
979
|
|
|
'login' => $data['login'] ?? '', |
980
|
|
|
'folder' => implode(' » ', $folder), |
981
|
|
|
'author' => $globalsUserId, |
982
|
|
|
'timestamp' => $data['date'], |
983
|
|
|
] |
984
|
|
|
); |
985
|
|
|
} |
986
|
|
|
|
987
|
|
|
/** |
988
|
|
|
* Do statistics. |
989
|
|
|
* |
990
|
|
|
* @param array $SETTINGS Teampass settings |
991
|
|
|
* |
992
|
|
|
* @return array |
993
|
|
|
*/ |
994
|
|
|
function getStatisticsData(array $SETTINGS): array |
995
|
|
|
{ |
996
|
|
|
DB::query( |
997
|
|
|
'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', |
998
|
|
|
0 |
999
|
|
|
); |
1000
|
|
|
$counter_folders = DB::count(); |
1001
|
|
|
DB::query( |
1002
|
|
|
'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', |
1003
|
|
|
1 |
1004
|
|
|
); |
1005
|
|
|
$counter_folders_perso = DB::count(); |
1006
|
|
|
DB::query( |
1007
|
|
|
'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i', |
1008
|
|
|
0 |
1009
|
|
|
); |
1010
|
|
|
$counter_items = DB::count(); |
1011
|
|
|
DB::query( |
1012
|
|
|
'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i', |
1013
|
|
|
1 |
1014
|
|
|
); |
1015
|
|
|
$counter_items_perso = DB::count(); |
1016
|
|
|
DB::query( |
1017
|
|
|
'SELECT id FROM ' . prefixTable('users') . '' |
1018
|
|
|
); |
1019
|
|
|
$counter_users = DB::count(); |
1020
|
|
|
DB::query( |
1021
|
|
|
'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i', |
1022
|
|
|
1 |
1023
|
|
|
); |
1024
|
|
|
$admins = DB::count(); |
1025
|
|
|
DB::query( |
1026
|
|
|
'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i', |
1027
|
|
|
1 |
1028
|
|
|
); |
1029
|
|
|
$managers = DB::count(); |
1030
|
|
|
DB::query( |
1031
|
|
|
'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i', |
1032
|
|
|
1 |
1033
|
|
|
); |
1034
|
|
|
$readOnly = DB::count(); |
1035
|
|
|
// list the languages |
1036
|
|
|
$usedLang = []; |
1037
|
|
|
$tp_languages = DB::query( |
1038
|
|
|
'SELECT name FROM ' . prefixTable('languages') |
1039
|
|
|
); |
1040
|
|
|
foreach ($tp_languages as $tp_language) { |
1041
|
|
|
DB::query( |
1042
|
|
|
'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s', |
1043
|
|
|
$tp_language['name'] |
1044
|
|
|
); |
1045
|
|
|
$usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0); |
1046
|
|
|
} |
1047
|
|
|
|
1048
|
|
|
// get list of ips |
1049
|
|
|
$usedIp = []; |
1050
|
|
|
$tp_ips = DB::query( |
1051
|
|
|
'SELECT user_ip FROM ' . prefixTable('users') |
1052
|
|
|
); |
1053
|
|
|
foreach ($tp_ips as $ip) { |
1054
|
|
|
if (array_key_exists($ip['user_ip'], $usedIp)) { |
1055
|
|
|
$usedIp[$ip['user_ip']] += $usedIp[$ip['user_ip']]; |
1056
|
|
|
} elseif (! empty($ip['user_ip']) && $ip['user_ip'] !== 'none') { |
1057
|
|
|
$usedIp[$ip['user_ip']] = 1; |
1058
|
|
|
} |
1059
|
|
|
} |
1060
|
|
|
|
1061
|
|
|
return [ |
1062
|
|
|
'error' => '', |
1063
|
|
|
'stat_phpversion' => phpversion(), |
1064
|
|
|
'stat_folders' => $counter_folders, |
1065
|
|
|
'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso), |
1066
|
|
|
'stat_items' => $counter_items, |
1067
|
|
|
'stat_items_shared' => intval($counter_items) - intval($counter_items_perso), |
1068
|
|
|
'stat_users' => $counter_users, |
1069
|
|
|
'stat_admins' => $admins, |
1070
|
|
|
'stat_managers' => $managers, |
1071
|
|
|
'stat_ro' => $readOnly, |
1072
|
|
|
'stat_kb' => $SETTINGS['enable_kb'], |
1073
|
|
|
'stat_pf' => $SETTINGS['enable_pf_feature'], |
1074
|
|
|
'stat_fav' => $SETTINGS['enable_favourites'], |
1075
|
|
|
'stat_teampassversion' => TP_VERSION, |
1076
|
|
|
'stat_ldap' => $SETTINGS['ldap_mode'], |
1077
|
|
|
'stat_agses' => $SETTINGS['agses_authentication_enabled'], |
1078
|
|
|
'stat_duo' => $SETTINGS['duo'], |
1079
|
|
|
'stat_suggestion' => $SETTINGS['enable_suggestion'], |
1080
|
|
|
'stat_api' => $SETTINGS['api'], |
1081
|
|
|
'stat_customfields' => $SETTINGS['item_extra_fields'], |
1082
|
|
|
'stat_syslog' => $SETTINGS['syslog_enable'], |
1083
|
|
|
'stat_2fa' => $SETTINGS['google_authentication'], |
1084
|
|
|
'stat_stricthttps' => $SETTINGS['enable_sts'], |
1085
|
|
|
'stat_mysqlversion' => DB::serverVersion(), |
1086
|
|
|
'stat_languages' => $usedLang, |
1087
|
|
|
'stat_country' => $usedIp, |
1088
|
|
|
]; |
1089
|
|
|
} |
1090
|
|
|
|
1091
|
|
|
/** |
1092
|
|
|
* Permits to prepare the way to send the email |
1093
|
|
|
* |
1094
|
|
|
* @param string $subject email subject |
1095
|
|
|
* @param string $body email message |
1096
|
|
|
* @param string $email email |
1097
|
|
|
* @param string $receiverName Receiver name |
1098
|
|
|
* @param array $SETTINGS settings |
1099
|
|
|
* |
1100
|
|
|
* @return void |
1101
|
|
|
*/ |
1102
|
|
|
function prepareSendingEmail( |
1103
|
|
|
$subject, |
1104
|
|
|
$body, |
1105
|
|
|
$email, |
1106
|
|
|
$receiverName = '' |
1107
|
|
|
): void |
1108
|
|
|
{ |
1109
|
|
|
DB::insert( |
1110
|
|
|
prefixTable('processes'), |
1111
|
|
|
array( |
1112
|
|
|
'created_at' => time(), |
1113
|
|
|
'process_type' => 'send_email', |
1114
|
|
|
'arguments' => json_encode([ |
1115
|
|
|
'subject' => $subject, |
1116
|
|
|
'receivers' => $email, |
1117
|
|
|
'body' => $body, |
1118
|
|
|
'receiver_name' => $receiverName, |
1119
|
|
|
], JSON_HEX_QUOT | JSON_HEX_TAG), |
1120
|
|
|
'updated_at' => '', |
1121
|
|
|
'finished_at' => '', |
1122
|
|
|
'output' => '', |
1123
|
|
|
) |
1124
|
|
|
); |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
/** |
1128
|
|
|
* Permits to send an email. |
1129
|
|
|
* |
1130
|
|
|
* @param string $subject email subject |
1131
|
|
|
* @param string $textMail email message |
1132
|
|
|
* @param string $email email |
1133
|
|
|
* @param array $SETTINGS settings |
1134
|
|
|
* @param string $textMailAlt email message alt |
1135
|
|
|
* @param bool $silent no errors |
1136
|
|
|
* |
1137
|
|
|
* @return string some json info |
1138
|
|
|
*/ |
1139
|
|
|
function sendEmail( |
1140
|
|
|
$subject, |
1141
|
|
|
$textMail, |
1142
|
|
|
$email, |
1143
|
|
|
$SETTINGS, |
1144
|
|
|
$textMailAlt = null, |
1145
|
|
|
$silent = true, |
1146
|
|
|
$cron = false |
1147
|
|
|
) { |
1148
|
|
|
$lang = new Language(); |
1149
|
|
|
|
1150
|
|
|
// CAse where email not defined |
1151
|
|
|
if ($email === 'none' || empty($email) === true) { |
1152
|
|
|
return json_encode( |
1153
|
|
|
[ |
1154
|
|
|
'error' => true, |
1155
|
|
|
'message' => $lang->get('forgot_my_pw_email_sent'), |
1156
|
|
|
] |
1157
|
|
|
); |
1158
|
|
|
} |
1159
|
|
|
|
1160
|
|
|
// Build and send email |
1161
|
|
|
$email = buildEmail( |
1162
|
|
|
$subject, |
1163
|
|
|
$textMail, |
1164
|
|
|
$email, |
1165
|
|
|
$SETTINGS, |
1166
|
|
|
$textMailAlt = null, |
1167
|
|
|
$silent = true, |
1168
|
|
|
$cron |
1169
|
|
|
); |
1170
|
|
|
|
1171
|
|
|
if ($silent === false) { |
|
|
|
|
1172
|
|
|
return json_encode( |
1173
|
|
|
[ |
1174
|
|
|
'error' => false, |
1175
|
|
|
'message' => $lang->get('forgot_my_pw_email_sent'), |
1176
|
|
|
] |
1177
|
|
|
); |
1178
|
|
|
} |
1179
|
|
|
// Debug purpose |
1180
|
|
|
if ((int) $SETTINGS['email_debug_level'] !== 0 && $cron === false) { |
1181
|
|
|
return json_encode( |
1182
|
|
|
[ |
1183
|
|
|
'error' => true, |
1184
|
|
|
'message' => isset($email['ErrorInfo']) === true ? $email['ErrorInfo'] : '', |
1185
|
|
|
] |
1186
|
|
|
); |
1187
|
|
|
} |
1188
|
|
|
return json_encode( |
1189
|
|
|
[ |
1190
|
|
|
'error' => false, |
1191
|
|
|
'message' => $lang->get('share_sent_ok'), |
1192
|
|
|
] |
1193
|
|
|
); |
1194
|
|
|
} |
1195
|
|
|
|
1196
|
|
|
|
1197
|
|
|
function buildEmail( |
1198
|
|
|
$subject, |
1199
|
|
|
$textMail, |
1200
|
|
|
$email, |
1201
|
|
|
$SETTINGS, |
1202
|
|
|
$textMailAlt = null, |
1203
|
|
|
$silent = true, |
1204
|
|
|
$cron = false |
1205
|
|
|
) |
1206
|
|
|
{ |
1207
|
|
|
// Load PHPMailer |
1208
|
|
|
$mail = new PHPMailer(true); |
1209
|
|
|
$languageDir = $SETTINGS['cpassman_dir'] . '/vendor/phpmailer/phpmailer/language/'; |
1210
|
|
|
|
1211
|
|
|
try { |
1212
|
|
|
// Set language and SMTPDebug |
1213
|
|
|
$mail->setLanguage('en', $languageDir); |
1214
|
|
|
$mail->SMTPDebug = ($cron || $silent) ? 0 : $SETTINGS['email_debug_level']; |
1215
|
|
|
|
1216
|
|
|
// Configure SMTP |
1217
|
|
|
$mail->isSMTP(); |
1218
|
|
|
$mail->Host = $SETTINGS['email_smtp_server']; |
1219
|
|
|
$mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1; |
1220
|
|
|
$mail->Username = $SETTINGS['email_auth_username']; |
1221
|
|
|
$mail->Password = $SETTINGS['email_auth_pwd']; |
1222
|
|
|
$mail->Port = (int) $SETTINGS['email_port']; |
1223
|
|
|
$mail->SMTPSecure = $SETTINGS['email_security'] !== 'none' ? $SETTINGS['email_security'] : ''; |
1224
|
|
|
$mail->SMTPAutoTLS = $SETTINGS['email_security'] !== 'none'; |
1225
|
|
|
$mail->SMTPOptions = [ |
1226
|
|
|
'ssl' => [ |
1227
|
|
|
'verify_peer' => false, |
1228
|
|
|
'verify_peer_name' => false, |
1229
|
|
|
'allow_self_signed' => true, |
1230
|
|
|
], |
1231
|
|
|
]; |
1232
|
|
|
|
1233
|
|
|
// Set From and FromName |
1234
|
|
|
$mail->From = $SETTINGS['email_from']; |
1235
|
|
|
$mail->FromName = $SETTINGS['email_from_name']; |
1236
|
|
|
|
1237
|
|
|
// Prepare recipients |
1238
|
|
|
foreach (array_filter(explode(',', $email)) as $dest) { |
1239
|
|
|
$mail->addAddress($dest); |
1240
|
|
|
} |
1241
|
|
|
|
1242
|
|
|
// Prepare HTML and AltBody |
1243
|
|
|
$text_html = emailBody($textMail); |
1244
|
|
|
$mail->WordWrap = 80; |
1245
|
|
|
$mail->isHtml(true); |
1246
|
|
|
$mail->Subject = $subject; |
1247
|
|
|
$mail->Body = $text_html; |
1248
|
|
|
$mail->AltBody = is_null($textMailAlt) ? '' : $textMailAlt; |
1249
|
|
|
|
1250
|
|
|
// Send email |
1251
|
|
|
$mail->send(); |
1252
|
|
|
$mail->smtpClose(); |
1253
|
|
|
|
1254
|
|
|
return ''; |
1255
|
|
|
} catch (Exception $e) { |
1256
|
|
|
if (!$silent || (int) $SETTINGS['email_debug_level'] !== 0) { |
1257
|
|
|
return json_encode([ |
1258
|
|
|
'error' => true, |
1259
|
|
|
'errorInfo' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo), |
1260
|
|
|
]); |
1261
|
|
|
} |
1262
|
|
|
return ''; |
1263
|
|
|
} |
1264
|
|
|
/* |
1265
|
|
|
// load PHPMailer |
1266
|
|
|
$mail = new PHPMailer(true); |
1267
|
|
|
|
1268
|
|
|
// send to user |
1269
|
|
|
$mail->setLanguage('en', $SETTINGS['cpassman_dir'] . '/vendor/phpmailer/phpmailer/language/'); |
1270
|
|
|
$mail->SMTPDebug = isset($SETTINGS['email_debug_level']) === true && $cron === false && $silent === false ? $SETTINGS['email_debug_level'] : 0; |
1271
|
|
|
$mail->Port = (int) $SETTINGS['email_port']; |
1272
|
|
|
//COULD BE USED |
1273
|
|
|
$mail->CharSet = 'utf-8'; |
1274
|
|
|
$mail->SMTPSecure = $SETTINGS['email_security'] !== 'none' ? $SETTINGS['email_security'] : ''; |
1275
|
|
|
$mail->SMTPAutoTLS = $SETTINGS['email_security'] !== 'none' ? true : false; |
1276
|
|
|
$mail->SMTPOptions = [ |
1277
|
|
|
'ssl' => [ |
1278
|
|
|
'verify_peer' => false, |
1279
|
|
|
'verify_peer_name' => false, |
1280
|
|
|
'allow_self_signed' => true, |
1281
|
|
|
], |
1282
|
|
|
]; |
1283
|
|
|
$mail->isSmtp(); |
1284
|
|
|
// send via SMTP |
1285
|
|
|
$mail->Host = $SETTINGS['email_smtp_server']; |
1286
|
|
|
// SMTP servers |
1287
|
|
|
$mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1 ? true : false; |
1288
|
|
|
// turn on SMTP authentication |
1289
|
|
|
$mail->Username = $SETTINGS['email_auth_username']; |
1290
|
|
|
// SMTP username |
1291
|
|
|
$mail->Password = $SETTINGS['email_auth_pwd']; |
1292
|
|
|
// SMTP password |
1293
|
|
|
$mail->From = $SETTINGS['email_from']; |
1294
|
|
|
$mail->FromName = $SETTINGS['email_from_name']; |
1295
|
|
|
// Prepare for each person |
1296
|
|
|
foreach (array_filter(explode(',', $email)) as $dest) { |
1297
|
|
|
$mail->addAddress($dest); |
1298
|
|
|
} |
1299
|
|
|
|
1300
|
|
|
// Prepare HTML |
1301
|
|
|
$text_html = emailBody($textMail); |
1302
|
|
|
$mail->WordWrap = 80; |
1303
|
|
|
// set word wrap |
1304
|
|
|
$mail->isHtml(true); |
1305
|
|
|
// send as HTML |
1306
|
|
|
$mail->Subject = $subject; |
1307
|
|
|
$mail->Body = $text_html; |
1308
|
|
|
$mail->AltBody = is_null($textMailAlt) === false ? $textMailAlt : ''; |
1309
|
|
|
|
1310
|
|
|
try { |
1311
|
|
|
// send email |
1312
|
|
|
$mail->send(); |
1313
|
|
|
} catch (Exception $e) { |
1314
|
|
|
if ($silent === false || (int) $SETTINGS['email_debug_level'] !== 0) { |
1315
|
|
|
return json_encode( |
1316
|
|
|
[ |
1317
|
|
|
'error' => true, |
1318
|
|
|
'errorInfo' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo), |
1319
|
|
|
] |
1320
|
|
|
); |
1321
|
|
|
} |
1322
|
|
|
return ''; |
1323
|
|
|
} |
1324
|
|
|
$mail->smtpClose(); |
1325
|
|
|
|
1326
|
|
|
return json_encode( |
1327
|
|
|
[ |
1328
|
|
|
'error' => true, |
1329
|
|
|
'errorInfo' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo), |
1330
|
|
|
] |
1331
|
|
|
); |
1332
|
|
|
*/ |
1333
|
|
|
} |
1334
|
|
|
|
1335
|
|
|
/** |
1336
|
|
|
* Returns the email body. |
1337
|
|
|
* |
1338
|
|
|
* @param string $textMail Text for the email |
1339
|
|
|
*/ |
1340
|
|
|
function emailBody(string $textMail): string |
1341
|
|
|
{ |
1342
|
|
|
return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.= |
1343
|
|
|
w3.org/TR/html4/loose.dtd"><html> |
1344
|
|
|
<head><title>Email Template</title> |
1345
|
|
|
<style type="text/css"> |
1346
|
|
|
body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; } |
1347
|
|
|
</style></head> |
1348
|
|
|
<body style="-ms-text-size-adjust: none; size-adjust: none; margin: 0; padding: 10px 0; background-color: #f0f0f0;" bgcolor="#f0f0f0" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"> |
1349
|
|
|
<table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;"> |
1350
|
|
|
<tr><td style="border-collapse: collapse;"><br> |
1351
|
|
|
<table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;"> |
1352
|
|
|
<tr><td style="border-collapse: collapse; padding: 11px 20px;"> |
1353
|
|
|
<div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div> |
1354
|
|
|
</td></tr></table></td> |
1355
|
|
|
</tr> |
1356
|
|
|
<tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;"> |
1357
|
|
|
<table width="600" cellpadding="0" cellspacing="0" border="0" class="container" bgcolor="#ffffff" style="border-spacing: 0; border-bottom: 1px solid #e0e0e0; box-shadow: 0 0 3px #ddd; color: #434343; font-family: Helvetica, Verdana, sans-serif;"> |
1358
|
|
|
<tr><td class="container-padding" bgcolor="#ffffff" style="border-collapse: collapse; border-left: 1px solid #e0e0e0; background-color: #ffffff; padding-left: 30px; padding-right: 30px;"> |
1359
|
|
|
<br><div style="float:right;">' . |
1360
|
|
|
$textMail . |
1361
|
|
|
'<br><br></td></tr></table> |
1362
|
|
|
</td></tr></table> |
1363
|
|
|
<br></body></html>'; |
1364
|
|
|
} |
1365
|
|
|
|
1366
|
|
|
/** |
1367
|
|
|
* Generate a Key. |
1368
|
|
|
* |
1369
|
|
|
* @return string |
1370
|
|
|
*/ |
1371
|
|
|
function generateKey(): string |
1372
|
|
|
{ |
1373
|
|
|
return substr(md5(rand() . rand()), 0, 15); |
1374
|
|
|
} |
1375
|
|
|
|
1376
|
|
|
/** |
1377
|
|
|
* Convert date to timestamp. |
1378
|
|
|
* |
1379
|
|
|
* @param string $date The date |
1380
|
|
|
* @param string $date_format Date format |
1381
|
|
|
* |
1382
|
|
|
* @return int |
1383
|
|
|
*/ |
1384
|
|
|
function dateToStamp(string $date, string $date_format): int |
1385
|
|
|
{ |
1386
|
|
|
$date = date_parse_from_format($date_format, $date); |
1387
|
|
|
if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) { |
1388
|
|
|
return mktime( |
1389
|
|
|
empty($date['hour']) === false ? $date['hour'] : 23, |
1390
|
|
|
empty($date['minute']) === false ? $date['minute'] : 59, |
1391
|
|
|
empty($date['second']) === false ? $date['second'] : 59, |
1392
|
|
|
$date['month'], |
1393
|
|
|
$date['day'], |
1394
|
|
|
$date['year'] |
1395
|
|
|
); |
1396
|
|
|
} |
1397
|
|
|
return 0; |
1398
|
|
|
} |
1399
|
|
|
|
1400
|
|
|
/** |
1401
|
|
|
* Is this a date. |
1402
|
|
|
* |
1403
|
|
|
* @param string $date Date |
1404
|
|
|
* |
1405
|
|
|
* @return bool |
1406
|
|
|
*/ |
1407
|
|
|
function isDate(string $date): bool |
1408
|
|
|
{ |
1409
|
|
|
return strtotime($date) !== false; |
1410
|
|
|
} |
1411
|
|
|
|
1412
|
|
|
/** |
1413
|
|
|
* Check if isUTF8(). |
1414
|
|
|
* |
1415
|
|
|
* @param string|array $string Is the string |
1416
|
|
|
* |
1417
|
|
|
* @return int is the string in UTF8 format |
1418
|
|
|
*/ |
1419
|
|
|
function isUTF8($string): int |
1420
|
|
|
{ |
1421
|
|
|
if (is_array($string) === true) { |
1422
|
|
|
$string = $string['string']; |
1423
|
|
|
} |
1424
|
|
|
|
1425
|
|
|
return preg_match( |
1426
|
|
|
'%^(?: |
1427
|
|
|
[\x09\x0A\x0D\x20-\x7E] # ASCII |
1428
|
|
|
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte |
1429
|
|
|
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs |
1430
|
|
|
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte |
1431
|
|
|
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates |
1432
|
|
|
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 |
1433
|
|
|
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 |
1434
|
|
|
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 |
1435
|
|
|
)*$%xs', |
1436
|
|
|
$string |
1437
|
|
|
); |
1438
|
|
|
} |
1439
|
|
|
|
1440
|
|
|
/** |
1441
|
|
|
* Prepare an array to UTF8 format before JSON_encode. |
1442
|
|
|
* |
1443
|
|
|
* @param array $array Array of values |
1444
|
|
|
* |
1445
|
|
|
* @return array |
1446
|
|
|
*/ |
1447
|
|
|
function utf8Converter(array $array): array |
1448
|
|
|
{ |
1449
|
|
|
array_walk_recursive( |
1450
|
|
|
$array, |
1451
|
|
|
static function (&$item): void { |
1452
|
|
|
if (mb_detect_encoding((string) $item, 'utf-8', true) === false) { |
1453
|
|
|
$item = mb_convert_encoding($item, 'ISO-8859-1', 'UTF-8'); |
1454
|
|
|
} |
1455
|
|
|
} |
1456
|
|
|
); |
1457
|
|
|
return $array; |
1458
|
|
|
} |
1459
|
|
|
|
1460
|
|
|
/** |
1461
|
|
|
* Permits to prepare data to be exchanged. |
1462
|
|
|
* |
1463
|
|
|
* @param array|string $data Text |
1464
|
|
|
* @param string $type Parameter |
1465
|
|
|
* @param string $key Optional key |
1466
|
|
|
* |
1467
|
|
|
* @return string|array |
1468
|
|
|
*/ |
1469
|
|
|
function prepareExchangedData($data, string $type, ?string $key = null) |
1470
|
|
|
{ |
1471
|
|
|
$session = SessionManager::getSession(); |
1472
|
|
|
$cookieValue = SessionManager::getCookieValue('PHPSESSID'); |
1473
|
|
|
// get session |
1474
|
|
|
if ($key !== null || !$session->has('key')) {// || $session->get('key') !== $cookieValue) { |
1475
|
|
|
//$session->set('key', $cookieValue); |
1476
|
|
|
//error_log('DEBUG : Réinitialisation de la clé de session ' . $session->get('key')); |
1477
|
|
|
} |
1478
|
|
|
error_log(strtoupper($type).' - Valeur de la clé de session ' . $session->get('key')." --- et la valeur du COOKIE : ".$cookieValue); |
1479
|
|
|
// Perform |
1480
|
|
|
if ($type === 'encode' && is_array($data) === true) { |
1481
|
|
|
// Now encode |
1482
|
|
|
return Encryption::encrypt( |
1483
|
|
|
json_encode( |
1484
|
|
|
$data, |
1485
|
|
|
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP |
1486
|
|
|
), |
1487
|
|
|
$session->get('key') |
1488
|
|
|
); |
1489
|
|
|
} |
1490
|
|
|
if ($type === 'decode' && is_array($data) === false) { |
1491
|
|
|
// check if key exists |
1492
|
|
|
return json_decode( |
1493
|
|
|
(string) Encryption::decrypt( |
1494
|
|
|
(string) $data, |
1495
|
|
|
$session->get('key') |
1496
|
|
|
), |
1497
|
|
|
true |
1498
|
|
|
); |
1499
|
|
|
} |
1500
|
|
|
return ''; |
1501
|
|
|
} |
1502
|
|
|
|
1503
|
|
|
|
1504
|
|
|
/** |
1505
|
|
|
* Create a thumbnail. |
1506
|
|
|
* |
1507
|
|
|
* @param string $src Source |
1508
|
|
|
* @param string $dest Destination |
1509
|
|
|
* @param int $desired_width Size of width |
1510
|
|
|
* |
1511
|
|
|
* @return void|string|bool |
1512
|
|
|
*/ |
1513
|
|
|
function makeThumbnail(string $src, string $dest, int $desired_width) |
1514
|
|
|
{ |
1515
|
|
|
/* read the source image */ |
1516
|
|
|
if (is_file($src) === true && mime_content_type($src) === 'image/png') { |
1517
|
|
|
$source_image = imagecreatefrompng($src); |
1518
|
|
|
if ($source_image === false) { |
1519
|
|
|
return "Error: Not a valid PNG file! It's type is ".mime_content_type($src); |
1520
|
|
|
} |
1521
|
|
|
} else { |
1522
|
|
|
return "Error: Not a valid PNG file! It's type is ".mime_content_type($src); |
1523
|
|
|
} |
1524
|
|
|
|
1525
|
|
|
// Get height and width |
1526
|
|
|
$width = imagesx($source_image); |
1527
|
|
|
$height = imagesy($source_image); |
1528
|
|
|
/* find the "desired height" of this thumbnail, relative to the desired width */ |
1529
|
|
|
$desired_height = (int) floor($height * $desired_width / $width); |
1530
|
|
|
/* create a new, "virtual" image */ |
1531
|
|
|
$virtual_image = imagecreatetruecolor($desired_width, $desired_height); |
1532
|
|
|
if ($virtual_image === false) { |
1533
|
|
|
return false; |
1534
|
|
|
} |
1535
|
|
|
/* copy source image at a resized size */ |
1536
|
|
|
imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height); |
1537
|
|
|
/* create the physical thumbnail image to its destination */ |
1538
|
|
|
imagejpeg($virtual_image, $dest); |
1539
|
|
|
} |
1540
|
|
|
|
1541
|
|
|
/** |
1542
|
|
|
* Check table prefix in SQL query. |
1543
|
|
|
* |
1544
|
|
|
* @param string $table Table name |
1545
|
|
|
* |
1546
|
|
|
* @return string |
1547
|
|
|
*/ |
1548
|
|
|
function prefixTable(string $table): string |
1549
|
|
|
{ |
1550
|
|
|
$safeTable = htmlspecialchars(DB_PREFIX . $table); |
1551
|
|
|
if (empty($safeTable) === false) { |
1552
|
|
|
// sanitize string |
1553
|
|
|
return $safeTable; |
1554
|
|
|
} |
1555
|
|
|
// stop error no table |
1556
|
|
|
return 'table_not_exists'; |
1557
|
|
|
} |
1558
|
|
|
|
1559
|
|
|
/** |
1560
|
|
|
* GenerateCryptKey |
1561
|
|
|
* |
1562
|
|
|
* @param int $size Length |
1563
|
|
|
* @param bool $secure Secure |
1564
|
|
|
* @param bool $numerals Numerics |
1565
|
|
|
* @param bool $uppercase Uppercase letters |
1566
|
|
|
* @param bool $symbols Symbols |
1567
|
|
|
* @param bool $lowercase Lowercase |
1568
|
|
|
* @param array $SETTINGS SETTINGS |
1569
|
|
|
* |
1570
|
|
|
* @return string |
1571
|
|
|
*/ |
1572
|
|
|
function GenerateCryptKey( |
1573
|
|
|
int $size = 20, |
1574
|
|
|
bool $secure = false, |
1575
|
|
|
bool $numerals = false, |
1576
|
|
|
bool $uppercase = false, |
1577
|
|
|
bool $symbols = false, |
1578
|
|
|
bool $lowercase = false, |
1579
|
|
|
array $SETTINGS = [] |
1580
|
|
|
): string { |
1581
|
|
|
$generator = new ComputerPasswordGenerator(); |
1582
|
|
|
$generator->setRandomGenerator(new Php7RandomGenerator()); |
1583
|
|
|
|
1584
|
|
|
// Manage size |
1585
|
|
|
$generator->setLength((int) $size); |
1586
|
|
|
if ($secure === true) { |
1587
|
|
|
$generator->setSymbols(true); |
1588
|
|
|
$generator->setLowercase(true); |
1589
|
|
|
$generator->setUppercase(true); |
1590
|
|
|
$generator->setNumbers(true); |
1591
|
|
|
} else { |
1592
|
|
|
$generator->setLowercase($lowercase); |
1593
|
|
|
$generator->setUppercase($uppercase); |
1594
|
|
|
$generator->setNumbers($numerals); |
1595
|
|
|
$generator->setSymbols($symbols); |
1596
|
|
|
} |
1597
|
|
|
|
1598
|
|
|
return $generator->generatePasswords()[0]; |
1599
|
|
|
} |
1600
|
|
|
|
1601
|
|
|
/** |
1602
|
|
|
* Send sysLOG message |
1603
|
|
|
* |
1604
|
|
|
* @param string $message |
1605
|
|
|
* @param string $host |
1606
|
|
|
* @param int $port |
1607
|
|
|
* @param string $component |
1608
|
|
|
* |
1609
|
|
|
* @return void |
1610
|
|
|
*/ |
1611
|
|
|
function send_syslog($message, $host, $port, $component = 'teampass'): void |
1612
|
|
|
{ |
1613
|
|
|
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); |
1614
|
|
|
$syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message; |
1615
|
|
|
socket_sendto($sock, (string) $syslog_message, strlen($syslog_message), 0, (string) $host, (int) $port); |
1616
|
|
|
socket_close($sock); |
1617
|
|
|
} |
1618
|
|
|
|
1619
|
|
|
/** |
1620
|
|
|
* Permits to log events into DB |
1621
|
|
|
* |
1622
|
|
|
* @param array $SETTINGS Teampass settings |
1623
|
|
|
* @param string $type Type |
1624
|
|
|
* @param string $label Label |
1625
|
|
|
* @param string $who Who |
1626
|
|
|
* @param string $login Login |
1627
|
|
|
* @param string|int $field_1 Field |
1628
|
|
|
* |
1629
|
|
|
* @return void |
1630
|
|
|
*/ |
1631
|
|
|
function logEvents( |
1632
|
|
|
array $SETTINGS, |
1633
|
|
|
string $type, |
1634
|
|
|
string $label, |
1635
|
|
|
string $who, |
1636
|
|
|
?string $login = null, |
1637
|
|
|
$field_1 = null |
1638
|
|
|
): void |
1639
|
|
|
{ |
1640
|
|
|
if (empty($who)) { |
1641
|
|
|
$who = getClientIpServer(); |
1642
|
|
|
} |
1643
|
|
|
|
1644
|
|
|
// Load class DB |
1645
|
|
|
loadClasses('DB'); |
1646
|
|
|
|
1647
|
|
|
DB::insert( |
1648
|
|
|
prefixTable('log_system'), |
1649
|
|
|
[ |
1650
|
|
|
'type' => $type, |
1651
|
|
|
'date' => time(), |
1652
|
|
|
'label' => $label, |
1653
|
|
|
'qui' => $who, |
1654
|
|
|
'field_1' => $field_1 === null ? '' : $field_1, |
1655
|
|
|
] |
1656
|
|
|
); |
1657
|
|
|
// If SYSLOG |
1658
|
|
|
if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) { |
1659
|
|
|
if ($type === 'user_mngt') { |
1660
|
|
|
send_syslog( |
1661
|
|
|
'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ', |
1662
|
|
|
$SETTINGS['syslog_host'], |
1663
|
|
|
$SETTINGS['syslog_port'], |
1664
|
|
|
'teampass' |
1665
|
|
|
); |
1666
|
|
|
} else { |
1667
|
|
|
send_syslog( |
1668
|
|
|
'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ', |
1669
|
|
|
$SETTINGS['syslog_host'], |
1670
|
|
|
$SETTINGS['syslog_port'], |
1671
|
|
|
'teampass' |
1672
|
|
|
); |
1673
|
|
|
} |
1674
|
|
|
} |
1675
|
|
|
} |
1676
|
|
|
|
1677
|
|
|
/** |
1678
|
|
|
* Log events. |
1679
|
|
|
* |
1680
|
|
|
* @param array $SETTINGS Teampass settings |
1681
|
|
|
* @param int $item_id Item id |
1682
|
|
|
* @param string $item_label Item label |
1683
|
|
|
* @param int $id_user User id |
1684
|
|
|
* @param string $action Code for reason |
1685
|
|
|
* @param string $login User login |
1686
|
|
|
* @param string $raison Code for reason |
1687
|
|
|
* @param string $encryption_type Encryption on |
1688
|
|
|
* @param string $time Encryption Time |
1689
|
|
|
* @param string $old_value Old value |
1690
|
|
|
* |
1691
|
|
|
* @return void |
1692
|
|
|
*/ |
1693
|
|
|
function logItems( |
1694
|
|
|
array $SETTINGS, |
1695
|
|
|
int $item_id, |
1696
|
|
|
string $item_label, |
1697
|
|
|
int $id_user, |
1698
|
|
|
string $action, |
1699
|
|
|
?string $login = null, |
1700
|
|
|
?string $raison = null, |
1701
|
|
|
?string $encryption_type = null, |
1702
|
|
|
?string $time = null, |
1703
|
|
|
?string $old_value = null |
1704
|
|
|
): void { |
1705
|
|
|
// Load class DB |
1706
|
|
|
loadClasses('DB'); |
1707
|
|
|
|
1708
|
|
|
// Insert log in DB |
1709
|
|
|
DB::insert( |
1710
|
|
|
prefixTable('log_items'), |
1711
|
|
|
[ |
1712
|
|
|
'id_item' => $item_id, |
1713
|
|
|
'date' => is_null($time) === true ? time() : $time, |
1714
|
|
|
'id_user' => $id_user, |
1715
|
|
|
'action' => $action, |
1716
|
|
|
'raison' => $raison, |
1717
|
|
|
'old_value' => $old_value, |
1718
|
|
|
'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type, |
1719
|
|
|
] |
1720
|
|
|
); |
1721
|
|
|
// Timestamp the last change |
1722
|
|
|
if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') { |
1723
|
|
|
DB::update( |
1724
|
|
|
prefixTable('misc'), |
1725
|
|
|
[ |
1726
|
|
|
'valeur' => time(), |
1727
|
|
|
], |
1728
|
|
|
'type = %s AND intitule = %s', |
1729
|
|
|
'timestamp', |
1730
|
|
|
'last_item_change' |
1731
|
|
|
); |
1732
|
|
|
} |
1733
|
|
|
|
1734
|
|
|
// SYSLOG |
1735
|
|
|
if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') { |
1736
|
|
|
// Extract reason |
1737
|
|
|
$attribute = is_null($raison) === true ? Array('') : explode(' : ', $raison); |
1738
|
|
|
// Get item info if not known |
1739
|
|
|
if (empty($item_label) === true) { |
1740
|
|
|
$dataItem = DB::queryfirstrow( |
1741
|
|
|
'SELECT id, id_tree, label |
1742
|
|
|
FROM ' . prefixTable('items') . ' |
1743
|
|
|
WHERE id = %i', |
1744
|
|
|
$item_id |
1745
|
|
|
); |
1746
|
|
|
$item_label = $dataItem['label']; |
1747
|
|
|
} |
1748
|
|
|
|
1749
|
|
|
send_syslog( |
1750
|
|
|
'action=' . str_replace('at_', '', $action) . |
1751
|
|
|
' attribute=' . str_replace('at_', '', $attribute[0]) . |
1752
|
|
|
' itemno=' . $item_id . |
1753
|
|
|
' user=' . is_null($login) === true ? '' : addslashes((string) $login) . |
1754
|
|
|
' itemname="' . addslashes($item_label) . '"', |
1755
|
|
|
$SETTINGS['syslog_host'], |
1756
|
|
|
$SETTINGS['syslog_port'], |
1757
|
|
|
'teampass' |
1758
|
|
|
); |
1759
|
|
|
} |
1760
|
|
|
|
1761
|
|
|
// send notification if enabled |
1762
|
|
|
//notifyOnChange($item_id, $action, $SETTINGS); |
1763
|
|
|
} |
1764
|
|
|
|
1765
|
|
|
/** |
1766
|
|
|
* Prepare notification email to subscribers. |
1767
|
|
|
* |
1768
|
|
|
* @param int $item_id Item id |
1769
|
|
|
* @param string $label Item label |
1770
|
|
|
* @param array $changes List of changes |
1771
|
|
|
* @param array $SETTINGS Teampass settings |
1772
|
|
|
* |
1773
|
|
|
* @return void |
1774
|
|
|
*/ |
1775
|
|
|
function notifyChangesToSubscribers(int $item_id, string $label, array $changes, array $SETTINGS): void |
1776
|
|
|
{ |
1777
|
|
|
$session = SessionManager::getSession(); |
1778
|
|
|
$lang = new Language(); |
1779
|
|
|
$globalsUserId = $session->get('user-id'); |
1780
|
|
|
$globalsLastname = $session->get('user-lastname'); |
1781
|
|
|
$globalsName = $session->get('user-name'); |
1782
|
|
|
// send email to user that what to be notified |
1783
|
|
|
$notification = DB::queryOneColumn( |
1784
|
|
|
'email', |
1785
|
|
|
'SELECT * |
1786
|
|
|
FROM ' . prefixTable('notification') . ' AS n |
1787
|
|
|
INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id) |
1788
|
|
|
WHERE n.item_id = %i AND n.user_id != %i', |
1789
|
|
|
$item_id, |
1790
|
|
|
$globalsUserId |
1791
|
|
|
); |
1792
|
|
|
if (DB::count() > 0) { |
1793
|
|
|
// Prepare path |
1794
|
|
|
$path = geItemReadablePath($item_id, '', $SETTINGS); |
1795
|
|
|
// Get list of changes |
1796
|
|
|
$htmlChanges = '<ul>'; |
1797
|
|
|
foreach ($changes as $change) { |
1798
|
|
|
$htmlChanges .= '<li>' . $change . '</li>'; |
1799
|
|
|
} |
1800
|
|
|
$htmlChanges .= '</ul>'; |
1801
|
|
|
// send email |
1802
|
|
|
DB::insert( |
1803
|
|
|
prefixTable('emails'), |
1804
|
|
|
[ |
1805
|
|
|
'timestamp' => time(), |
1806
|
|
|
'subject' => $lang->get('email_subject_item_updated'), |
1807
|
|
|
'body' => str_replace( |
1808
|
|
|
['#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'], |
1809
|
|
|
[$label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges], |
1810
|
|
|
$lang->get('email_body_item_updated') |
1811
|
|
|
), |
1812
|
|
|
'receivers' => implode(',', $notification), |
1813
|
|
|
'status' => '', |
1814
|
|
|
] |
1815
|
|
|
); |
1816
|
|
|
} |
1817
|
|
|
} |
1818
|
|
|
|
1819
|
|
|
/** |
1820
|
|
|
* Returns the Item + path. |
1821
|
|
|
* |
1822
|
|
|
* @param int $id_tree Node id |
1823
|
|
|
* @param string $label Label |
1824
|
|
|
* @param array $SETTINGS TP settings |
1825
|
|
|
* |
1826
|
|
|
* @return string |
1827
|
|
|
*/ |
1828
|
|
|
function geItemReadablePath(int $id_tree, string $label, array $SETTINGS): string |
1829
|
|
|
{ |
1830
|
|
|
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
1831
|
|
|
$arbo = $tree->getPath($id_tree, true); |
1832
|
|
|
$path = ''; |
1833
|
|
|
foreach ($arbo as $elem) { |
1834
|
|
|
if (empty($path) === true) { |
1835
|
|
|
$path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' '; |
1836
|
|
|
} else { |
1837
|
|
|
$path .= '→ ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES); |
1838
|
|
|
} |
1839
|
|
|
} |
1840
|
|
|
|
1841
|
|
|
// Build text to show user |
1842
|
|
|
if (empty($label) === false) { |
1843
|
|
|
return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')'; |
1844
|
|
|
} |
1845
|
|
|
return empty($path) === true ? '' : $path; |
1846
|
|
|
} |
1847
|
|
|
|
1848
|
|
|
/** |
1849
|
|
|
* Get the client ip address. |
1850
|
|
|
* |
1851
|
|
|
* @return string IP address |
1852
|
|
|
*/ |
1853
|
|
|
function getClientIpServer(): string |
1854
|
|
|
{ |
1855
|
|
|
if (getenv('HTTP_CLIENT_IP')) { |
1856
|
|
|
$ipaddress = getenv('HTTP_CLIENT_IP'); |
1857
|
|
|
} elseif (getenv('HTTP_X_FORWARDED_FOR')) { |
1858
|
|
|
$ipaddress = getenv('HTTP_X_FORWARDED_FOR'); |
1859
|
|
|
} elseif (getenv('HTTP_X_FORWARDED')) { |
1860
|
|
|
$ipaddress = getenv('HTTP_X_FORWARDED'); |
1861
|
|
|
} elseif (getenv('HTTP_FORWARDED_FOR')) { |
1862
|
|
|
$ipaddress = getenv('HTTP_FORWARDED_FOR'); |
1863
|
|
|
} elseif (getenv('HTTP_FORWARDED')) { |
1864
|
|
|
$ipaddress = getenv('HTTP_FORWARDED'); |
1865
|
|
|
} elseif (getenv('REMOTE_ADDR')) { |
1866
|
|
|
$ipaddress = getenv('REMOTE_ADDR'); |
1867
|
|
|
} else { |
1868
|
|
|
$ipaddress = 'UNKNOWN'; |
1869
|
|
|
} |
1870
|
|
|
|
1871
|
|
|
return $ipaddress; |
1872
|
|
|
} |
1873
|
|
|
|
1874
|
|
|
/** |
1875
|
|
|
* Escape all HTML, JavaScript, and CSS. |
1876
|
|
|
* |
1877
|
|
|
* @param string $input The input string |
1878
|
|
|
* @param string $encoding Which character encoding are we using? |
1879
|
|
|
* |
1880
|
|
|
* @return string |
1881
|
|
|
*/ |
1882
|
|
|
function noHTML(string $input, string $encoding = 'UTF-8'): string |
1883
|
|
|
{ |
1884
|
|
|
return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false); |
1885
|
|
|
} |
1886
|
|
|
|
1887
|
|
|
/** |
1888
|
|
|
* Permits to handle the Teampass config file |
1889
|
|
|
* $action accepts "rebuild" and "update" |
1890
|
|
|
* |
1891
|
|
|
* @param string $action Action to perform |
1892
|
|
|
* @param array $SETTINGS Teampass settings |
1893
|
|
|
* @param string $field Field to refresh |
1894
|
|
|
* @param string $value Value to set |
1895
|
|
|
* |
1896
|
|
|
* @return string|bool |
1897
|
|
|
*/ |
1898
|
|
|
function handleConfigFile($action, $SETTINGS, $field = null, $value = null) |
1899
|
|
|
{ |
1900
|
|
|
$tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php'; |
1901
|
|
|
|
1902
|
|
|
// Load class DB |
1903
|
|
|
loadClasses('DB'); |
1904
|
|
|
|
1905
|
|
|
if (file_exists($tp_config_file) === false || $action === 'rebuild') { |
1906
|
|
|
// perform a copy |
1907
|
|
|
if (file_exists($tp_config_file)) { |
1908
|
|
|
if (! copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) { |
1909
|
|
|
return "ERROR: Could not copy file '" . $tp_config_file . "'"; |
1910
|
|
|
} |
1911
|
|
|
} |
1912
|
|
|
|
1913
|
|
|
// regenerate |
1914
|
|
|
$data = []; |
1915
|
|
|
$data[0] = "<?php\n"; |
1916
|
|
|
$data[1] = "global \$SETTINGS;\n"; |
1917
|
|
|
$data[2] = "\$SETTINGS = array (\n"; |
1918
|
|
|
$rows = DB::query( |
1919
|
|
|
'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s', |
1920
|
|
|
'admin' |
1921
|
|
|
); |
1922
|
|
|
foreach ($rows as $record) { |
1923
|
|
|
array_push($data, " '" . $record['intitule'] . "' => '" . htmlspecialchars_decode($record['valeur'], ENT_COMPAT) . "',\n"); |
1924
|
|
|
} |
1925
|
|
|
array_push($data, ");\n"); |
1926
|
|
|
$data = array_unique($data); |
1927
|
|
|
// --- |
1928
|
|
|
} elseif ($action === 'update' && empty($field) === false) { |
1929
|
|
|
$data = file($tp_config_file); |
1930
|
|
|
$inc = 0; |
1931
|
|
|
$bFound = false; |
1932
|
|
|
foreach ($data as $line) { |
1933
|
|
|
if (stristr($line, ');')) { |
1934
|
|
|
break; |
1935
|
|
|
} |
1936
|
|
|
|
1937
|
|
|
if (stristr($line, "'" . $field . "' => '")) { |
1938
|
|
|
$data[$inc] = " '" . $field . "' => '" . htmlspecialchars_decode($value ?? '', ENT_COMPAT) . "',\n"; |
1939
|
|
|
$bFound = true; |
1940
|
|
|
break; |
1941
|
|
|
} |
1942
|
|
|
++$inc; |
1943
|
|
|
} |
1944
|
|
|
if ($bFound === false) { |
1945
|
|
|
$data[$inc] = " '" . $field . "' => '" . htmlspecialchars_decode($value ?? '', ENT_COMPAT). "',\n);\n"; |
1946
|
|
|
} |
1947
|
|
|
} |
1948
|
|
|
|
1949
|
|
|
// update file |
1950
|
|
|
file_put_contents($tp_config_file, implode('', $data ?? [])); |
1951
|
|
|
return true; |
1952
|
|
|
} |
1953
|
|
|
|
1954
|
|
|
/** |
1955
|
|
|
* Permits to replace \ to permit correct display |
1956
|
|
|
* |
1957
|
|
|
* @param string $input Some text |
1958
|
|
|
* |
1959
|
|
|
* @return string |
1960
|
|
|
*/ |
1961
|
|
|
function handleBackslash(string $input): string |
1962
|
|
|
{ |
1963
|
|
|
return str_replace('&#92;', '\', $input); |
1964
|
|
|
} |
1965
|
|
|
|
1966
|
|
|
/** |
1967
|
|
|
* Permits to load settings |
1968
|
|
|
* |
1969
|
|
|
* @return void |
1970
|
|
|
*/ |
1971
|
|
|
function loadSettings(): void |
1972
|
|
|
{ |
1973
|
|
|
global $SETTINGS; |
1974
|
|
|
/* LOAD CPASSMAN SETTINGS */ |
1975
|
|
|
if (! isset($SETTINGS['loaded']) || $SETTINGS['loaded'] !== 1) { |
1976
|
|
|
$SETTINGS = []; |
1977
|
|
|
$SETTINGS['duplicate_folder'] = 0; |
1978
|
|
|
//by default, this is set to 0; |
1979
|
|
|
$SETTINGS['duplicate_item'] = 0; |
1980
|
|
|
//by default, this is set to 0; |
1981
|
|
|
$SETTINGS['number_of_used_pw'] = 5; |
1982
|
|
|
//by default, this value is set to 5; |
1983
|
|
|
$settings = []; |
1984
|
|
|
$rows = DB::query( |
1985
|
|
|
'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2', |
1986
|
|
|
[ |
1987
|
|
|
'type' => 'admin', |
1988
|
|
|
'type2' => 'settings', |
1989
|
|
|
] |
1990
|
|
|
); |
1991
|
|
|
foreach ($rows as $record) { |
1992
|
|
|
if ($record['type'] === 'admin') { |
1993
|
|
|
$SETTINGS[$record['intitule']] = $record['valeur']; |
1994
|
|
|
} else { |
1995
|
|
|
$settings[$record['intitule']] = $record['valeur']; |
1996
|
|
|
} |
1997
|
|
|
} |
1998
|
|
|
$SETTINGS['loaded'] = 1; |
1999
|
|
|
$SETTINGS['default_session_expiration_time'] = 5; |
2000
|
|
|
} |
2001
|
|
|
} |
2002
|
|
|
|
2003
|
|
|
/** |
2004
|
|
|
* check if folder has custom fields. |
2005
|
|
|
* Ensure that target one also has same custom fields |
2006
|
|
|
* |
2007
|
|
|
* @param int $source_id |
2008
|
|
|
* @param int $target_id |
2009
|
|
|
* |
2010
|
|
|
* @return bool |
2011
|
|
|
*/ |
2012
|
|
|
function checkCFconsistency(int $source_id, int $target_id): bool |
2013
|
|
|
{ |
2014
|
|
|
$source_cf = []; |
2015
|
|
|
$rows = DB::QUERY( |
2016
|
|
|
'SELECT id_category |
2017
|
|
|
FROM ' . prefixTable('categories_folders') . ' |
2018
|
|
|
WHERE id_folder = %i', |
2019
|
|
|
$source_id |
2020
|
|
|
); |
2021
|
|
|
foreach ($rows as $record) { |
2022
|
|
|
array_push($source_cf, $record['id_category']); |
2023
|
|
|
} |
2024
|
|
|
|
2025
|
|
|
$target_cf = []; |
2026
|
|
|
$rows = DB::QUERY( |
2027
|
|
|
'SELECT id_category |
2028
|
|
|
FROM ' . prefixTable('categories_folders') . ' |
2029
|
|
|
WHERE id_folder = %i', |
2030
|
|
|
$target_id |
2031
|
|
|
); |
2032
|
|
|
foreach ($rows as $record) { |
2033
|
|
|
array_push($target_cf, $record['id_category']); |
2034
|
|
|
} |
2035
|
|
|
|
2036
|
|
|
$cf_diff = array_diff($source_cf, $target_cf); |
2037
|
|
|
if (count($cf_diff) > 0) { |
2038
|
|
|
return false; |
2039
|
|
|
} |
2040
|
|
|
|
2041
|
|
|
return true; |
2042
|
|
|
} |
2043
|
|
|
|
2044
|
|
|
/** |
2045
|
|
|
* Will encrypte/decrypt a fil eusing Defuse. |
2046
|
|
|
* |
2047
|
|
|
* @param string $type can be either encrypt or decrypt |
2048
|
|
|
* @param string $source_file path to source file |
2049
|
|
|
* @param string $target_file path to target file |
2050
|
|
|
* @param array $SETTINGS Settings |
2051
|
|
|
* @param string $password A password |
2052
|
|
|
* |
2053
|
|
|
* @return string|bool |
2054
|
|
|
*/ |
2055
|
|
|
function prepareFileWithDefuse( |
2056
|
|
|
string $type, |
2057
|
|
|
string $source_file, |
2058
|
|
|
string $target_file, |
2059
|
|
|
array $SETTINGS, |
2060
|
|
|
string $password = null |
2061
|
|
|
) { |
2062
|
|
|
// Load AntiXSS |
2063
|
|
|
$antiXss = new AntiXSS(); |
2064
|
|
|
// Protect against bad inputs |
2065
|
|
|
if (is_array($source_file) === true || is_array($target_file) === true) { |
2066
|
|
|
return 'error_cannot_be_array'; |
2067
|
|
|
} |
2068
|
|
|
|
2069
|
|
|
// Sanitize |
2070
|
|
|
$source_file = $antiXss->xss_clean($source_file); |
2071
|
|
|
$target_file = $antiXss->xss_clean($target_file); |
2072
|
|
|
if (empty($password) === true || is_null($password) === true) { |
2073
|
|
|
// get KEY to define password |
2074
|
|
|
$ascii_key = file_get_contents(SECUREPATH.'/'.SECUREFILE); |
2075
|
|
|
$password = Key::loadFromAsciiSafeString($ascii_key); |
2076
|
|
|
} |
2077
|
|
|
|
2078
|
|
|
$err = ''; |
2079
|
|
|
if ($type === 'decrypt') { |
2080
|
|
|
// Decrypt file |
2081
|
|
|
$err = defuseFileDecrypt( |
2082
|
|
|
$source_file, |
2083
|
|
|
$target_file, |
2084
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
2085
|
|
|
$password |
2086
|
|
|
); |
2087
|
|
|
} elseif ($type === 'encrypt') { |
2088
|
|
|
// Encrypt file |
2089
|
|
|
$err = defuseFileEncrypt( |
2090
|
|
|
$source_file, |
2091
|
|
|
$target_file, |
2092
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
2093
|
|
|
$password |
2094
|
|
|
); |
2095
|
|
|
} |
2096
|
|
|
|
2097
|
|
|
// return error |
2098
|
|
|
return $err === true ? '' : $err; |
2099
|
|
|
} |
2100
|
|
|
|
2101
|
|
|
/** |
2102
|
|
|
* Encrypt a file with Defuse. |
2103
|
|
|
* |
2104
|
|
|
* @param string $source_file path to source file |
2105
|
|
|
* @param string $target_file path to target file |
2106
|
|
|
* @param array $SETTINGS Settings |
2107
|
|
|
* @param string $password A password |
2108
|
|
|
* |
2109
|
|
|
* @return string|bool |
2110
|
|
|
*/ |
2111
|
|
|
function defuseFileEncrypt( |
2112
|
|
|
string $source_file, |
2113
|
|
|
string $target_file, |
2114
|
|
|
array $SETTINGS, |
2115
|
|
|
string $password = null |
2116
|
|
|
) { |
2117
|
|
|
try { |
2118
|
|
|
CryptoFile::encryptFileWithPassword( |
2119
|
|
|
$source_file, |
2120
|
|
|
$target_file, |
2121
|
|
|
$password |
2122
|
|
|
); |
2123
|
|
|
} catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) { |
2124
|
|
|
$err = 'wrong_key'; |
2125
|
|
|
} catch (CryptoException\EnvironmentIsBrokenException $ex) { |
2126
|
|
|
$err = $ex; |
2127
|
|
|
} catch (CryptoException\IOException $ex) { |
2128
|
|
|
$err = $ex; |
2129
|
|
|
} |
2130
|
|
|
|
2131
|
|
|
// return error |
2132
|
|
|
return empty($err) === false ? $err : true; |
2133
|
|
|
} |
2134
|
|
|
|
2135
|
|
|
/** |
2136
|
|
|
* Decrypt a file with Defuse. |
2137
|
|
|
* |
2138
|
|
|
* @param string $source_file path to source file |
2139
|
|
|
* @param string $target_file path to target file |
2140
|
|
|
* @param array $SETTINGS Settings |
2141
|
|
|
* @param string $password A password |
2142
|
|
|
* |
2143
|
|
|
* @return string|bool |
2144
|
|
|
*/ |
2145
|
|
|
function defuseFileDecrypt( |
2146
|
|
|
string $source_file, |
2147
|
|
|
string $target_file, |
2148
|
|
|
array $SETTINGS, |
2149
|
|
|
string $password = null |
2150
|
|
|
) { |
2151
|
|
|
try { |
2152
|
|
|
CryptoFile::decryptFileWithPassword( |
2153
|
|
|
$source_file, |
2154
|
|
|
$target_file, |
2155
|
|
|
$password |
2156
|
|
|
); |
2157
|
|
|
} catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) { |
2158
|
|
|
$err = 'wrong_key'; |
2159
|
|
|
} catch (CryptoException\EnvironmentIsBrokenException $ex) { |
2160
|
|
|
$err = $ex; |
2161
|
|
|
} catch (CryptoException\IOException $ex) { |
2162
|
|
|
$err = $ex; |
2163
|
|
|
} |
2164
|
|
|
|
2165
|
|
|
// return error |
2166
|
|
|
return empty($err) === false ? $err : true; |
2167
|
|
|
} |
2168
|
|
|
|
2169
|
|
|
/* |
2170
|
|
|
* NOT TO BE USED |
2171
|
|
|
*/ |
2172
|
|
|
/** |
2173
|
|
|
* Undocumented function. |
2174
|
|
|
* |
2175
|
|
|
* @param string $text Text to debug |
2176
|
|
|
*/ |
2177
|
|
|
function debugTeampass(string $text): void |
2178
|
|
|
{ |
2179
|
|
|
$debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+'); |
2180
|
|
|
if ($debugFile !== false) { |
2181
|
|
|
fputs($debugFile, $text); |
2182
|
|
|
fclose($debugFile); |
2183
|
|
|
} |
2184
|
|
|
} |
2185
|
|
|
|
2186
|
|
|
/** |
2187
|
|
|
* DELETE the file with expected command depending on server type. |
2188
|
|
|
* |
2189
|
|
|
* @param string $file Path to file |
2190
|
|
|
* @param array $SETTINGS Teampass settings |
2191
|
|
|
* |
2192
|
|
|
* @return void |
2193
|
|
|
*/ |
2194
|
|
|
function fileDelete(string $file, array $SETTINGS): void |
2195
|
|
|
{ |
2196
|
|
|
// Load AntiXSS |
2197
|
|
|
$antiXss = new AntiXSS(); |
2198
|
|
|
$file = $antiXss->xss_clean($file); |
2199
|
|
|
if (is_file($file)) { |
2200
|
|
|
unlink($file); |
2201
|
|
|
} |
2202
|
|
|
} |
2203
|
|
|
|
2204
|
|
|
/** |
2205
|
|
|
* Permits to extract the file extension. |
2206
|
|
|
* |
2207
|
|
|
* @param string $file File name |
2208
|
|
|
* |
2209
|
|
|
* @return string |
2210
|
|
|
*/ |
2211
|
|
|
function getFileExtension(string $file): string |
2212
|
|
|
{ |
2213
|
|
|
if (strpos($file, '.') === false) { |
2214
|
|
|
return $file; |
2215
|
|
|
} |
2216
|
|
|
|
2217
|
|
|
return substr($file, strrpos($file, '.') + 1); |
2218
|
|
|
} |
2219
|
|
|
|
2220
|
|
|
/** |
2221
|
|
|
* Chmods files and folders with different permissions. |
2222
|
|
|
* |
2223
|
|
|
* This is an all-PHP alternative to using: \n |
2224
|
|
|
* <tt>exec("find ".$path." -type f -exec chmod 644 {} \;");</tt> \n |
2225
|
|
|
* <tt>exec("find ".$path." -type d -exec chmod 755 {} \;");</tt> |
2226
|
|
|
* |
2227
|
|
|
* @author Jeppe Toustrup (tenzer at tenzer dot dk) |
2228
|
|
|
* |
2229
|
|
|
* @param string $path An either relative or absolute path to a file or directory which should be processed. |
2230
|
|
|
* @param int $filePerm The permissions any found files should get. |
2231
|
|
|
* @param int $dirPerm The permissions any found folder should get. |
2232
|
|
|
* |
2233
|
|
|
* @return bool Returns TRUE if the path if found and FALSE if not. |
2234
|
|
|
* |
2235
|
|
|
* @warning The permission levels has to be entered in octal format, which |
2236
|
|
|
* normally means adding a zero ("0") in front of the permission level. \n |
2237
|
|
|
* More info at: http://php.net/chmod. |
2238
|
|
|
*/ |
2239
|
|
|
|
2240
|
|
|
function recursiveChmod( |
2241
|
|
|
string $path, |
2242
|
|
|
int $filePerm = 0644, |
2243
|
|
|
int $dirPerm = 0755 |
2244
|
|
|
) { |
2245
|
|
|
// Check if the path exists |
2246
|
|
|
if (! file_exists($path)) { |
2247
|
|
|
return false; |
2248
|
|
|
} |
2249
|
|
|
|
2250
|
|
|
// See whether this is a file |
2251
|
|
|
if (is_file($path)) { |
2252
|
|
|
// Chmod the file with our given filepermissions |
2253
|
|
|
try { |
2254
|
|
|
chmod($path, $filePerm); |
2255
|
|
|
} catch (Exception $e) { |
2256
|
|
|
return false; |
2257
|
|
|
} |
2258
|
|
|
// If this is a directory... |
2259
|
|
|
} elseif (is_dir($path)) { |
2260
|
|
|
// Then get an array of the contents |
2261
|
|
|
$foldersAndFiles = scandir($path); |
2262
|
|
|
// Remove "." and ".." from the list |
2263
|
|
|
$entries = array_slice($foldersAndFiles, 2); |
2264
|
|
|
// Parse every result... |
2265
|
|
|
foreach ($entries as $entry) { |
2266
|
|
|
// And call this function again recursively, with the same permissions |
2267
|
|
|
recursiveChmod($path.'/'.$entry, $filePerm, $dirPerm); |
2268
|
|
|
} |
2269
|
|
|
|
2270
|
|
|
// When we are done with the contents of the directory, we chmod the directory itself |
2271
|
|
|
try { |
2272
|
|
|
chmod($path, $filePerm); |
2273
|
|
|
} catch (Exception $e) { |
2274
|
|
|
return false; |
2275
|
|
|
} |
2276
|
|
|
} |
2277
|
|
|
|
2278
|
|
|
// Everything seemed to work out well, return true |
2279
|
|
|
return true; |
2280
|
|
|
} |
2281
|
|
|
|
2282
|
|
|
/** |
2283
|
|
|
* Check if user can access to this item. |
2284
|
|
|
* |
2285
|
|
|
* @param int $item_id ID of item |
2286
|
|
|
* @param array $SETTINGS |
2287
|
|
|
* |
2288
|
|
|
* @return bool|string |
2289
|
|
|
*/ |
2290
|
|
|
function accessToItemIsGranted(int $item_id, array $SETTINGS) |
2291
|
|
|
{ |
2292
|
|
|
|
2293
|
|
|
$session = SessionManager::getSession(); |
2294
|
|
|
$session_groupes_visibles = $session->get('user-accessible_folders'); |
2295
|
|
|
$session_list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items'); |
2296
|
|
|
// Load item data |
2297
|
|
|
$data = DB::queryFirstRow( |
2298
|
|
|
'SELECT id_tree |
2299
|
|
|
FROM ' . prefixTable('items') . ' |
2300
|
|
|
WHERE id = %i', |
2301
|
|
|
$item_id |
2302
|
|
|
); |
2303
|
|
|
// Check if user can access this folder |
2304
|
|
|
if (in_array($data['id_tree'], $session_groupes_visibles) === false) { |
2305
|
|
|
// Now check if this folder is restricted to user |
2306
|
|
|
if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true |
2307
|
|
|
&& in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false |
2308
|
|
|
) { |
2309
|
|
|
return 'ERR_FOLDER_NOT_ALLOWED'; |
2310
|
|
|
} |
2311
|
|
|
} |
2312
|
|
|
|
2313
|
|
|
return true; |
2314
|
|
|
} |
2315
|
|
|
|
2316
|
|
|
/** |
2317
|
|
|
* Creates a unique key. |
2318
|
|
|
* |
2319
|
|
|
* @param int $lenght Key lenght |
2320
|
|
|
* |
2321
|
|
|
* @return string |
2322
|
|
|
*/ |
2323
|
|
|
function uniqidReal(int $lenght = 13): string |
2324
|
|
|
{ |
2325
|
|
|
if (function_exists('random_bytes')) { |
2326
|
|
|
$bytes = random_bytes(intval(ceil($lenght / 2))); |
2327
|
|
|
} elseif (function_exists('openssl_random_pseudo_bytes')) { |
2328
|
|
|
$bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2))); |
2329
|
|
|
} else { |
2330
|
|
|
throw new Exception('no cryptographically secure random function available'); |
2331
|
|
|
} |
2332
|
|
|
|
2333
|
|
|
return substr(bin2hex($bytes), 0, $lenght); |
2334
|
|
|
} |
2335
|
|
|
|
2336
|
|
|
/** |
2337
|
|
|
* Obfuscate an email. |
2338
|
|
|
* |
2339
|
|
|
* @param string $email Email address |
2340
|
|
|
* |
2341
|
|
|
* @return string |
2342
|
|
|
*/ |
2343
|
|
|
function obfuscateEmail(string $email): string |
2344
|
|
|
{ |
2345
|
|
|
$email = explode("@", $email); |
2346
|
|
|
$name = $email[0]; |
2347
|
|
|
if (strlen($name) > 3) { |
2348
|
|
|
$name = substr($name, 0, 2); |
2349
|
|
|
for ($i = 0; $i < strlen($email[0]) - 3; $i++) { |
2350
|
|
|
$name .= "*"; |
2351
|
|
|
} |
2352
|
|
|
$name .= substr($email[0], -1, 1); |
2353
|
|
|
} |
2354
|
|
|
$host = explode(".", $email[1])[0]; |
2355
|
|
|
if (strlen($host) > 3) { |
2356
|
|
|
$host = substr($host, 0, 1); |
2357
|
|
|
for ($i = 0; $i < strlen(explode(".", $email[1])[0]) - 2; $i++) { |
2358
|
|
|
$host .= "*"; |
2359
|
|
|
} |
2360
|
|
|
$host .= substr(explode(".", $email[1])[0], -1, 1); |
2361
|
|
|
} |
2362
|
|
|
$email = $name . "@" . $host . "." . explode(".", $email[1])[1]; |
2363
|
|
|
return $email; |
2364
|
|
|
} |
2365
|
|
|
|
2366
|
|
|
/** |
2367
|
|
|
* Perform a Query. |
2368
|
|
|
* |
2369
|
|
|
* @param array $SETTINGS Teamapss settings |
2370
|
|
|
* @param string $fields Fields to use |
2371
|
|
|
* @param string $table Table to use |
2372
|
|
|
* |
2373
|
|
|
* @return array |
2374
|
|
|
*/ |
2375
|
|
|
function performDBQuery(array $SETTINGS, string $fields, string $table): array |
2376
|
|
|
{ |
2377
|
|
|
// include librairies & connect to DB |
2378
|
|
|
//include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php'; |
2379
|
|
|
|
2380
|
|
|
// Load class DB |
2381
|
|
|
loadClasses('DB'); |
2382
|
|
|
|
2383
|
|
|
// Insert log in DB |
2384
|
|
|
return DB::query( |
2385
|
|
|
'SELECT ' . $fields . ' |
2386
|
|
|
FROM ' . prefixTable($table) |
2387
|
|
|
); |
2388
|
|
|
} |
2389
|
|
|
|
2390
|
|
|
/** |
2391
|
|
|
* Undocumented function. |
2392
|
|
|
* |
2393
|
|
|
* @param int $bytes Size of file |
2394
|
|
|
* |
2395
|
|
|
* @return string |
2396
|
|
|
*/ |
2397
|
|
|
function formatSizeUnits(int $bytes): string |
2398
|
|
|
{ |
2399
|
|
|
if ($bytes >= 1073741824) { |
2400
|
|
|
$bytes = number_format($bytes / 1073741824, 2) . ' GB'; |
2401
|
|
|
} elseif ($bytes >= 1048576) { |
2402
|
|
|
$bytes = number_format($bytes / 1048576, 2) . ' MB'; |
2403
|
|
|
} elseif ($bytes >= 1024) { |
2404
|
|
|
$bytes = number_format($bytes / 1024, 2) . ' KB'; |
2405
|
|
|
} elseif ($bytes > 1) { |
2406
|
|
|
$bytes .= ' bytes'; |
2407
|
|
|
} elseif ($bytes === 1) { |
2408
|
|
|
$bytes .= ' byte'; |
2409
|
|
|
} else { |
2410
|
|
|
$bytes = '0 bytes'; |
2411
|
|
|
} |
2412
|
|
|
|
2413
|
|
|
return $bytes; |
2414
|
|
|
} |
2415
|
|
|
|
2416
|
|
|
/** |
2417
|
|
|
* Generate user pair of keys. |
2418
|
|
|
* |
2419
|
|
|
* @param string $userPwd User password |
2420
|
|
|
* |
2421
|
|
|
* @return array |
2422
|
|
|
*/ |
2423
|
|
|
function generateUserKeys(string $userPwd): array |
2424
|
|
|
{ |
2425
|
|
|
//if (WIP === false) { |
2426
|
|
|
// Load classes |
2427
|
|
|
$rsa = new Crypt_RSA(); |
2428
|
|
|
$cipher = new Crypt_AES(); |
2429
|
|
|
// Create the private and public key |
2430
|
|
|
$res = $rsa->createKey(4096); |
2431
|
|
|
// Encrypt the privatekey |
2432
|
|
|
$cipher->setPassword($userPwd); |
2433
|
|
|
$privatekey = $cipher->encrypt($res['privatekey']); |
2434
|
|
|
return [ |
2435
|
|
|
'private_key' => base64_encode($privatekey), |
2436
|
|
|
'public_key' => base64_encode($res['publickey']), |
2437
|
|
|
'private_key_clear' => base64_encode($res['privatekey']), |
2438
|
|
|
]; |
2439
|
|
|
/*} else { |
2440
|
|
|
// Create the keys |
2441
|
|
|
$keys = RSA::createKey(); |
2442
|
|
|
|
2443
|
|
|
return [ |
2444
|
|
|
'private_key' => base64_encode($keys->withPassword($userPwd)->toString('PKCS8')), |
2445
|
|
|
'public_key' => base64_encode($keys->getPublicKey()), |
2446
|
|
|
'private_key_clear' => base64_encode($keys->toString('PKCS8')), |
2447
|
|
|
]; |
2448
|
|
|
}*/ |
2449
|
|
|
} |
2450
|
|
|
|
2451
|
|
|
/** |
2452
|
|
|
* Permits to decrypt the user's privatekey. |
2453
|
|
|
* |
2454
|
|
|
* @param string $userPwd User password |
2455
|
|
|
* @param string $userPrivateKey User private key |
2456
|
|
|
* |
2457
|
|
|
* @return string|object |
2458
|
|
|
*/ |
2459
|
|
|
function decryptPrivateKey(string $userPwd, string $userPrivateKey) |
2460
|
|
|
{ |
2461
|
|
|
if (empty($userPwd) === false) { |
2462
|
|
|
//if (WIP === false) { |
2463
|
|
|
// Load classes |
2464
|
|
|
$cipher = new Crypt_AES(); |
2465
|
|
|
// Encrypt the privatekey |
2466
|
|
|
$cipher->setPassword($userPwd); |
2467
|
|
|
try { |
2468
|
|
|
return base64_encode((string) $cipher->decrypt(base64_decode($userPrivateKey))); |
2469
|
|
|
} catch (Exception $e) { |
2470
|
|
|
return $e; |
2471
|
|
|
} |
2472
|
|
|
/*} else { |
2473
|
|
|
//echo $userPrivateKey." ;; ".($userPwd)." ;;"; |
2474
|
|
|
// Load and decrypt the private key |
2475
|
|
|
try { |
2476
|
|
|
$privateKey = PublicKeyLoader::loadPrivateKey(base64_decode($userPrivateKey), $userPwd)->withHash('sha1')->withMGFHash('sha1'); |
2477
|
|
|
print_r($privateKey); |
2478
|
|
|
return base64_encode((string) $$privateKey); |
2479
|
|
|
} catch (NoKeyLoadedException $e) { |
2480
|
|
|
print_r($e); |
2481
|
|
|
return $e; |
2482
|
|
|
} |
2483
|
|
|
}*/ |
2484
|
|
|
} |
2485
|
|
|
return ''; |
2486
|
|
|
} |
2487
|
|
|
|
2488
|
|
|
/** |
2489
|
|
|
* Permits to encrypt the user's privatekey. |
2490
|
|
|
* |
2491
|
|
|
* @param string $userPwd User password |
2492
|
|
|
* @param string $userPrivateKey User private key |
2493
|
|
|
* |
2494
|
|
|
* @return string |
2495
|
|
|
*/ |
2496
|
|
|
function encryptPrivateKey(string $userPwd, string $userPrivateKey): string |
2497
|
|
|
{ |
2498
|
|
|
if (empty($userPwd) === false) { |
2499
|
|
|
//if (WIP === false) { |
2500
|
|
|
// Load classes |
2501
|
|
|
$cipher = new Crypt_AES(); |
2502
|
|
|
// Encrypt the privatekey |
2503
|
|
|
$cipher->setPassword($userPwd); |
2504
|
|
|
try { |
2505
|
|
|
return base64_encode($cipher->encrypt(base64_decode($userPrivateKey))); |
2506
|
|
|
} catch (Exception $e) { |
2507
|
|
|
return $e; |
2508
|
|
|
} |
2509
|
|
|
/*} else { |
2510
|
|
|
// Load the private key |
2511
|
|
|
$privateKey = PublicKeyLoader::load(base64_decode($userPrivateKey)); |
2512
|
|
|
|
2513
|
|
|
try { |
2514
|
|
|
return base64_encode($privateKey->withPassword($userPwd)); |
2515
|
|
|
} catch (Exception $e) { |
2516
|
|
|
return $e; |
2517
|
|
|
} |
2518
|
|
|
}*/ |
2519
|
|
|
} |
2520
|
|
|
return ''; |
2521
|
|
|
} |
2522
|
|
|
|
2523
|
|
|
/** |
2524
|
|
|
* Encrypts a string using AES. |
2525
|
|
|
* |
2526
|
|
|
* @param string $data String to encrypt |
2527
|
|
|
* @param string $key |
2528
|
|
|
* |
2529
|
|
|
* @return array |
2530
|
|
|
*/ |
2531
|
|
|
function doDataEncryption(string $data, string $key = NULL): array |
2532
|
|
|
{ |
2533
|
|
|
//if (WIP === false) { |
2534
|
|
|
// Load classes |
2535
|
|
|
$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC); |
2536
|
|
|
// Generate an object key |
2537
|
|
|
$objectKey = is_null($key) === true ? uniqidReal(KEY_LENGTH) : $key; |
2538
|
|
|
// Set it as password |
2539
|
|
|
$cipher->setPassword($objectKey); |
2540
|
|
|
return [ |
2541
|
|
|
'encrypted' => base64_encode($cipher->encrypt($data)), |
2542
|
|
|
'objectKey' => base64_encode($objectKey), |
2543
|
|
|
]; |
2544
|
|
|
/*} else { |
2545
|
|
|
|
2546
|
|
|
}*/ |
2547
|
|
|
} |
2548
|
|
|
|
2549
|
|
|
/** |
2550
|
|
|
* Decrypts a string using AES. |
2551
|
|
|
* |
2552
|
|
|
* @param string $data Encrypted data |
2553
|
|
|
* @param string $key Key to uncrypt |
2554
|
|
|
* |
2555
|
|
|
* @return string |
2556
|
|
|
*/ |
2557
|
|
|
function doDataDecryption(string $data, string $key): string |
2558
|
|
|
{ |
2559
|
|
|
//if (WIP === false) { |
2560
|
|
|
// Load classes |
2561
|
|
|
$cipher = new Crypt_AES(); |
2562
|
|
|
// Set the object key |
2563
|
|
|
$cipher->setPassword(base64_decode($key)); |
2564
|
|
|
return base64_encode((string) $cipher->decrypt(base64_decode($data))); |
2565
|
|
|
/*} else { |
2566
|
|
|
|
2567
|
|
|
}*/ |
2568
|
|
|
} |
2569
|
|
|
|
2570
|
|
|
/** |
2571
|
|
|
* Encrypts using RSA a string using a public key. |
2572
|
|
|
* |
2573
|
|
|
* @param string $key Key to be encrypted |
2574
|
|
|
* @param string $publicKey User public key |
2575
|
|
|
* |
2576
|
|
|
* @return string |
2577
|
|
|
*/ |
2578
|
|
|
function encryptUserObjectKey(string $key, string $publicKey): string |
2579
|
|
|
{ |
2580
|
|
|
//if (WIP === false) { |
2581
|
|
|
// Load classes |
2582
|
|
|
$rsa = new Crypt_RSA(); |
2583
|
|
|
$rsa->loadKey(base64_decode($publicKey)); |
2584
|
|
|
// Encrypt |
2585
|
|
|
return base64_encode($rsa->encrypt(base64_decode($key))); |
2586
|
|
|
/*} else { |
2587
|
|
|
|
2588
|
|
|
}*/ |
2589
|
|
|
} |
2590
|
|
|
|
2591
|
|
|
/** |
2592
|
|
|
* Decrypts using RSA an encrypted string using a private key. |
2593
|
|
|
* |
2594
|
|
|
* @param string $key Encrypted key |
2595
|
|
|
* @param string $privateKey User private key |
2596
|
|
|
* |
2597
|
|
|
* @return string |
2598
|
|
|
*/ |
2599
|
|
|
function decryptUserObjectKey(string $key, string $privateKey): string |
2600
|
|
|
{ |
2601
|
|
|
//if (WIP === false) { |
2602
|
|
|
// Load classes |
2603
|
|
|
$rsa = new Crypt_RSA(); |
2604
|
|
|
$rsa->loadKey(base64_decode($privateKey)); |
2605
|
|
|
// Decrypt |
2606
|
|
|
try { |
2607
|
|
|
$tmpValue = $rsa->decrypt(base64_decode($key)); |
2608
|
|
|
if (is_bool($tmpValue) === false) { |
|
|
|
|
2609
|
|
|
$ret = base64_encode((string) /** @scrutinizer ignore-type */$tmpValue); |
2610
|
|
|
} else { |
2611
|
|
|
$ret = ''; |
2612
|
|
|
} |
2613
|
|
|
} catch (Exception $e) { |
2614
|
|
|
return $e; |
2615
|
|
|
} |
2616
|
|
|
/*} else { |
2617
|
|
|
|
2618
|
|
|
}*/ |
2619
|
|
|
|
2620
|
|
|
return $ret; |
2621
|
|
|
} |
2622
|
|
|
|
2623
|
|
|
/** |
2624
|
|
|
* Encrypts a file. |
2625
|
|
|
* |
2626
|
|
|
* @param string $fileInName File name |
2627
|
|
|
* @param string $fileInPath Path to file |
2628
|
|
|
* |
2629
|
|
|
* @return array |
2630
|
|
|
*/ |
2631
|
|
|
function encryptFile(string $fileInName, string $fileInPath): array |
2632
|
|
|
{ |
2633
|
|
|
if (defined('FILE_BUFFER_SIZE') === false) { |
2634
|
|
|
define('FILE_BUFFER_SIZE', 128 * 1024); |
2635
|
|
|
} |
2636
|
|
|
//if (WIP === false) { |
2637
|
|
|
// Load classes |
2638
|
|
|
$cipher = new Crypt_AES(); |
2639
|
|
|
// Generate an object key |
2640
|
|
|
$objectKey = uniqidReal(32); |
2641
|
|
|
// Set it as password |
2642
|
|
|
$cipher->setPassword($objectKey); |
2643
|
|
|
// Prevent against out of memory |
2644
|
|
|
$cipher->enableContinuousBuffer(); |
2645
|
|
|
//$cipher->disablePadding(); |
2646
|
|
|
|
2647
|
|
|
// Encrypt the file content |
2648
|
|
|
$plaintext = file_get_contents( |
2649
|
|
|
filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL) |
2650
|
|
|
); |
2651
|
|
|
$ciphertext = $cipher->encrypt($plaintext); |
2652
|
|
|
// Save new file |
2653
|
|
|
$hash = md5($plaintext); |
2654
|
|
|
$fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash; |
2655
|
|
|
file_put_contents($fileOut, $ciphertext); |
2656
|
|
|
unlink($fileInPath . '/' . $fileInName); |
2657
|
|
|
return [ |
2658
|
|
|
'fileHash' => base64_encode($hash), |
2659
|
|
|
'objectKey' => base64_encode($objectKey), |
2660
|
|
|
]; |
2661
|
|
|
/*} else { |
2662
|
|
|
|
2663
|
|
|
}*/ |
2664
|
|
|
} |
2665
|
|
|
|
2666
|
|
|
/** |
2667
|
|
|
* Decrypt a file. |
2668
|
|
|
* |
2669
|
|
|
* @param string $fileName File name |
2670
|
|
|
* @param string $filePath Path to file |
2671
|
|
|
* @param string $key Key to use |
2672
|
|
|
* |
2673
|
|
|
* @return string |
2674
|
|
|
*/ |
2675
|
|
|
function decryptFile(string $fileName, string $filePath, string $key): string |
2676
|
|
|
{ |
2677
|
|
|
if (! defined('FILE_BUFFER_SIZE')) { |
2678
|
|
|
define('FILE_BUFFER_SIZE', 128 * 1024); |
2679
|
|
|
} |
2680
|
|
|
|
2681
|
|
|
// Get file name |
2682
|
|
|
$fileName = base64_decode($fileName); |
2683
|
|
|
|
2684
|
|
|
//if (WIP === false) { |
2685
|
|
|
// Load classes |
2686
|
|
|
$cipher = new Crypt_AES(); |
2687
|
|
|
// Set the object key |
2688
|
|
|
$cipher->setPassword(base64_decode($key)); |
2689
|
|
|
// Prevent against out of memory |
2690
|
|
|
$cipher->enableContinuousBuffer(); |
2691
|
|
|
$cipher->disablePadding(); |
2692
|
|
|
// Get file content |
2693
|
|
|
$ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName); |
2694
|
|
|
// Decrypt file content and return |
2695
|
|
|
return base64_encode($cipher->decrypt($ciphertext)); |
2696
|
|
|
/*} else { |
2697
|
|
|
|
2698
|
|
|
}*/ |
2699
|
|
|
} |
2700
|
|
|
|
2701
|
|
|
/** |
2702
|
|
|
* Generate a simple password |
2703
|
|
|
* |
2704
|
|
|
* @param int $length Length of string |
2705
|
|
|
* @param bool $symbolsincluded Allow symbols |
2706
|
|
|
* |
2707
|
|
|
* @return string |
2708
|
|
|
*/ |
2709
|
|
|
function generateQuickPassword(int $length = 16, bool $symbolsincluded = true): string |
2710
|
|
|
{ |
2711
|
|
|
// Generate new user password |
2712
|
|
|
$small_letters = range('a', 'z'); |
2713
|
|
|
$big_letters = range('A', 'Z'); |
2714
|
|
|
$digits = range(0, 9); |
2715
|
|
|
$symbols = $symbolsincluded === true ? |
2716
|
|
|
['#', '_', '-', '@', '$', '+', '!'] : []; |
2717
|
|
|
$res = array_merge($small_letters, $big_letters, $digits, $symbols); |
2718
|
|
|
$count = count($res); |
2719
|
|
|
// first variant |
2720
|
|
|
|
2721
|
|
|
$random_string = ''; |
2722
|
|
|
for ($i = 0; $i < $length; ++$i) { |
2723
|
|
|
$random_string .= $res[random_int(0, $count - 1)]; |
2724
|
|
|
} |
2725
|
|
|
|
2726
|
|
|
return $random_string; |
2727
|
|
|
} |
2728
|
|
|
|
2729
|
|
|
/** |
2730
|
|
|
* Permit to store the sharekey of an object for users. |
2731
|
|
|
* |
2732
|
|
|
* @param string $object_name Type for table selection |
2733
|
|
|
* @param int $post_folder_is_personal Personal |
2734
|
|
|
* @param int $post_folder_id Folder |
2735
|
|
|
* @param int $post_object_id Object |
2736
|
|
|
* @param string $objectKey Object key |
2737
|
|
|
* @param array $SETTINGS Teampass settings |
2738
|
|
|
* @param int $user_id User ID if needed |
2739
|
|
|
* @param bool $onlyForUser User ID if needed |
2740
|
|
|
* @param bool $deleteAll User ID if needed |
2741
|
|
|
* @param array $objectKeyArray User ID if needed |
2742
|
|
|
* |
2743
|
|
|
* @return void |
2744
|
|
|
*/ |
2745
|
|
|
function storeUsersShareKey( |
2746
|
|
|
string $object_name, |
2747
|
|
|
int $post_folder_is_personal, |
2748
|
|
|
int $post_folder_id, |
2749
|
|
|
int $post_object_id, |
2750
|
|
|
string $objectKey, |
2751
|
|
|
array $SETTINGS, |
2752
|
|
|
bool $onlyForUser = false, |
2753
|
|
|
bool $deleteAll = true, |
2754
|
|
|
array $objectKeyArray = [] |
2755
|
|
|
): void { |
2756
|
|
|
|
2757
|
|
|
$session = SessionManager::getSession(); |
2758
|
|
|
loadClasses('DB'); |
2759
|
|
|
|
2760
|
|
|
// Delete existing entries for this object |
2761
|
|
|
if ($deleteAll === true) { |
2762
|
|
|
DB::delete( |
2763
|
|
|
$object_name, |
2764
|
|
|
'object_id = %i', |
2765
|
|
|
$post_object_id |
2766
|
|
|
); |
2767
|
|
|
} |
2768
|
|
|
|
2769
|
|
|
if ( |
2770
|
|
|
$onlyForUser === true || (int) $post_folder_is_personal === 1 |
2771
|
|
|
) { |
2772
|
|
|
// Only create the sharekey for a user |
2773
|
|
|
$user = DB::queryFirstRow( |
2774
|
|
|
'SELECT public_key |
2775
|
|
|
FROM ' . prefixTable('users') . ' |
2776
|
|
|
WHERE id = ' . (int) $session->get('user-id') . ' |
2777
|
|
|
AND public_key != ""' |
2778
|
|
|
); |
2779
|
|
|
|
2780
|
|
|
if (empty($objectKey) === false) { |
2781
|
|
|
DB::insert( |
2782
|
|
|
$object_name, |
2783
|
|
|
[ |
2784
|
|
|
'object_id' => (int) $post_object_id, |
2785
|
|
|
'user_id' => (int) $session->get('user-id'), |
2786
|
|
|
'share_key' => encryptUserObjectKey( |
2787
|
|
|
$objectKey, |
2788
|
|
|
$user['public_key'] |
2789
|
|
|
), |
2790
|
|
|
] |
2791
|
|
|
); |
2792
|
|
|
} else if (count($objectKeyArray) > 0) { |
2793
|
|
|
foreach ($objectKeyArray as $object) { |
2794
|
|
|
DB::insert( |
2795
|
|
|
$object_name, |
2796
|
|
|
[ |
2797
|
|
|
'object_id' => (int) $object['objectId'], |
2798
|
|
|
'user_id' => (int) $session->get('user-id'), |
2799
|
|
|
'share_key' => encryptUserObjectKey( |
2800
|
|
|
$object['objectKey'], |
2801
|
|
|
$user['public_key'] |
2802
|
|
|
), |
2803
|
|
|
] |
2804
|
|
|
); |
2805
|
|
|
} |
2806
|
|
|
} |
2807
|
|
|
} else { |
2808
|
|
|
// Create sharekey for each user |
2809
|
|
|
//DB::debugmode(true); |
2810
|
|
|
$users = DB::query( |
2811
|
|
|
'SELECT id, public_key |
2812
|
|
|
FROM ' . prefixTable('users') . ' |
2813
|
|
|
WHERE ' . ($onlyForUser === true ? |
|
|
|
|
2814
|
|
|
'id IN ("' . TP_USER_ID . '","' . $session->get('user-id') . '") ' : |
2815
|
|
|
'id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '") ') . ' |
2816
|
|
|
AND public_key != ""' |
2817
|
|
|
); |
2818
|
|
|
//DB::debugmode(false); |
2819
|
|
|
foreach ($users as $user) { |
2820
|
|
|
// Insert in DB the new object key for this item by user |
2821
|
|
|
if (count($objectKeyArray) === 0) { |
2822
|
|
|
DB::insert( |
2823
|
|
|
$object_name, |
2824
|
|
|
[ |
2825
|
|
|
'object_id' => $post_object_id, |
2826
|
|
|
'user_id' => (int) $user['id'], |
2827
|
|
|
'share_key' => encryptUserObjectKey( |
2828
|
|
|
$objectKey, |
2829
|
|
|
$user['public_key'] |
2830
|
|
|
), |
2831
|
|
|
] |
2832
|
|
|
); |
2833
|
|
|
} else { |
2834
|
|
|
foreach ($objectKeyArray as $object) { |
2835
|
|
|
DB::insert( |
2836
|
|
|
$object_name, |
2837
|
|
|
[ |
2838
|
|
|
'object_id' => (int) $object['objectId'], |
2839
|
|
|
'user_id' => (int) $user['id'], |
2840
|
|
|
'share_key' => encryptUserObjectKey( |
2841
|
|
|
$object['objectKey'], |
2842
|
|
|
$user['public_key'] |
2843
|
|
|
), |
2844
|
|
|
] |
2845
|
|
|
); |
2846
|
|
|
} |
2847
|
|
|
} |
2848
|
|
|
} |
2849
|
|
|
} |
2850
|
|
|
} |
2851
|
|
|
|
2852
|
|
|
/** |
2853
|
|
|
* Is this string base64 encoded? |
2854
|
|
|
* |
2855
|
|
|
* @param string $str Encoded string? |
2856
|
|
|
* |
2857
|
|
|
* @return bool |
2858
|
|
|
*/ |
2859
|
|
|
function isBase64(string $str): bool |
2860
|
|
|
{ |
2861
|
|
|
$str = (string) trim($str); |
2862
|
|
|
if (! isset($str[0])) { |
2863
|
|
|
return false; |
2864
|
|
|
} |
2865
|
|
|
|
2866
|
|
|
$base64String = (string) base64_decode($str, true); |
2867
|
|
|
if ($base64String && base64_encode($base64String) === $str) { |
2868
|
|
|
return true; |
2869
|
|
|
} |
2870
|
|
|
|
2871
|
|
|
return false; |
2872
|
|
|
} |
2873
|
|
|
|
2874
|
|
|
/** |
2875
|
|
|
* Undocumented function |
2876
|
|
|
* |
2877
|
|
|
* @param string $field Parameter |
2878
|
|
|
* |
2879
|
|
|
* @return array|bool|resource|string |
2880
|
|
|
*/ |
2881
|
|
|
function filterString(string $field) |
2882
|
|
|
{ |
2883
|
|
|
// Sanitize string |
2884
|
|
|
$field = filter_var(trim($field), FILTER_SANITIZE_FULL_SPECIAL_CHARS); |
2885
|
|
|
if (empty($field) === false) { |
2886
|
|
|
// Load AntiXSS |
2887
|
|
|
$antiXss = new AntiXSS(); |
2888
|
|
|
// Return |
2889
|
|
|
return $antiXss->xss_clean($field); |
2890
|
|
|
} |
2891
|
|
|
|
2892
|
|
|
return false; |
2893
|
|
|
} |
2894
|
|
|
|
2895
|
|
|
/** |
2896
|
|
|
* CHeck if provided credentials are allowed on server |
2897
|
|
|
* |
2898
|
|
|
* @param string $login User Login |
2899
|
|
|
* @param string $password User Pwd |
2900
|
|
|
* @param array $SETTINGS Teampass settings |
2901
|
|
|
* |
2902
|
|
|
* @return bool |
2903
|
|
|
*/ |
2904
|
|
|
function ldapCheckUserPassword(string $login, string $password, array $SETTINGS): bool |
2905
|
|
|
{ |
2906
|
|
|
// Build ldap configuration array |
2907
|
|
|
$config = [ |
2908
|
|
|
// Mandatory Configuration Options |
2909
|
|
|
'hosts' => [$SETTINGS['ldap_hosts']], |
2910
|
|
|
'base_dn' => $SETTINGS['ldap_bdn'], |
2911
|
|
|
'username' => $SETTINGS['ldap_username'], |
2912
|
|
|
'password' => $SETTINGS['ldap_password'], |
2913
|
|
|
|
2914
|
|
|
// Optional Configuration Options |
2915
|
|
|
'port' => $SETTINGS['ldap_port'], |
2916
|
|
|
'use_ssl' => (int) $SETTINGS['ldap_ssl'] === 1 ? true : false, |
2917
|
|
|
'use_tls' => (int) $SETTINGS['ldap_tls'] === 1 ? true : false, |
2918
|
|
|
'version' => 3, |
2919
|
|
|
'timeout' => 5, |
2920
|
|
|
'follow_referrals' => false, |
2921
|
|
|
|
2922
|
|
|
// Custom LDAP Options |
2923
|
|
|
'options' => [ |
2924
|
|
|
// See: http://php.net/ldap_set_option |
2925
|
|
|
LDAP_OPT_X_TLS_REQUIRE_CERT => (isset($SETTINGS['ldap_tls_certiface_check']) ? $SETTINGS['ldap_tls_certiface_check'] : LDAP_OPT_X_TLS_HARD), |
2926
|
|
|
], |
2927
|
|
|
]; |
2928
|
|
|
|
2929
|
|
|
$connection = new Connection($config); |
2930
|
|
|
// Connect to LDAP |
2931
|
|
|
try { |
2932
|
|
|
$connection->connect(); |
2933
|
|
|
} catch (\LdapRecord\Auth\BindException $e) { |
2934
|
|
|
$error = $e->getDetailedError(); |
2935
|
|
|
echo 'Error : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(); |
2936
|
|
|
return false; |
2937
|
|
|
} |
2938
|
|
|
|
2939
|
|
|
// Authenticate user |
2940
|
|
|
try { |
2941
|
|
|
if ($SETTINGS['ldap_type'] === 'ActiveDirectory') { |
2942
|
|
|
$connection->auth()->attempt($login, $password, $stayAuthenticated = true); |
2943
|
|
|
} else { |
2944
|
|
|
$connection->auth()->attempt($SETTINGS['ldap_user_attribute'].'='.$login.','.(isset($SETTINGS['ldap_dn_additional_user_dn']) && !empty($SETTINGS['ldap_dn_additional_user_dn']) ? $SETTINGS['ldap_dn_additional_user_dn'].',' : '').$SETTINGS['ldap_bdn'], $password, $stayAuthenticated = true); |
2945
|
|
|
} |
2946
|
|
|
} catch (\LdapRecord\Auth\BindException $e) { |
2947
|
|
|
$error = $e->getDetailedError(); |
2948
|
|
|
echo 'Error : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(); |
2949
|
|
|
return false; |
2950
|
|
|
} |
2951
|
|
|
|
2952
|
|
|
return true; |
2953
|
|
|
} |
2954
|
|
|
|
2955
|
|
|
/** |
2956
|
|
|
* Removes from DB all sharekeys of this user |
2957
|
|
|
* |
2958
|
|
|
* @param int $userId User's id |
2959
|
|
|
* @param array $SETTINGS Teampass settings |
2960
|
|
|
* |
2961
|
|
|
* @return bool |
2962
|
|
|
*/ |
2963
|
|
|
function deleteUserObjetsKeys(int $userId, array $SETTINGS = []): bool |
2964
|
|
|
{ |
2965
|
|
|
// Load class DB |
2966
|
|
|
loadClasses('DB'); |
2967
|
|
|
|
2968
|
|
|
// Remove all item sharekeys items |
2969
|
|
|
// expect if personal item |
2970
|
|
|
DB::delete( |
2971
|
|
|
prefixTable('sharekeys_items'), |
2972
|
|
|
'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)', |
2973
|
|
|
$userId |
2974
|
|
|
); |
2975
|
|
|
// Remove all item sharekeys files |
2976
|
|
|
DB::delete( |
2977
|
|
|
prefixTable('sharekeys_files'), |
2978
|
|
|
'user_id = %i AND object_id NOT IN ( |
2979
|
|
|
SELECT f.id |
2980
|
|
|
FROM ' . prefixTable('items') . ' AS i |
2981
|
|
|
INNER JOIN ' . prefixTable('files') . ' AS f ON f.id_item = i.id |
2982
|
|
|
WHERE i.perso = 1 |
2983
|
|
|
)', |
2984
|
|
|
$userId |
2985
|
|
|
); |
2986
|
|
|
// Remove all item sharekeys fields |
2987
|
|
|
DB::delete( |
2988
|
|
|
prefixTable('sharekeys_fields'), |
2989
|
|
|
'user_id = %i AND object_id NOT IN ( |
2990
|
|
|
SELECT c.id |
2991
|
|
|
FROM ' . prefixTable('items') . ' AS i |
2992
|
|
|
INNER JOIN ' . prefixTable('categories_items') . ' AS c ON c.item_id = i.id |
2993
|
|
|
WHERE i.perso = 1 |
2994
|
|
|
)', |
2995
|
|
|
$userId |
2996
|
|
|
); |
2997
|
|
|
// Remove all item sharekeys logs |
2998
|
|
|
DB::delete( |
2999
|
|
|
prefixTable('sharekeys_logs'), |
3000
|
|
|
'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)', |
3001
|
|
|
$userId |
3002
|
|
|
); |
3003
|
|
|
// Remove all item sharekeys suggestions |
3004
|
|
|
DB::delete( |
3005
|
|
|
prefixTable('sharekeys_suggestions'), |
3006
|
|
|
'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)', |
3007
|
|
|
$userId |
3008
|
|
|
); |
3009
|
|
|
return false; |
3010
|
|
|
} |
3011
|
|
|
|
3012
|
|
|
/** |
3013
|
|
|
* Manage list of timezones $SETTINGS Teampass settings |
3014
|
|
|
* |
3015
|
|
|
* @return array |
3016
|
|
|
*/ |
3017
|
|
|
function timezone_list() |
3018
|
|
|
{ |
3019
|
|
|
static $timezones = null; |
3020
|
|
|
if ($timezones === null) { |
3021
|
|
|
$timezones = []; |
3022
|
|
|
$offsets = []; |
3023
|
|
|
$now = new DateTime('now', new DateTimeZone('UTC')); |
3024
|
|
|
foreach (DateTimeZone::listIdentifiers() as $timezone) { |
3025
|
|
|
$now->setTimezone(new DateTimeZone($timezone)); |
3026
|
|
|
$offsets[] = $offset = $now->getOffset(); |
3027
|
|
|
$timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone); |
3028
|
|
|
} |
3029
|
|
|
|
3030
|
|
|
array_multisort($offsets, $timezones); |
3031
|
|
|
} |
3032
|
|
|
|
3033
|
|
|
return $timezones; |
3034
|
|
|
} |
3035
|
|
|
|
3036
|
|
|
/** |
3037
|
|
|
* Provide timezone offset |
3038
|
|
|
* |
3039
|
|
|
* @param int $offset Timezone offset |
3040
|
|
|
* |
3041
|
|
|
* @return string |
3042
|
|
|
*/ |
3043
|
|
|
function format_GMT_offset($offset): string |
3044
|
|
|
{ |
3045
|
|
|
$hours = intval($offset / 3600); |
3046
|
|
|
$minutes = abs(intval($offset % 3600 / 60)); |
3047
|
|
|
return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : ''); |
3048
|
|
|
} |
3049
|
|
|
|
3050
|
|
|
/** |
3051
|
|
|
* Provides timezone name |
3052
|
|
|
* |
3053
|
|
|
* @param string $name Timezone name |
3054
|
|
|
* |
3055
|
|
|
* @return string |
3056
|
|
|
*/ |
3057
|
|
|
function format_timezone_name($name): string |
3058
|
|
|
{ |
3059
|
|
|
$name = str_replace('/', ', ', $name); |
3060
|
|
|
$name = str_replace('_', ' ', $name); |
3061
|
|
|
|
3062
|
|
|
return str_replace('St ', 'St. ', $name); |
3063
|
|
|
} |
3064
|
|
|
|
3065
|
|
|
/** |
3066
|
|
|
* Provides info if user should use MFA based on roles |
3067
|
|
|
* |
3068
|
|
|
* @param string $userRolesIds User roles ids |
3069
|
|
|
* @param string $mfaRoles Roles for which MFA is requested |
3070
|
|
|
* |
3071
|
|
|
* @return bool |
3072
|
|
|
*/ |
3073
|
|
|
function mfa_auth_requested_roles(string $userRolesIds, string $mfaRoles): bool |
3074
|
|
|
{ |
3075
|
|
|
if (empty($mfaRoles) === true) { |
3076
|
|
|
return true; |
3077
|
|
|
} |
3078
|
|
|
|
3079
|
|
|
$mfaRoles = array_values(json_decode($mfaRoles, true)); |
3080
|
|
|
$userRolesIds = array_filter(explode(';', $userRolesIds)); |
3081
|
|
|
if (count($mfaRoles) === 0 || count(array_intersect($mfaRoles, $userRolesIds)) > 0) { |
3082
|
|
|
return true; |
3083
|
|
|
} |
3084
|
|
|
|
3085
|
|
|
return false; |
3086
|
|
|
} |
3087
|
|
|
|
3088
|
|
|
/** |
3089
|
|
|
* Permits to clean a string for export purpose |
3090
|
|
|
* |
3091
|
|
|
* @param string $text |
3092
|
|
|
* @param bool $emptyCheckOnly |
3093
|
|
|
* |
3094
|
|
|
* @return string |
3095
|
|
|
*/ |
3096
|
|
|
function cleanStringForExport(string $text, bool $emptyCheckOnly = false): string |
3097
|
|
|
{ |
3098
|
|
|
if (is_null($text) === true || empty($text) === true) { |
3099
|
|
|
return ''; |
3100
|
|
|
} |
3101
|
|
|
// only expected to check if $text was empty |
3102
|
|
|
elseif ($emptyCheckOnly === true) { |
3103
|
|
|
return $text; |
3104
|
|
|
} |
3105
|
|
|
|
3106
|
|
|
return strip_tags( |
3107
|
|
|
cleanString( |
3108
|
|
|
html_entity_decode($text, ENT_QUOTES | ENT_XHTML, 'UTF-8'), |
3109
|
|
|
true) |
3110
|
|
|
); |
3111
|
|
|
} |
3112
|
|
|
|
3113
|
|
|
/** |
3114
|
|
|
* Permits to check if user ID is valid |
3115
|
|
|
* |
3116
|
|
|
* @param integer $post_user_id |
3117
|
|
|
* @return bool |
3118
|
|
|
*/ |
3119
|
|
|
function isUserIdValid($userId): bool |
3120
|
|
|
{ |
3121
|
|
|
if (is_null($userId) === false |
3122
|
|
|
&& isset($userId) === true |
3123
|
|
|
&& empty($userId) === false |
3124
|
|
|
) { |
3125
|
|
|
return true; |
3126
|
|
|
} |
3127
|
|
|
return false; |
3128
|
|
|
} |
3129
|
|
|
|
3130
|
|
|
/** |
3131
|
|
|
* Check if a key exists and if its value equal the one expected |
3132
|
|
|
* |
3133
|
|
|
* @param string $key |
3134
|
|
|
* @param integer|string $value |
3135
|
|
|
* @param array $array |
3136
|
|
|
* |
3137
|
|
|
* @return boolean |
3138
|
|
|
*/ |
3139
|
|
|
function isKeyExistingAndEqual( |
3140
|
|
|
string $key, |
3141
|
|
|
/*PHP8 - integer|string*/$value, |
3142
|
|
|
array $array |
3143
|
|
|
): bool |
3144
|
|
|
{ |
3145
|
|
|
if (isset($array[$key]) === true |
3146
|
|
|
&& (is_int($value) === true ? |
3147
|
|
|
(int) $array[$key] === $value : |
3148
|
|
|
(string) $array[$key] === $value) |
3149
|
|
|
) { |
3150
|
|
|
return true; |
3151
|
|
|
} |
3152
|
|
|
return false; |
3153
|
|
|
} |
3154
|
|
|
|
3155
|
|
|
/** |
3156
|
|
|
* Check if a variable is not set or equal to a value |
3157
|
|
|
* |
3158
|
|
|
* @param string|null $var |
3159
|
|
|
* @param integer|string $value |
3160
|
|
|
* |
3161
|
|
|
* @return boolean |
3162
|
|
|
*/ |
3163
|
|
|
function isKeyNotSetOrEqual( |
3164
|
|
|
/*PHP8 - string|null*/$var, |
3165
|
|
|
/*PHP8 - integer|string*/$value |
3166
|
|
|
): bool |
3167
|
|
|
{ |
3168
|
|
|
if (isset($var) === false |
3169
|
|
|
|| (is_int($value) === true ? |
3170
|
|
|
(int) $var === $value : |
3171
|
|
|
(string) $var === $value) |
3172
|
|
|
) { |
3173
|
|
|
return true; |
3174
|
|
|
} |
3175
|
|
|
return false; |
3176
|
|
|
} |
3177
|
|
|
|
3178
|
|
|
/** |
3179
|
|
|
* Check if a key exists and if its value < to the one expected |
3180
|
|
|
* |
3181
|
|
|
* @param string $key |
3182
|
|
|
* @param integer $value |
3183
|
|
|
* @param array $array |
3184
|
|
|
* |
3185
|
|
|
* @return boolean |
3186
|
|
|
*/ |
3187
|
|
|
function isKeyExistingAndInferior(string $key, int $value, array $array): bool |
3188
|
|
|
{ |
3189
|
|
|
if (isset($array[$key]) === true && (int) $array[$key] < $value) { |
3190
|
|
|
return true; |
3191
|
|
|
} |
3192
|
|
|
return false; |
3193
|
|
|
} |
3194
|
|
|
|
3195
|
|
|
/** |
3196
|
|
|
* Check if a key exists and if its value > to the one expected |
3197
|
|
|
* |
3198
|
|
|
* @param string $key |
3199
|
|
|
* @param integer $value |
3200
|
|
|
* @param array $array |
3201
|
|
|
* |
3202
|
|
|
* @return boolean |
3203
|
|
|
*/ |
3204
|
|
|
function isKeyExistingAndSuperior(string $key, int $value, array $array): bool |
3205
|
|
|
{ |
3206
|
|
|
if (isset($array[$key]) === true && (int) $array[$key] > $value) { |
3207
|
|
|
return true; |
3208
|
|
|
} |
3209
|
|
|
return false; |
3210
|
|
|
} |
3211
|
|
|
|
3212
|
|
|
/** |
3213
|
|
|
* Check if values in array are set |
3214
|
|
|
* Return true if all set |
3215
|
|
|
* Return false if one of them is not set |
3216
|
|
|
* |
3217
|
|
|
* @param array $arrayOfValues |
3218
|
|
|
* @return boolean |
3219
|
|
|
*/ |
3220
|
|
|
function isSetArrayOfValues(array $arrayOfValues): bool |
3221
|
|
|
{ |
3222
|
|
|
foreach($arrayOfValues as $value) { |
3223
|
|
|
if (isset($value) === false) { |
3224
|
|
|
return false; |
3225
|
|
|
} |
3226
|
|
|
} |
3227
|
|
|
return true; |
3228
|
|
|
} |
3229
|
|
|
|
3230
|
|
|
/** |
3231
|
|
|
* Check if values in array are set |
3232
|
|
|
* Return true if all set |
3233
|
|
|
* Return false if one of them is not set |
3234
|
|
|
* |
3235
|
|
|
* @param array $arrayOfValues |
3236
|
|
|
* @param integer|string $value |
3237
|
|
|
* @return boolean |
3238
|
|
|
*/ |
3239
|
|
|
function isArrayOfVarsEqualToValue( |
3240
|
|
|
array $arrayOfVars, |
3241
|
|
|
/*PHP8 - integer|string*/$value |
3242
|
|
|
) : bool |
3243
|
|
|
{ |
3244
|
|
|
foreach($arrayOfVars as $variable) { |
3245
|
|
|
if ($variable !== $value) { |
3246
|
|
|
return false; |
3247
|
|
|
} |
3248
|
|
|
} |
3249
|
|
|
return true; |
3250
|
|
|
} |
3251
|
|
|
|
3252
|
|
|
/** |
3253
|
|
|
* Checks if at least one variable in array is equal to value |
3254
|
|
|
* |
3255
|
|
|
* @param array $arrayOfValues |
3256
|
|
|
* @param integer|string $value |
3257
|
|
|
* @return boolean |
3258
|
|
|
*/ |
3259
|
|
|
function isOneVarOfArrayEqualToValue( |
3260
|
|
|
array $arrayOfVars, |
3261
|
|
|
/*PHP8 - integer|string*/$value |
3262
|
|
|
) : bool |
3263
|
|
|
{ |
3264
|
|
|
foreach($arrayOfVars as $variable) { |
3265
|
|
|
if ($variable === $value) { |
3266
|
|
|
return true; |
3267
|
|
|
} |
3268
|
|
|
} |
3269
|
|
|
return false; |
3270
|
|
|
} |
3271
|
|
|
|
3272
|
|
|
/** |
3273
|
|
|
* Checks is value is null, not set OR empty |
3274
|
|
|
* |
3275
|
|
|
* @param string|int|null $value |
3276
|
|
|
* @return boolean |
3277
|
|
|
*/ |
3278
|
|
|
function isValueSetNullEmpty(/*PHP8 - string|int|null*/ $value) : bool |
3279
|
|
|
{ |
3280
|
|
|
if (is_null($value) === true || isset($value) === false || empty($value) === true) { |
3281
|
|
|
return true; |
3282
|
|
|
} |
3283
|
|
|
return false; |
3284
|
|
|
} |
3285
|
|
|
|
3286
|
|
|
/** |
3287
|
|
|
* Checks if value is set and if empty is equal to passed boolean |
3288
|
|
|
* |
3289
|
|
|
* @param string|int $value |
3290
|
|
|
* @param boolean $boolean |
3291
|
|
|
* @return boolean |
3292
|
|
|
*/ |
3293
|
|
|
function isValueSetEmpty($value, $boolean = true) : bool |
3294
|
|
|
{ |
3295
|
|
|
if (isset($value) === true && empty($value) === $boolean) { |
3296
|
|
|
return true; |
3297
|
|
|
} |
3298
|
|
|
return false; |
3299
|
|
|
} |
3300
|
|
|
|
3301
|
|
|
/** |
3302
|
|
|
* Ensure Complexity is translated |
3303
|
|
|
* |
3304
|
|
|
* @return void |
3305
|
|
|
*/ |
3306
|
|
|
function defineComplexity() : void |
3307
|
|
|
{ |
3308
|
|
|
// Load user's language |
3309
|
|
|
$lang = new Language(); |
3310
|
|
|
|
3311
|
|
|
if (defined('TP_PW_COMPLEXITY') === false) { |
3312
|
|
|
define( |
3313
|
|
|
'TP_PW_COMPLEXITY', |
3314
|
|
|
[ |
3315
|
|
|
TP_PW_STRENGTH_1 => array(TP_PW_STRENGTH_1, $lang->get('complex_level1'), 'fas fa-thermometer-empty text-danger'), |
3316
|
|
|
TP_PW_STRENGTH_2 => array(TP_PW_STRENGTH_2, $lang->get('complex_level2'), 'fas fa-thermometer-quarter text-warning'), |
3317
|
|
|
TP_PW_STRENGTH_3 => array(TP_PW_STRENGTH_3, $lang->get('complex_level3'), 'fas fa-thermometer-half text-warning'), |
3318
|
|
|
TP_PW_STRENGTH_4 => array(TP_PW_STRENGTH_4, $lang->get('complex_level4'), 'fas fa-thermometer-three-quarters text-success'), |
3319
|
|
|
TP_PW_STRENGTH_5 => array(TP_PW_STRENGTH_5, $lang->get('complex_level5'), 'fas fa-thermometer-full text-success'), |
3320
|
|
|
] |
3321
|
|
|
); |
3322
|
|
|
} |
3323
|
|
|
} |
3324
|
|
|
|
3325
|
|
|
/** |
3326
|
|
|
* Uses Sanitizer to perform data sanitization |
3327
|
|
|
* |
3328
|
|
|
* @param array $data |
3329
|
|
|
* @param array $filters |
3330
|
|
|
* @return array|string |
3331
|
|
|
*/ |
3332
|
|
|
function dataSanitizer(array $data, array $filters): array|string |
3333
|
|
|
{ |
3334
|
|
|
// Load Sanitizer library |
3335
|
|
|
$sanitizer = new Sanitizer($data, $filters); |
3336
|
|
|
|
3337
|
|
|
// Load AntiXSS |
3338
|
|
|
$antiXss = new AntiXSS(); |
3339
|
|
|
|
3340
|
|
|
// Sanitize post and get variables |
3341
|
|
|
return $antiXss->xss_clean($sanitizer->sanitize()); |
3342
|
|
|
} |
3343
|
|
|
|
3344
|
|
|
/** |
3345
|
|
|
* Permits to manage the cache tree for a user |
3346
|
|
|
* |
3347
|
|
|
* @param integer $user_id |
3348
|
|
|
* @param string $data |
3349
|
|
|
* @param array $SETTINGS |
3350
|
|
|
* @param string $field_update |
3351
|
|
|
* @return void |
3352
|
|
|
*/ |
3353
|
|
|
function cacheTreeUserHandler(int $user_id, string $data, array $SETTINGS, string $field_update = '') |
3354
|
|
|
{ |
3355
|
|
|
// Load class DB |
3356
|
|
|
loadClasses('DB'); |
3357
|
|
|
|
3358
|
|
|
// Exists ? |
3359
|
|
|
$userCacheId = DB::queryfirstrow( |
3360
|
|
|
'SELECT increment_id |
3361
|
|
|
FROM ' . prefixTable('cache_tree') . ' |
3362
|
|
|
WHERE user_id = %i', |
3363
|
|
|
$user_id |
3364
|
|
|
); |
3365
|
|
|
|
3366
|
|
|
if (is_null($userCacheId) === true || count($userCacheId) === 0) { |
3367
|
|
|
// insert in table |
3368
|
|
|
DB::insert( |
3369
|
|
|
prefixTable('cache_tree'), |
3370
|
|
|
array( |
3371
|
|
|
'data' => $data, |
3372
|
|
|
'timestamp' => time(), |
3373
|
|
|
'user_id' => $user_id, |
3374
|
|
|
'visible_folders' => '', |
3375
|
|
|
) |
3376
|
|
|
); |
3377
|
|
|
} else { |
3378
|
|
|
if (empty($field_update) === true) { |
3379
|
|
|
DB::update( |
3380
|
|
|
prefixTable('cache_tree'), |
3381
|
|
|
[ |
3382
|
|
|
'timestamp' => time(), |
3383
|
|
|
'data' => $data, |
3384
|
|
|
], |
3385
|
|
|
'increment_id = %i', |
3386
|
|
|
$userCacheId['increment_id'] |
3387
|
|
|
); |
3388
|
|
|
/* USELESS |
3389
|
|
|
} else { |
3390
|
|
|
DB::update( |
3391
|
|
|
prefixTable('cache_tree'), |
3392
|
|
|
[ |
3393
|
|
|
$field_update => $data, |
3394
|
|
|
], |
3395
|
|
|
'increment_id = %i', |
3396
|
|
|
$userCacheId['increment_id'] |
3397
|
|
|
);*/ |
3398
|
|
|
} |
3399
|
|
|
} |
3400
|
|
|
} |
3401
|
|
|
|
3402
|
|
|
/** |
3403
|
|
|
* Permits to calculate a % |
3404
|
|
|
* |
3405
|
|
|
* @param float $nombre |
3406
|
|
|
* @param float $total |
3407
|
|
|
* @param float $pourcentage |
3408
|
|
|
* @return float |
3409
|
|
|
*/ |
3410
|
|
|
function pourcentage(float $nombre, float $total, float $pourcentage): float |
3411
|
|
|
{ |
3412
|
|
|
$resultat = ($nombre/$total) * $pourcentage; |
3413
|
|
|
return round($resultat); |
3414
|
|
|
} |
3415
|
|
|
|
3416
|
|
|
/** |
3417
|
|
|
* Load the folders list from the cache |
3418
|
|
|
* |
3419
|
|
|
* @param string $fieldName |
3420
|
|
|
* @param string $sessionName |
3421
|
|
|
* @param boolean $forceRefresh |
3422
|
|
|
* @return array |
3423
|
|
|
*/ |
3424
|
|
|
function loadFoldersListByCache( |
3425
|
|
|
string $fieldName, |
3426
|
|
|
string $sessionName, |
3427
|
|
|
bool $forceRefresh = false |
3428
|
|
|
): array |
3429
|
|
|
{ |
3430
|
|
|
// Case when refresh is EXPECTED / MANDATORY |
3431
|
|
|
if ($forceRefresh === true) { |
3432
|
|
|
return [ |
3433
|
|
|
'state' => false, |
3434
|
|
|
'data' => [], |
3435
|
|
|
]; |
3436
|
|
|
} |
3437
|
|
|
|
3438
|
|
|
$session = SessionManager::getSession(); |
3439
|
|
|
|
3440
|
|
|
// Get last folder update |
3441
|
|
|
$lastFolderChange = DB::queryfirstrow( |
3442
|
|
|
'SELECT valeur FROM ' . prefixTable('misc') . ' |
3443
|
|
|
WHERE type = %s AND intitule = %s', |
3444
|
|
|
'timestamp', |
3445
|
|
|
'last_folder_change' |
3446
|
|
|
); |
3447
|
|
|
if (DB::count() === 0) { |
3448
|
|
|
$lastFolderChange['valeur'] = 0; |
3449
|
|
|
} |
3450
|
|
|
|
3451
|
|
|
// Case when an update in the tree has been done |
3452
|
|
|
// Refresh is then mandatory |
3453
|
|
|
if ((int) $lastFolderChange['valeur'] > (int) (null !== $session->get('user-tree_last_refresh_timestamp') ? $session->get('user-tree_last_refresh_timestamp') : 0)) { |
3454
|
|
|
return [ |
3455
|
|
|
'state' => false, |
3456
|
|
|
'data' => [], |
3457
|
|
|
]; |
3458
|
|
|
} |
3459
|
|
|
|
3460
|
|
|
// Does this user has the tree structure in session? |
3461
|
|
|
// If yes then use it |
3462
|
|
|
if (count(null !== $session->get('user-folders_list') ? $session->get('user-folders_list') : []) > 0) { |
3463
|
|
|
return [ |
3464
|
|
|
'state' => true, |
3465
|
|
|
'data' => json_encode($session->get('user-folders_list')), |
3466
|
|
|
]; |
3467
|
|
|
} |
3468
|
|
|
|
3469
|
|
|
// Does this user has a tree cache |
3470
|
|
|
$userCacheTree = DB::queryfirstrow( |
3471
|
|
|
'SELECT '.$fieldName.' |
3472
|
|
|
FROM ' . prefixTable('cache_tree') . ' |
3473
|
|
|
WHERE user_id = %i', |
3474
|
|
|
$session->get('user-id') |
3475
|
|
|
); |
3476
|
|
|
if (empty($userCacheTree[$fieldName]) === false && $userCacheTree[$fieldName] !== '[]') { |
3477
|
|
|
SessionManager::addRemoveFromSessionAssociativeArray( |
3478
|
|
|
'user-folders_list', |
3479
|
|
|
[$userCacheTree[$fieldName]], |
3480
|
|
|
'add' |
3481
|
|
|
); |
3482
|
|
|
return [ |
3483
|
|
|
'state' => true, |
3484
|
|
|
'data' => $userCacheTree[$fieldName], |
3485
|
|
|
]; |
3486
|
|
|
} |
3487
|
|
|
|
3488
|
|
|
return [ |
3489
|
|
|
'state' => false, |
3490
|
|
|
'data' => [], |
3491
|
|
|
]; |
3492
|
|
|
} |
3493
|
|
|
|
3494
|
|
|
|
3495
|
|
|
/** |
3496
|
|
|
* Permits to refresh the categories of folders |
3497
|
|
|
* |
3498
|
|
|
* @param array $folderIds |
3499
|
|
|
* @return void |
3500
|
|
|
*/ |
3501
|
|
|
function handleFoldersCategories( |
3502
|
|
|
array $folderIds |
3503
|
|
|
) |
3504
|
|
|
{ |
3505
|
|
|
// Load class DB |
3506
|
|
|
loadClasses('DB'); |
3507
|
|
|
|
3508
|
|
|
$arr_data = array(); |
3509
|
|
|
|
3510
|
|
|
// force full list of folders |
3511
|
|
|
if (count($folderIds) === 0) { |
3512
|
|
|
$folderIds = DB::queryFirstColumn( |
3513
|
|
|
'SELECT id |
3514
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
3515
|
|
|
WHERE personal_folder=%i', |
3516
|
|
|
0 |
3517
|
|
|
); |
3518
|
|
|
} |
3519
|
|
|
|
3520
|
|
|
// Get complexity |
3521
|
|
|
defineComplexity(); |
3522
|
|
|
|
3523
|
|
|
// update |
3524
|
|
|
foreach ($folderIds as $folder) { |
3525
|
|
|
// Do we have Categories |
3526
|
|
|
// get list of associated Categories |
3527
|
|
|
$arrCatList = array(); |
3528
|
|
|
$rows_tmp = DB::query( |
3529
|
|
|
'SELECT c.id, c.title, c.level, c.type, c.masked, c.order, c.encrypted_data, c.role_visibility, c.is_mandatory, |
3530
|
|
|
f.id_category AS category_id |
3531
|
|
|
FROM ' . prefixTable('categories_folders') . ' AS f |
3532
|
|
|
INNER JOIN ' . prefixTable('categories') . ' AS c ON (f.id_category = c.parent_id) |
3533
|
|
|
WHERE id_folder=%i', |
3534
|
|
|
$folder |
3535
|
|
|
); |
3536
|
|
|
if (DB::count() > 0) { |
3537
|
|
|
foreach ($rows_tmp as $row) { |
3538
|
|
|
$arrCatList[$row['id']] = array( |
3539
|
|
|
'id' => $row['id'], |
3540
|
|
|
'title' => $row['title'], |
3541
|
|
|
'level' => $row['level'], |
3542
|
|
|
'type' => $row['type'], |
3543
|
|
|
'masked' => $row['masked'], |
3544
|
|
|
'order' => $row['order'], |
3545
|
|
|
'encrypted_data' => $row['encrypted_data'], |
3546
|
|
|
'role_visibility' => $row['role_visibility'], |
3547
|
|
|
'is_mandatory' => $row['is_mandatory'], |
3548
|
|
|
'category_id' => $row['category_id'], |
3549
|
|
|
); |
3550
|
|
|
} |
3551
|
|
|
} |
3552
|
|
|
$arr_data['categories'] = $arrCatList; |
3553
|
|
|
|
3554
|
|
|
// Now get complexity |
3555
|
|
|
$valTemp = ''; |
3556
|
|
|
$data = DB::queryFirstRow( |
3557
|
|
|
'SELECT valeur |
3558
|
|
|
FROM ' . prefixTable('misc') . ' |
3559
|
|
|
WHERE type = %s AND intitule=%i', |
3560
|
|
|
'complex', |
3561
|
|
|
$folder |
3562
|
|
|
); |
3563
|
|
|
if (DB::count() > 0 && empty($data['valeur']) === false) { |
3564
|
|
|
$valTemp = array( |
3565
|
|
|
'value' => $data['valeur'], |
3566
|
|
|
'text' => TP_PW_COMPLEXITY[$data['valeur']][1], |
3567
|
|
|
); |
3568
|
|
|
} |
3569
|
|
|
$arr_data['complexity'] = $valTemp; |
3570
|
|
|
|
3571
|
|
|
// Now get Roles |
3572
|
|
|
$valTemp = ''; |
3573
|
|
|
$rows_tmp = DB::query( |
3574
|
|
|
'SELECT t.title |
3575
|
|
|
FROM ' . prefixTable('roles_values') . ' as v |
3576
|
|
|
INNER JOIN ' . prefixTable('roles_title') . ' as t ON (v.role_id = t.id) |
3577
|
|
|
WHERE v.folder_id = %i |
3578
|
|
|
GROUP BY title', |
3579
|
|
|
$folder |
3580
|
|
|
); |
3581
|
|
|
foreach ($rows_tmp as $record) { |
3582
|
|
|
$valTemp .= (empty($valTemp) === true ? '' : ' - ') . $record['title']; |
3583
|
|
|
} |
3584
|
|
|
$arr_data['visibilityRoles'] = $valTemp; |
3585
|
|
|
|
3586
|
|
|
// now save in DB |
3587
|
|
|
DB::update( |
3588
|
|
|
prefixTable('nested_tree'), |
3589
|
|
|
array( |
3590
|
|
|
'categories' => json_encode($arr_data), |
3591
|
|
|
), |
3592
|
|
|
'id = %i', |
3593
|
|
|
$folder |
3594
|
|
|
); |
3595
|
|
|
} |
3596
|
|
|
} |
3597
|
|
|
|
3598
|
|
|
/** |
3599
|
|
|
* List all users that have specific roles |
3600
|
|
|
* |
3601
|
|
|
* @param array $roles |
3602
|
|
|
* @return array |
3603
|
|
|
*/ |
3604
|
|
|
function getUsersWithRoles( |
3605
|
|
|
array $roles |
3606
|
|
|
): array |
3607
|
|
|
{ |
3608
|
|
|
$session = SessionManager::getSession(); |
3609
|
|
|
$arrUsers = array(); |
3610
|
|
|
|
3611
|
|
|
foreach ($roles as $role) { |
3612
|
|
|
// loop on users and check if user has this role |
3613
|
|
|
$rows = DB::query( |
3614
|
|
|
'SELECT id, fonction_id |
3615
|
|
|
FROM ' . prefixTable('users') . ' |
3616
|
|
|
WHERE id != %i AND admin = 0 AND fonction_id IS NOT NULL AND fonction_id != ""', |
3617
|
|
|
$session->get('user-id') |
3618
|
|
|
); |
3619
|
|
|
foreach ($rows as $user) { |
3620
|
|
|
$userRoles = is_null($user['fonction_id']) === false && empty($user['fonction_id']) === false ? explode(';', $user['fonction_id']) : []; |
3621
|
|
|
if (in_array($role, $userRoles, true) === true) { |
3622
|
|
|
array_push($arrUsers, $user['id']); |
3623
|
|
|
} |
3624
|
|
|
} |
3625
|
|
|
} |
3626
|
|
|
|
3627
|
|
|
return $arrUsers; |
3628
|
|
|
} |
3629
|
|
|
|
3630
|
|
|
|
3631
|
|
|
/** |
3632
|
|
|
* Get all users informations |
3633
|
|
|
* |
3634
|
|
|
* @param integer $userId |
3635
|
|
|
* @return array |
3636
|
|
|
*/ |
3637
|
|
|
function getFullUserInfos( |
3638
|
|
|
int $userId |
3639
|
|
|
): array |
3640
|
|
|
{ |
3641
|
|
|
if (empty($userId) === true) { |
3642
|
|
|
return array(); |
3643
|
|
|
} |
3644
|
|
|
|
3645
|
|
|
$val = DB::queryfirstrow( |
3646
|
|
|
'SELECT * |
3647
|
|
|
FROM ' . prefixTable('users') . ' |
3648
|
|
|
WHERE id = %i', |
3649
|
|
|
$userId |
3650
|
|
|
); |
3651
|
|
|
|
3652
|
|
|
return $val; |
3653
|
|
|
} |
3654
|
|
|
|
3655
|
|
|
/** |
3656
|
|
|
* Is required an upgrade |
3657
|
|
|
* |
3658
|
|
|
* @return boolean |
3659
|
|
|
*/ |
3660
|
|
|
function upgradeRequired(): bool |
3661
|
|
|
{ |
3662
|
|
|
// Get settings.php |
3663
|
|
|
include_once __DIR__. '/../includes/config/settings.php'; |
3664
|
|
|
|
3665
|
|
|
// Get timestamp in DB |
3666
|
|
|
$val = DB::queryfirstrow( |
3667
|
|
|
'SELECT valeur |
3668
|
|
|
FROM ' . prefixTable('misc') . ' |
3669
|
|
|
WHERE type = %s AND intitule = %s', |
3670
|
|
|
'admin', |
3671
|
|
|
'upgrade_timestamp' |
3672
|
|
|
); |
3673
|
|
|
|
3674
|
|
|
// if not exists then error |
3675
|
|
|
if (is_null($val) === true || count($val) === 0 || defined('UPGRADE_MIN_DATE') === false) return true; |
3676
|
|
|
|
3677
|
|
|
// if empty or too old then error |
3678
|
|
|
if (empty($val['valeur']) === true || (int) $val['valeur'] < (int) UPGRADE_MIN_DATE) { |
3679
|
|
|
return true; |
3680
|
|
|
} |
3681
|
|
|
|
3682
|
|
|
return false; |
3683
|
|
|
} |
3684
|
|
|
|
3685
|
|
|
/** |
3686
|
|
|
* Permits to change the user keys on his demand |
3687
|
|
|
* |
3688
|
|
|
* @param integer $userId |
3689
|
|
|
* @param string $passwordClear |
3690
|
|
|
* @param integer $nbItemsToTreat |
3691
|
|
|
* @param string $encryptionKey |
3692
|
|
|
* @param boolean $deleteExistingKeys |
3693
|
|
|
* @param boolean $sendEmailToUser |
3694
|
|
|
* @param boolean $encryptWithUserPassword |
3695
|
|
|
* @param boolean $generate_user_new_password |
3696
|
|
|
* @param string $emailBody |
3697
|
|
|
* @param boolean $user_self_change |
3698
|
|
|
* @param string $recovery_public_key |
3699
|
|
|
* @param string $recovery_private_key |
3700
|
|
|
* @return string |
3701
|
|
|
*/ |
3702
|
|
|
function handleUserKeys( |
3703
|
|
|
int $userId, |
3704
|
|
|
string $passwordClear, |
3705
|
|
|
int $nbItemsToTreat, |
3706
|
|
|
string $encryptionKey = '', |
3707
|
|
|
bool $deleteExistingKeys = false, |
3708
|
|
|
bool $sendEmailToUser = true, |
3709
|
|
|
bool $encryptWithUserPassword = false, |
3710
|
|
|
bool $generate_user_new_password = false, |
3711
|
|
|
string $emailBody = '', |
3712
|
|
|
bool $user_self_change = false, |
3713
|
|
|
string $recovery_public_key = '', |
3714
|
|
|
string $recovery_private_key = '' |
3715
|
|
|
): string |
3716
|
|
|
{ |
3717
|
|
|
$session = SessionManager::getSession(); |
3718
|
|
|
$lang = new Language(); |
3719
|
|
|
|
3720
|
|
|
// prepapre background tasks for item keys generation |
3721
|
|
|
$userTP = DB::queryFirstRow( |
3722
|
|
|
'SELECT pw, public_key, private_key |
3723
|
|
|
FROM ' . prefixTable('users') . ' |
3724
|
|
|
WHERE id = %i', |
3725
|
|
|
TP_USER_ID |
3726
|
|
|
); |
3727
|
|
|
if (DB::count() > 0) { |
3728
|
|
|
// Do we need to generate new user password |
3729
|
|
|
if ($generate_user_new_password === true) { |
3730
|
|
|
// Generate a new password |
3731
|
|
|
$passwordClear = GenerateCryptKey(20, false, true, true, false, true); |
3732
|
|
|
} |
3733
|
|
|
|
3734
|
|
|
// Hash the password |
3735
|
|
|
$pwdlib = new PasswordLib(); |
3736
|
|
|
$hashedPassword = $pwdlib->createPasswordHash($passwordClear); |
3737
|
|
|
if ($pwdlib->verifyPasswordHash($passwordClear, $hashedPassword) === false) { |
3738
|
|
|
return prepareExchangedData( |
3739
|
|
|
array( |
3740
|
|
|
'error' => true, |
3741
|
|
|
'message' => $lang->get('pw_hash_not_correct'), |
3742
|
|
|
), |
3743
|
|
|
'encode' |
3744
|
|
|
); |
3745
|
|
|
} |
3746
|
|
|
|
3747
|
|
|
// Generate new keys |
3748
|
|
|
if ($user_self_change === true && empty($recovery_public_key) === false && empty($recovery_private_key) === false){ |
3749
|
|
|
$userKeys = [ |
3750
|
|
|
'public_key' => $recovery_public_key, |
3751
|
|
|
'private_key_clear' => $recovery_private_key, |
3752
|
|
|
'private_key' => encryptPrivateKey($passwordClear, $recovery_private_key), |
3753
|
|
|
]; |
3754
|
|
|
} else { |
3755
|
|
|
$userKeys = generateUserKeys($passwordClear); |
3756
|
|
|
} |
3757
|
|
|
|
3758
|
|
|
// Save in DB |
3759
|
|
|
DB::update( |
3760
|
|
|
prefixTable('users'), |
3761
|
|
|
array( |
3762
|
|
|
'pw' => $hashedPassword, |
3763
|
|
|
'public_key' => $userKeys['public_key'], |
3764
|
|
|
'private_key' => $userKeys['private_key'], |
3765
|
|
|
), |
3766
|
|
|
'id=%i', |
3767
|
|
|
$userId |
3768
|
|
|
); |
3769
|
|
|
|
3770
|
|
|
// update session too |
3771
|
|
|
if ($userId === $session->get('user-id')) { |
3772
|
|
|
$session->set('user-private_key', $userKeys['private_key_clear']); |
3773
|
|
|
$session->set('user-public_key', $userKeys['public_key']); |
3774
|
|
|
} |
3775
|
|
|
|
3776
|
|
|
// Manage empty encryption key |
3777
|
|
|
// Let's take the user's password if asked and if no encryption key provided |
3778
|
|
|
$encryptionKey = $encryptWithUserPassword === true && empty($encryptionKey) === true ? $passwordClear : $encryptionKey; |
3779
|
|
|
|
3780
|
|
|
// Create process |
3781
|
|
|
DB::insert( |
3782
|
|
|
prefixTable('processes'), |
3783
|
|
|
array( |
3784
|
|
|
'created_at' => time(), |
3785
|
|
|
'process_type' => 'create_user_keys', |
3786
|
|
|
'arguments' => json_encode([ |
3787
|
|
|
'new_user_id' => (int) $userId, |
3788
|
|
|
'new_user_pwd' => cryption($passwordClear, '','encrypt')['string'], |
3789
|
|
|
'new_user_code' => cryption(empty($encryptionKey) === true ? uniqidReal(20) : $encryptionKey, '','encrypt')['string'], |
3790
|
|
|
'owner_id' => (int) TP_USER_ID, |
3791
|
|
|
'creator_pwd' => $userTP['pw'], |
3792
|
|
|
'send_email' => $sendEmailToUser === true ? 1 : 0, |
3793
|
|
|
'otp_provided_new_value' => 1, |
3794
|
|
|
'email_body' => empty($emailBody) === true ? '' : $lang->get($emailBody), |
3795
|
|
|
'user_self_change' => $user_self_change === true ? 1 : 0, |
3796
|
|
|
]), |
3797
|
|
|
'updated_at' => '', |
3798
|
|
|
'finished_at' => '', |
3799
|
|
|
'output' => '', |
3800
|
|
|
) |
3801
|
|
|
); |
3802
|
|
|
$processId = DB::insertId(); |
3803
|
|
|
|
3804
|
|
|
// Delete existing keys |
3805
|
|
|
if ($deleteExistingKeys === true) { |
3806
|
|
|
deleteUserObjetsKeys( |
3807
|
|
|
(int) $userId, |
3808
|
|
|
); |
3809
|
|
|
} |
3810
|
|
|
|
3811
|
|
|
// Create tasks |
3812
|
|
|
DB::insert( |
3813
|
|
|
prefixTable('processes_tasks'), |
3814
|
|
|
array( |
3815
|
|
|
'process_id' => $processId, |
3816
|
|
|
'created_at' => time(), |
3817
|
|
|
'task' => json_encode([ |
3818
|
|
|
'step' => 'step0', |
3819
|
|
|
'index' => 0, |
3820
|
|
|
'nb' => $nbItemsToTreat, |
3821
|
|
|
]), |
3822
|
|
|
) |
3823
|
|
|
); |
3824
|
|
|
|
3825
|
|
|
DB::insert( |
3826
|
|
|
prefixTable('processes_tasks'), |
3827
|
|
|
array( |
3828
|
|
|
'process_id' => $processId, |
3829
|
|
|
'created_at' => time(), |
3830
|
|
|
'task' => json_encode([ |
3831
|
|
|
'step' => 'step10', |
3832
|
|
|
'index' => 0, |
3833
|
|
|
'nb' => $nbItemsToTreat, |
3834
|
|
|
]), |
3835
|
|
|
) |
3836
|
|
|
); |
3837
|
|
|
|
3838
|
|
|
DB::insert( |
3839
|
|
|
prefixTable('processes_tasks'), |
3840
|
|
|
array( |
3841
|
|
|
'process_id' => $processId, |
3842
|
|
|
'created_at' => time(), |
3843
|
|
|
'task' => json_encode([ |
3844
|
|
|
'step' => 'step20', |
3845
|
|
|
'index' => 0, |
3846
|
|
|
'nb' => $nbItemsToTreat, |
3847
|
|
|
]), |
3848
|
|
|
) |
3849
|
|
|
); |
3850
|
|
|
|
3851
|
|
|
DB::insert( |
3852
|
|
|
prefixTable('processes_tasks'), |
3853
|
|
|
array( |
3854
|
|
|
'process_id' => $processId, |
3855
|
|
|
'created_at' => time(), |
3856
|
|
|
'task' => json_encode([ |
3857
|
|
|
'step' => 'step30', |
3858
|
|
|
'index' => 0, |
3859
|
|
|
'nb' => $nbItemsToTreat, |
3860
|
|
|
]), |
3861
|
|
|
) |
3862
|
|
|
); |
3863
|
|
|
|
3864
|
|
|
DB::insert( |
3865
|
|
|
prefixTable('processes_tasks'), |
3866
|
|
|
array( |
3867
|
|
|
'process_id' => $processId, |
3868
|
|
|
'created_at' => time(), |
3869
|
|
|
'task' => json_encode([ |
3870
|
|
|
'step' => 'step40', |
3871
|
|
|
'index' => 0, |
3872
|
|
|
'nb' => $nbItemsToTreat, |
3873
|
|
|
]), |
3874
|
|
|
) |
3875
|
|
|
); |
3876
|
|
|
|
3877
|
|
|
DB::insert( |
3878
|
|
|
prefixTable('processes_tasks'), |
3879
|
|
|
array( |
3880
|
|
|
'process_id' => $processId, |
3881
|
|
|
'created_at' => time(), |
3882
|
|
|
'task' => json_encode([ |
3883
|
|
|
'step' => 'step50', |
3884
|
|
|
'index' => 0, |
3885
|
|
|
'nb' => $nbItemsToTreat, |
3886
|
|
|
]), |
3887
|
|
|
) |
3888
|
|
|
); |
3889
|
|
|
|
3890
|
|
|
DB::insert( |
3891
|
|
|
prefixTable('processes_tasks'), |
3892
|
|
|
array( |
3893
|
|
|
'process_id' => $processId, |
3894
|
|
|
'created_at' => time(), |
3895
|
|
|
'task' => json_encode([ |
3896
|
|
|
'step' => 'step60', |
3897
|
|
|
'index' => 0, |
3898
|
|
|
'nb' => $nbItemsToTreat, |
3899
|
|
|
]), |
3900
|
|
|
) |
3901
|
|
|
); |
3902
|
|
|
|
3903
|
|
|
// update user's new status |
3904
|
|
|
DB::update( |
3905
|
|
|
prefixTable('users'), |
3906
|
|
|
[ |
3907
|
|
|
'is_ready_for_usage' => 0, |
3908
|
|
|
'otp_provided' => 1, |
3909
|
|
|
'ongoing_process_id' => $processId, |
3910
|
|
|
'special' => 'generate-keys', |
3911
|
|
|
], |
3912
|
|
|
'id=%i', |
3913
|
|
|
$userId |
3914
|
|
|
); |
3915
|
|
|
} |
3916
|
|
|
|
3917
|
|
|
return prepareExchangedData( |
3918
|
|
|
array( |
3919
|
|
|
'error' => false, |
3920
|
|
|
'message' => '', |
3921
|
|
|
), |
3922
|
|
|
'encode' |
3923
|
|
|
); |
3924
|
|
|
} |
3925
|
|
|
|
3926
|
|
|
/** |
3927
|
|
|
* Permeits to check the consistency of date versus columns definition |
3928
|
|
|
* |
3929
|
|
|
* @param string $table |
3930
|
|
|
* @param array $dataFields |
3931
|
|
|
* @return array |
3932
|
|
|
*/ |
3933
|
|
|
function validateDataFields( |
3934
|
|
|
string $table, |
3935
|
|
|
array $dataFields |
3936
|
|
|
): array |
3937
|
|
|
{ |
3938
|
|
|
// Get table structure |
3939
|
|
|
$result = DB::query( |
3940
|
|
|
"SELECT `COLUMN_NAME`, `CHARACTER_MAXIMUM_LENGTH` FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%l' AND TABLE_NAME = '%l';", |
3941
|
|
|
DB_NAME, |
3942
|
|
|
$table |
3943
|
|
|
); |
3944
|
|
|
|
3945
|
|
|
foreach ($result as $row) { |
3946
|
|
|
$field = $row['COLUMN_NAME']; |
3947
|
|
|
$maxLength = is_null($row['CHARACTER_MAXIMUM_LENGTH']) === false ? (int) $row['CHARACTER_MAXIMUM_LENGTH'] : ''; |
3948
|
|
|
|
3949
|
|
|
if (isset($dataFields[$field]) === true && is_array($dataFields[$field]) === false && empty($maxLength) === false) { |
3950
|
|
|
if (strlen((string) $dataFields[$field]) > $maxLength) { |
3951
|
|
|
return [ |
3952
|
|
|
'state' => false, |
3953
|
|
|
'field' => $field, |
3954
|
|
|
'maxLength' => $maxLength, |
3955
|
|
|
'currentLength' => strlen((string) $dataFields[$field]), |
3956
|
|
|
]; |
3957
|
|
|
} |
3958
|
|
|
} |
3959
|
|
|
} |
3960
|
|
|
|
3961
|
|
|
return [ |
3962
|
|
|
'state' => true, |
3963
|
|
|
'message' => '', |
3964
|
|
|
]; |
3965
|
|
|
} |
3966
|
|
|
|
3967
|
|
|
/** |
3968
|
|
|
* Adapt special characters sanitized during filter_var with option FILTER_SANITIZE_SPECIAL_CHARS operation |
3969
|
|
|
* |
3970
|
|
|
* @param string $string |
3971
|
|
|
* @return string |
3972
|
|
|
*/ |
3973
|
|
|
function filterVarBack(string $string): string |
3974
|
|
|
{ |
3975
|
|
|
$arr = [ |
3976
|
|
|
'<' => '<', |
3977
|
|
|
'>' => '>', |
3978
|
|
|
'"' => '"', |
3979
|
|
|
''' => "'", |
3980
|
|
|
'&' => '&', |
3981
|
|
|
]; |
3982
|
|
|
|
3983
|
|
|
foreach ($arr as $key => $value) { |
3984
|
|
|
$string = str_replace($key, $value, $string); |
3985
|
|
|
} |
3986
|
|
|
|
3987
|
|
|
return $string; |
3988
|
|
|
} |
3989
|
|
|
|
3990
|
|
|
/** |
3991
|
|
|
* |
3992
|
|
|
*/ |
3993
|
|
|
function storeTask( |
3994
|
|
|
string $taskName, |
3995
|
|
|
int $user_id, |
3996
|
|
|
int $is_personal_folder, |
3997
|
|
|
int $folder_destination_id, |
3998
|
|
|
int $item_id, |
3999
|
|
|
string $object_keys |
4000
|
|
|
) |
4001
|
|
|
{ |
4002
|
|
|
if (in_array($taskName, ['item_copy', 'new_item', 'update_item'])) { |
4003
|
|
|
// Create process |
4004
|
|
|
DB::insert( |
4005
|
|
|
prefixTable('processes'), |
4006
|
|
|
array( |
4007
|
|
|
'created_at' => time(), |
4008
|
|
|
'process_type' => $taskName, |
4009
|
|
|
'arguments' => json_encode([ |
4010
|
|
|
'item_id' => $item_id, |
4011
|
|
|
'object_key' => $object_keys, |
4012
|
|
|
]), |
4013
|
|
|
'updated_at' => '', |
4014
|
|
|
'finished_at' => '', |
4015
|
|
|
'output' => '', |
4016
|
|
|
'item_id' => $item_id, |
4017
|
|
|
) |
4018
|
|
|
); |
4019
|
|
|
$processId = DB::insertId(); |
4020
|
|
|
|
4021
|
|
|
// Create tasks |
4022
|
|
|
// 1- Create password sharekeys for users of this new ITEM |
4023
|
|
|
DB::insert( |
4024
|
|
|
prefixTable('processes_tasks'), |
4025
|
|
|
array( |
4026
|
|
|
'process_id' => $processId, |
4027
|
|
|
'created_at' => time(), |
4028
|
|
|
'task' => json_encode([ |
4029
|
|
|
'step' => 'create_users_pwd_key', |
4030
|
|
|
'index' => 0, |
4031
|
|
|
]), |
4032
|
|
|
) |
4033
|
|
|
); |
4034
|
|
|
|
4035
|
|
|
// 2- Create fields sharekeys for users of this new ITEM |
4036
|
|
|
DB::insert( |
4037
|
|
|
prefixTable('processes_tasks'), |
4038
|
|
|
array( |
4039
|
|
|
'process_id' => $processId, |
4040
|
|
|
'created_at' => time(), |
4041
|
|
|
'task' => json_encode([ |
4042
|
|
|
'step' => 'create_users_fields_key', |
4043
|
|
|
'index' => 0, |
4044
|
|
|
]), |
4045
|
|
|
) |
4046
|
|
|
); |
4047
|
|
|
|
4048
|
|
|
// 3- Create files sharekeys for users of this new ITEM |
4049
|
|
|
DB::insert( |
4050
|
|
|
prefixTable('processes_tasks'), |
4051
|
|
|
array( |
4052
|
|
|
'process_id' => $processId, |
4053
|
|
|
'created_at' => time(), |
4054
|
|
|
'task' => json_encode([ |
4055
|
|
|
'step' => 'create_users_files_key', |
4056
|
|
|
'index' => 0, |
4057
|
|
|
]), |
4058
|
|
|
) |
4059
|
|
|
); |
4060
|
|
|
} |
4061
|
|
|
} |
4062
|
|
|
|
4063
|
|
|
/** |
4064
|
|
|
* Return PHP binary path |
4065
|
|
|
* |
4066
|
|
|
* @return string |
4067
|
|
|
*/ |
4068
|
|
|
function getPHPBinary(): string |
4069
|
|
|
{ |
4070
|
|
|
// Get PHP binary path |
4071
|
|
|
$phpBinaryFinder = new PhpExecutableFinder(); |
4072
|
|
|
$phpBinaryPath = $phpBinaryFinder->find(); |
4073
|
|
|
return $phpBinaryPath === false ? 'false' : $phpBinaryPath; |
4074
|
|
|
} |
4075
|
|
|
|
4076
|
|
|
|
4077
|
|
|
|
4078
|
|
|
/** |
4079
|
|
|
* Delete unnecessary keys for personal items |
4080
|
|
|
* |
4081
|
|
|
* @param boolean $allUsers |
4082
|
|
|
* @param integer $user_id |
4083
|
|
|
* @return void |
4084
|
|
|
*/ |
4085
|
|
|
function purgeUnnecessaryKeys(bool $allUsers = true, int $user_id=0) |
4086
|
|
|
{ |
4087
|
|
|
if ($allUsers === true) { |
4088
|
|
|
// Load class DB |
4089
|
|
|
if (class_exists('DB') === false) { |
4090
|
|
|
loadClasses('DB'); |
4091
|
|
|
} |
4092
|
|
|
|
4093
|
|
|
$users = DB::query( |
4094
|
|
|
'SELECT id |
4095
|
|
|
FROM ' . prefixTable('users') . ' |
4096
|
|
|
WHERE id NOT IN ('.OTV_USER_ID.', '.TP_USER_ID.', '.SSH_USER_ID.', '.API_USER_ID.') |
4097
|
|
|
ORDER BY login ASC' |
4098
|
|
|
); |
4099
|
|
|
foreach ($users as $user) { |
4100
|
|
|
purgeUnnecessaryKeysForUser((int) $user['id']); |
4101
|
|
|
} |
4102
|
|
|
} else { |
4103
|
|
|
purgeUnnecessaryKeysForUser((int) $user_id); |
4104
|
|
|
} |
4105
|
|
|
} |
4106
|
|
|
|
4107
|
|
|
/** |
4108
|
|
|
* Delete unnecessary keys for personal items |
4109
|
|
|
* |
4110
|
|
|
* @param integer $user_id |
4111
|
|
|
* @return void |
4112
|
|
|
*/ |
4113
|
|
|
function purgeUnnecessaryKeysForUser(int $user_id=0) |
4114
|
|
|
{ |
4115
|
|
|
if ($user_id === 0) { |
4116
|
|
|
return; |
4117
|
|
|
} |
4118
|
|
|
|
4119
|
|
|
// Load class DB |
4120
|
|
|
loadClasses('DB'); |
4121
|
|
|
|
4122
|
|
|
$personalItems = DB::queryFirstColumn( |
4123
|
|
|
'SELECT id |
4124
|
|
|
FROM ' . prefixTable('items') . ' AS i |
4125
|
|
|
INNER JOIN ' . prefixTable('log_items') . ' AS li ON li.id_item = i.id |
4126
|
|
|
WHERE i.perso = 1 AND li.action = "at_creation" AND li.id_user IN (%i, '.TP_USER_ID.')', |
4127
|
|
|
$user_id |
4128
|
|
|
); |
4129
|
|
|
if (count($personalItems) > 0) { |
4130
|
|
|
// Item keys |
4131
|
|
|
DB::delete( |
4132
|
|
|
prefixTable('sharekeys_items'), |
4133
|
|
|
'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')', |
4134
|
|
|
$personalItems, |
4135
|
|
|
$user_id |
4136
|
|
|
); |
4137
|
|
|
// Files keys |
4138
|
|
|
DB::delete( |
4139
|
|
|
prefixTable('sharekeys_files'), |
4140
|
|
|
'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')', |
4141
|
|
|
$personalItems, |
4142
|
|
|
$user_id |
4143
|
|
|
); |
4144
|
|
|
// Fields keys |
4145
|
|
|
DB::delete( |
4146
|
|
|
prefixTable('sharekeys_fields'), |
4147
|
|
|
'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')', |
4148
|
|
|
$personalItems, |
4149
|
|
|
$user_id |
4150
|
|
|
); |
4151
|
|
|
// Logs keys |
4152
|
|
|
DB::delete( |
4153
|
|
|
prefixTable('sharekeys_logs'), |
4154
|
|
|
'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')', |
4155
|
|
|
$personalItems, |
4156
|
|
|
$user_id |
4157
|
|
|
); |
4158
|
|
|
} |
4159
|
|
|
} |
4160
|
|
|
|
4161
|
|
|
/** |
4162
|
|
|
* Generate recovery keys file |
4163
|
|
|
* |
4164
|
|
|
* @param integer $userId |
4165
|
|
|
* @param array $SETTINGS |
4166
|
|
|
* @return string |
4167
|
|
|
*/ |
4168
|
|
|
function handleUserRecoveryKeysDownload(int $userId, array $SETTINGS):string |
4169
|
|
|
{ |
4170
|
|
|
$session = SessionManager::getSession(); |
4171
|
|
|
// Check if user exists |
4172
|
|
|
$userInfo = DB::queryFirstRow( |
4173
|
|
|
'SELECT pw, public_key, private_key, login, name |
4174
|
|
|
FROM ' . prefixTable('users') . ' |
4175
|
|
|
WHERE id = %i', |
4176
|
|
|
$userId |
4177
|
|
|
); |
4178
|
|
|
|
4179
|
|
|
if (DB::count() > 0) { |
4180
|
|
|
$now = (int) time(); |
4181
|
|
|
|
4182
|
|
|
// Prepare file content |
4183
|
|
|
$export_value = file_get_contents(__DIR__."/../includes/core/teampass_ascii.txt")."\n". |
4184
|
|
|
"Generation date: ".date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], $now)."\n\n". |
4185
|
|
|
"RECOVERY KEYS - Not to be shared - To be store safely\n\n". |
4186
|
|
|
"Public Key:\n".$userInfo['public_key']."\n\n". |
4187
|
|
|
"Private Key:\n".decryptPrivateKey($session->get('user-password'), $userInfo['private_key'])."\n\n"; |
4188
|
|
|
|
4189
|
|
|
// Update user's keys_recovery_time |
4190
|
|
|
DB::update( |
4191
|
|
|
prefixTable('users'), |
4192
|
|
|
[ |
4193
|
|
|
'keys_recovery_time' => $now, |
4194
|
|
|
], |
4195
|
|
|
'id=%i', |
4196
|
|
|
$userId |
4197
|
|
|
); |
4198
|
|
|
$session->set('user-keys_recovery_time', $now); |
4199
|
|
|
|
4200
|
|
|
//Log into DB the user's disconnection |
4201
|
|
|
logEvents($SETTINGS, 'user_mngt', 'at_user_keys_download', (string) $userId, $userInfo['login']); |
4202
|
|
|
|
4203
|
|
|
// Return data |
4204
|
|
|
return prepareExchangedData( |
4205
|
|
|
array( |
4206
|
|
|
'error' => false, |
4207
|
|
|
'datetime' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], $now), |
4208
|
|
|
'timestamp' => $now, |
4209
|
|
|
'content' => base64_encode($export_value), |
4210
|
|
|
'login' => $userInfo['login'], |
4211
|
|
|
), |
4212
|
|
|
'encode' |
4213
|
|
|
); |
4214
|
|
|
} |
4215
|
|
|
|
4216
|
|
|
return prepareExchangedData( |
4217
|
|
|
array( |
4218
|
|
|
'error' => true, |
4219
|
|
|
'datetime' => '', |
4220
|
|
|
), |
4221
|
|
|
'encode' |
4222
|
|
|
); |
4223
|
|
|
} |
4224
|
|
|
|
4225
|
|
|
/** |
4226
|
|
|
* Permits to load expected classes |
4227
|
|
|
* |
4228
|
|
|
* @param string $className |
4229
|
|
|
* @return void |
4230
|
|
|
*/ |
4231
|
|
|
function loadClasses(string $className = ''): void |
4232
|
|
|
{ |
4233
|
|
|
require_once __DIR__. '/../includes/config/include.php'; |
4234
|
|
|
require_once __DIR__. '/../includes/config/settings.php'; |
4235
|
|
|
require_once __DIR__.'/../vendor/autoload.php'; |
4236
|
|
|
|
4237
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
4238
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, [])); |
4239
|
|
|
} |
4240
|
|
|
|
4241
|
|
|
if (empty($className) === false) { |
4242
|
|
|
// Load class DB |
4243
|
|
|
if ((string) $className === 'DB') { |
4244
|
|
|
//Connect to DB |
4245
|
|
|
DB::$host = DB_HOST; |
4246
|
|
|
DB::$user = DB_USER; |
4247
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
4248
|
|
|
DB::$dbName = DB_NAME; |
4249
|
|
|
DB::$port = DB_PORT; |
4250
|
|
|
DB::$encoding = DB_ENCODING; |
4251
|
|
|
DB::$ssl = DB_SSL; |
4252
|
|
|
DB::$connect_options = DB_CONNECT_OPTIONS; |
4253
|
|
|
} |
4254
|
|
|
} |
4255
|
|
|
|
4256
|
|
|
} |
4257
|
|
|
|
4258
|
|
|
/** |
4259
|
|
|
* Returns the page the user is visiting. |
4260
|
|
|
* |
4261
|
|
|
* @return string The page name |
4262
|
|
|
*/ |
4263
|
|
|
function getCurrectPage($SETTINGS) |
4264
|
|
|
{ |
4265
|
|
|
|
4266
|
|
|
$request = Request::createFromGlobals(); |
4267
|
|
|
|
4268
|
|
|
// Parse the url |
4269
|
|
|
parse_str( |
4270
|
|
|
substr( |
4271
|
|
|
(string) $request->server->get('REQUEST_URI'), |
4272
|
|
|
strpos((string) $request->server->get('REQUEST_URI'), '?') + 1 |
4273
|
|
|
), |
4274
|
|
|
$result |
4275
|
|
|
); |
4276
|
|
|
|
4277
|
|
|
return $result['page']; |
4278
|
|
|
} |
4279
|
|
|
|
4280
|
|
|
/** |
4281
|
|
|
* Permits to return value if set |
4282
|
|
|
* |
4283
|
|
|
* @param string|int $value |
4284
|
|
|
* @param string|int|null $retFalse |
4285
|
|
|
* @param string|int $retTrue |
4286
|
|
|
* @return mixed |
4287
|
|
|
*/ |
4288
|
|
|
function returnIfSet($value, $retFalse = '', $retTrue = null): mixed |
4289
|
|
|
{ |
4290
|
|
|
|
4291
|
|
|
return isset($value) === true ? ($retTrue === null ? $value : $retTrue) : $retFalse; |
4292
|
|
|
} |
4293
|
|
|
|
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: