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
|
|
|
* |
15
|
|
|
* @file main.functions.php |
16
|
|
|
* --- |
17
|
|
|
* |
18
|
|
|
* @author Nils Laumaillé ([email protected]) |
19
|
|
|
* |
20
|
|
|
* @copyright 2009-2022 Teampass.net |
21
|
|
|
* |
22
|
|
|
* @license https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0 |
23
|
|
|
* --- |
24
|
|
|
* |
25
|
|
|
* @see https://www.teampass.net |
26
|
|
|
*/ |
27
|
|
|
|
28
|
|
|
use LdapRecord\Connection; |
29
|
|
|
|
30
|
|
|
if (isset($_SESSION['CPM']) === false || (int) $_SESSION['CPM'] !== 1) { |
31
|
|
|
die('Hacking attempt...'); |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
// Load config if $SETTINGS not defined |
35
|
|
|
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) { |
36
|
|
|
if (file_exists('../includes/config/tp.config.php')) { |
37
|
|
|
include_once '../includes/config/tp.config.php'; |
38
|
|
|
} elseif (file_exists('./includes/config/tp.config.php')) { |
39
|
|
|
include_once './includes/config/tp.config.php'; |
40
|
|
|
} elseif (file_exists('../../includes/config/tp.config.php')) { |
41
|
|
|
include_once '../../includes/config/tp.config.php'; |
42
|
|
|
} else { |
43
|
|
|
throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1); |
44
|
|
|
} |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
header('Content-type: text/html; charset=utf-8'); |
48
|
|
|
header('Cache-Control: no-cache, must-revalidate'); |
49
|
|
|
/** |
50
|
|
|
* Convert language code to string. |
51
|
|
|
* |
52
|
|
|
* @param string $string String to get |
53
|
|
|
*/ |
54
|
|
|
function langHdl(string $string): string |
55
|
|
|
{ |
56
|
|
|
if (empty($string) === true) { |
57
|
|
|
// Manage error |
58
|
|
|
return 'ERROR in language strings!'; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
// Load superglobal |
62
|
|
|
if (file_exists('../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) { |
63
|
|
|
include_once '../includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
64
|
|
|
} elseif (file_exists('./includes/libraries/protect/SuperGlobal/SuperGlobal.php')) { |
65
|
|
|
include_once './includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
66
|
|
|
} elseif (file_exists('../../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) { |
67
|
|
|
include_once '../../includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
68
|
|
|
} else { |
69
|
|
|
throw new Exception("Error file '/includes/libraries/protect/SuperGlobal/SuperGlobal.php' not exists", 1); |
70
|
|
|
} |
71
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
72
|
|
|
// Get language string |
73
|
|
|
$session_language = $superGlobal->get(trim($string), 'SESSION', 'lang'); |
74
|
|
|
if (isset($session_language) === false) { |
75
|
|
|
// Manage error |
76
|
|
|
return 'ERROR in language strings!'; |
77
|
|
|
} |
78
|
|
|
return str_replace( |
79
|
|
|
['"', "'"], |
80
|
|
|
['"', '''], |
81
|
|
|
$session_language |
82
|
|
|
); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* genHash(). |
87
|
|
|
* |
88
|
|
|
* Generate a hash for user login |
89
|
|
|
* |
90
|
|
|
* @param string $password What password |
91
|
|
|
* @param string $cost What cost |
92
|
|
|
* |
93
|
|
|
* @return string|void |
94
|
|
|
*/ |
95
|
|
|
function bCrypt( |
96
|
|
|
string $password, |
97
|
|
|
string $cost |
98
|
|
|
): ?string |
99
|
|
|
{ |
100
|
|
|
$salt = sprintf('$2y$%02d$', $cost); |
101
|
|
|
if (function_exists('openssl_random_pseudo_bytes')) { |
102
|
|
|
$salt .= bin2hex(openssl_random_pseudo_bytes(11)); |
103
|
|
|
} else { |
104
|
|
|
$chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
105
|
|
|
for ($i = 0; $i < 22; ++$i) { |
106
|
|
|
$salt .= $chars[mt_rand(0, 63)]; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return crypt($password, $salt); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Defuse cryption function. |
115
|
|
|
* |
116
|
|
|
* @param string $message what to de/crypt |
117
|
|
|
* @param string $ascii_key key to use |
118
|
|
|
* @param string $type operation to perform |
119
|
|
|
* @param array $SETTINGS Teampass settings |
120
|
|
|
* |
121
|
|
|
* @return array |
122
|
|
|
*/ |
123
|
|
|
function cryption(string $message, string $ascii_key, string $type, array $SETTINGS): array |
124
|
|
|
{ |
125
|
|
|
$ascii_key = empty($ascii_key) === true ? file_get_contents(SECUREPATH . '/teampass-seckey.txt') : $ascii_key; |
126
|
|
|
$err = false; |
127
|
|
|
// load PhpEncryption library |
128
|
|
|
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) { |
129
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
130
|
|
|
} else { |
131
|
|
|
$path = $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/Encryption/'; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
include_once $path . 'Exception/CryptoException.php'; |
135
|
|
|
include_once $path . 'Exception/BadFormatException.php'; |
136
|
|
|
include_once $path . 'Exception/EnvironmentIsBrokenException.php'; |
137
|
|
|
include_once $path . 'Exception/IOException.php'; |
138
|
|
|
include_once $path . 'Exception/WrongKeyOrModifiedCiphertextException.php'; |
139
|
|
|
include_once $path . 'Crypto.php'; |
140
|
|
|
include_once $path . 'Encoding.php'; |
141
|
|
|
include_once $path . 'DerivedKeys.php'; |
142
|
|
|
include_once $path . 'Key.php'; |
143
|
|
|
include_once $path . 'KeyOrPassword.php'; |
144
|
|
|
include_once $path . 'File.php'; |
145
|
|
|
include_once $path . 'RuntimeTests.php'; |
146
|
|
|
include_once $path . 'KeyProtectedByPassword.php'; |
147
|
|
|
include_once $path . 'Core.php'; |
148
|
|
|
|
149
|
|
|
// convert KEY |
150
|
|
|
$key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key); |
151
|
|
|
try { |
152
|
|
|
if ($type === 'encrypt') { |
153
|
|
|
$text = \Defuse\Crypto\Crypto::encrypt($message, $key); |
154
|
|
|
} elseif ($type === 'decrypt') { |
155
|
|
|
$text = \Defuse\Crypto\Crypto::decrypt($message, $key); |
156
|
|
|
} |
157
|
|
|
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) { |
158
|
|
|
$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.'; |
159
|
|
|
} catch (Defuse\Crypto\Exception\BadFormatException $ex) { |
160
|
|
|
$err = $ex; |
161
|
|
|
} catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) { |
162
|
|
|
$err = $ex; |
163
|
|
|
} catch (Defuse\Crypto\Exception\CryptoException $ex) { |
164
|
|
|
$err = $ex; |
165
|
|
|
} catch (Defuse\Crypto\Exception\IOException $ex) { |
166
|
|
|
$err = $ex; |
167
|
|
|
} |
168
|
|
|
//echo \Defuse\Crypto\Crypto::decrypt($message, $key).' ## '; |
169
|
|
|
|
170
|
|
|
return [ |
171
|
|
|
'string' => $text ?? '', |
172
|
|
|
'error' => $err, |
173
|
|
|
]; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Generating a defuse key. |
178
|
|
|
* |
179
|
|
|
* @return string |
180
|
|
|
*/ |
181
|
|
|
function defuse_generate_key() |
182
|
|
|
{ |
183
|
|
|
// load PhpEncryption library |
184
|
|
|
if (file_exists('../includes/config/tp.config.php') === true) { |
185
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
186
|
|
|
} elseif (file_exists('./includes/config/tp.config.php') === true) { |
187
|
|
|
$path = './includes/libraries/Encryption/Encryption/'; |
188
|
|
|
} else { |
189
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
include_once $path . 'Exception/CryptoException.php'; |
193
|
|
|
include_once $path . 'Exception/BadFormatException.php'; |
194
|
|
|
include_once $path . 'Exception/EnvironmentIsBrokenException.php'; |
195
|
|
|
include_once $path . 'Exception/IOException.php'; |
196
|
|
|
include_once $path . 'Exception/WrongKeyOrModifiedCiphertextException.php'; |
197
|
|
|
include_once $path . 'Crypto.php'; |
198
|
|
|
include_once $path . 'Encoding.php'; |
199
|
|
|
include_once $path . 'DerivedKeys.php'; |
200
|
|
|
include_once $path . 'Key.php'; |
201
|
|
|
include_once $path . 'KeyOrPassword.php'; |
202
|
|
|
include_once $path . 'File.php'; |
203
|
|
|
include_once $path . 'RuntimeTests.php'; |
204
|
|
|
include_once $path . 'KeyProtectedByPassword.php'; |
205
|
|
|
include_once $path . 'Core.php'; |
206
|
|
|
|
207
|
|
|
$key = \Defuse\Crypto\Key::createNewRandomKey(); |
208
|
|
|
$key = $key->saveToAsciiSafeString(); |
209
|
|
|
return $key; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Generate a Defuse personal key. |
214
|
|
|
* |
215
|
|
|
* @param string $psk psk used |
216
|
|
|
* |
217
|
|
|
* @return string |
218
|
|
|
*/ |
219
|
|
|
function defuse_generate_personal_key(string $psk): string |
220
|
|
|
{ |
221
|
|
|
// load PhpEncryption library |
222
|
|
|
if (file_exists('../includes/config/tp.config.php') === true) { |
223
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
224
|
|
|
} elseif (file_exists('./includes/config/tp.config.php') === true) { |
225
|
|
|
$path = './includes/libraries/Encryption/Encryption/'; |
226
|
|
|
} else { |
227
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
include_once $path . 'Exception/CryptoException.php'; |
231
|
|
|
include_once $path . 'Exception/BadFormatException.php'; |
232
|
|
|
include_once $path . 'Exception/EnvironmentIsBrokenException.php'; |
233
|
|
|
include_once $path . 'Exception/IOException.php'; |
234
|
|
|
include_once $path . 'Exception/WrongKeyOrModifiedCiphertextException.php'; |
235
|
|
|
include_once $path . 'Crypto.php'; |
236
|
|
|
include_once $path . 'Encoding.php'; |
237
|
|
|
include_once $path . 'DerivedKeys.php'; |
238
|
|
|
include_once $path . 'Key.php'; |
239
|
|
|
include_once $path . 'KeyOrPassword.php'; |
240
|
|
|
include_once $path . 'File.php'; |
241
|
|
|
include_once $path . 'RuntimeTests.php'; |
242
|
|
|
include_once $path . 'KeyProtectedByPassword.php'; |
243
|
|
|
include_once $path . 'Core.php'; |
244
|
|
|
|
245
|
|
|
$protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk); |
246
|
|
|
return $protected_key->saveToAsciiSafeString(); // save this in user table |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* Validate persoanl key with defuse. |
251
|
|
|
* |
252
|
|
|
* @param string $psk the user's psk |
253
|
|
|
* @param string $protected_key_encoded special key |
254
|
|
|
* |
255
|
|
|
* @return string |
256
|
|
|
*/ |
257
|
|
|
function defuse_validate_personal_key(string $psk, string $protected_key_encoded): string |
258
|
|
|
{ |
259
|
|
|
// load PhpEncryption library |
260
|
|
|
if (file_exists('../includes/config/tp.config.php') === true) { |
261
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
262
|
|
|
} elseif (file_exists('./includes/config/tp.config.php') === true) { |
263
|
|
|
$path = './includes/libraries/Encryption/Encryption/'; |
264
|
|
|
} else { |
265
|
|
|
$path = '../includes/libraries/Encryption/Encryption/'; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
include_once $path . 'Exception/CryptoException.php'; |
269
|
|
|
include_once $path . 'Exception/BadFormatException.php'; |
270
|
|
|
include_once $path . 'Exception/EnvironmentIsBrokenException.php'; |
271
|
|
|
include_once $path . 'Exception/IOException.php'; |
272
|
|
|
include_once $path . 'Exception/WrongKeyOrModifiedCiphertextException.php'; |
273
|
|
|
include_once $path . 'Crypto.php'; |
274
|
|
|
include_once $path . 'Encoding.php'; |
275
|
|
|
include_once $path . 'DerivedKeys.php'; |
276
|
|
|
include_once $path . 'Key.php'; |
277
|
|
|
include_once $path . 'KeyOrPassword.php'; |
278
|
|
|
include_once $path . 'File.php'; |
279
|
|
|
include_once $path . 'RuntimeTests.php'; |
280
|
|
|
include_once $path . 'KeyProtectedByPassword.php'; |
281
|
|
|
include_once $path . 'Core.php'; |
282
|
|
|
|
283
|
|
|
try { |
284
|
|
|
$protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded); |
285
|
|
|
$user_key = $protected_key->unlockKey($psk); |
286
|
|
|
$user_key_encoded = $user_key->saveToAsciiSafeString(); |
287
|
|
|
} catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) { |
288
|
|
|
return 'Error - Major issue as the encryption is broken.'; |
289
|
|
|
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) { |
290
|
|
|
return 'Error - The saltkey is not the correct one.'; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
return $user_key_encoded; |
294
|
|
|
// store it in session once user has entered his psk |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Decrypt a defuse string if encrypted. |
299
|
|
|
* |
300
|
|
|
* @param string $value Encrypted string |
301
|
|
|
* |
302
|
|
|
* @return string Decrypted string |
303
|
|
|
*/ |
304
|
|
|
function defuseReturnDecrypted(string $value, $SETTINGS): string |
305
|
|
|
{ |
306
|
|
|
if (substr($value, 0, 3) === 'def') { |
307
|
|
|
$value = cryption($value, '', 'decrypt', $SETTINGS)['string']; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
return $value; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Trims a string depending on a specific string. |
315
|
|
|
* |
316
|
|
|
* @param string|array $chaine what to trim |
317
|
|
|
* @param string $element trim on what |
318
|
|
|
* |
319
|
|
|
* @return string |
320
|
|
|
*/ |
321
|
|
|
function trimElement($chaine, string $element): string |
322
|
|
|
{ |
323
|
|
|
if (! empty($chaine)) { |
324
|
|
|
if (is_array($chaine) === true) { |
325
|
|
|
$chaine = implode(';', $chaine); |
326
|
|
|
} |
327
|
|
|
$chaine = trim($chaine); |
328
|
|
|
if (substr($chaine, 0, 1) === $element) { |
329
|
|
|
$chaine = substr($chaine, 1); |
330
|
|
|
} |
331
|
|
|
if (substr($chaine, strlen($chaine) - 1, 1) === $element) { |
332
|
|
|
$chaine = substr($chaine, 0, strlen($chaine) - 1); |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
return $chaine; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Permits to suppress all "special" characters from string. |
341
|
|
|
* |
342
|
|
|
* @param string $string what to clean |
343
|
|
|
* @param bool $special use of special chars? |
344
|
|
|
* |
345
|
|
|
* @return string |
346
|
|
|
*/ |
347
|
|
|
function cleanString(string $string, bool $special = false): string |
348
|
|
|
{ |
349
|
|
|
// Create temporary table for special characters escape |
350
|
|
|
$tabSpecialChar = []; |
351
|
|
|
for ($i = 0; $i <= 31; ++$i) { |
352
|
|
|
$tabSpecialChar[] = chr($i); |
353
|
|
|
} |
354
|
|
|
array_push($tabSpecialChar, '<br />'); |
355
|
|
|
if ((int) $special === 1) { |
356
|
|
|
$tabSpecialChar = array_merge($tabSpecialChar, ['</li>', '<ul>', '<ol>']); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
return str_replace($tabSpecialChar, "\n", $string); |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Erro manager for DB. |
364
|
|
|
* |
365
|
|
|
* @param array $params output from query |
366
|
|
|
* |
367
|
|
|
* @return void |
368
|
|
|
*/ |
369
|
|
|
function db_error_handler(array $params): void |
370
|
|
|
{ |
371
|
|
|
echo 'Error: ' . $params['error'] . "<br>\n"; |
372
|
|
|
echo 'Query: ' . $params['query'] . "<br>\n"; |
373
|
|
|
throw new Exception('Error - Query', 1); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Identify user's rights |
378
|
|
|
* |
379
|
|
|
* @param string|array $groupesVisiblesUser [description] |
380
|
|
|
* @param string|array $groupesInterditsUser [description] |
381
|
|
|
* @param string $isAdmin [description] |
382
|
|
|
* @param string $idFonctions [description] |
383
|
|
|
* |
384
|
|
|
* @return bool |
385
|
|
|
*/ |
386
|
|
|
function identifyUserRights( |
387
|
|
|
$groupesVisiblesUser, |
388
|
|
|
$groupesInterditsUser, |
389
|
|
|
$isAdmin, |
390
|
|
|
$idFonctions, |
391
|
|
|
$SETTINGS |
392
|
|
|
) { |
393
|
|
|
//load ClassLoader |
394
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
395
|
|
|
// Load superglobal |
396
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
397
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
398
|
|
|
//Connect to DB |
399
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
400
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
401
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
402
|
|
|
} |
403
|
|
|
DB::$host = DB_HOST; |
404
|
|
|
DB::$user = DB_USER; |
405
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
406
|
|
|
DB::$dbName = DB_NAME; |
407
|
|
|
DB::$port = DB_PORT; |
408
|
|
|
DB::$encoding = DB_ENCODING; |
409
|
|
|
//Build tree |
410
|
|
|
$tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries'); |
411
|
|
|
$tree->register(); |
412
|
|
|
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
413
|
|
|
|
414
|
|
|
// Check if user is ADMINISTRATOR |
415
|
|
|
(int) $isAdmin === 1 ? |
416
|
|
|
identAdmin( |
417
|
|
|
$idFonctions, |
418
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
419
|
|
|
$tree |
420
|
|
|
) |
421
|
|
|
: |
422
|
|
|
identUser( |
423
|
|
|
$groupesVisiblesUser, |
424
|
|
|
$groupesInterditsUser, |
425
|
|
|
$idFonctions, |
426
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
427
|
|
|
$tree |
428
|
|
|
); |
429
|
|
|
|
430
|
|
|
// update user's timestamp |
431
|
|
|
DB::update( |
432
|
|
|
prefixTable('users'), |
433
|
|
|
[ |
434
|
|
|
'timestamp' => time(), |
435
|
|
|
], |
436
|
|
|
'id=%i', |
437
|
|
|
$superGlobal->get('user_id', 'SESSION') |
438
|
|
|
); |
439
|
|
|
|
440
|
|
|
return true; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* Identify administrator. |
445
|
|
|
* |
446
|
|
|
* @param string $idFonctions Roles of user |
447
|
|
|
* @param array $SETTINGS Teampass settings |
448
|
|
|
* @param array $tree Tree of folders |
449
|
|
|
* |
450
|
|
|
* @return bool |
451
|
|
|
*/ |
452
|
|
|
function identAdmin($idFonctions, $SETTINGS, $tree) |
453
|
|
|
{ |
454
|
|
|
// Load superglobal |
455
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
456
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
457
|
|
|
// Init |
458
|
|
|
$groupesVisibles = []; |
459
|
|
|
$superGlobal->put('personal_folders', [], 'SESSION'); |
460
|
|
|
$superGlobal->put('groupes_visibles', [], 'SESSION'); |
461
|
|
|
$superGlobal->put('no_access_folders', [], 'SESSION'); |
462
|
|
|
$superGlobal->put('personal_visible_groups', [], 'SESSION'); |
463
|
|
|
$superGlobal->put('read_only_folders', [], 'SESSION'); |
464
|
|
|
$superGlobal->put('list_restricted_folders_for_items', [], 'SESSION'); |
465
|
|
|
$superGlobal->put('list_folders_editable_by_role', [], 'SESSION'); |
466
|
|
|
$superGlobal->put('list_folders_limited', [], 'SESSION'); |
467
|
|
|
$superGlobal->put('no_access_folders', [], 'SESSION'); |
468
|
|
|
$superGlobal->put('forbiden_pfs', [], 'SESSION'); |
469
|
|
|
// Get superglobals |
470
|
|
|
$globalsUserId = $superGlobal->get('user_id', 'SESSION'); |
471
|
|
|
$globalsVisibleFolders = $superGlobal->get('groupes_visibles', 'SESSION'); |
472
|
|
|
$globalsPersonalVisibleFolders = $superGlobal->get('personal_visible_groups', 'SESSION'); |
473
|
|
|
// Get list of Folders |
474
|
|
|
$rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0); |
475
|
|
|
foreach ($rows as $record) { |
476
|
|
|
array_push($groupesVisibles, $record['id']); |
477
|
|
|
} |
478
|
|
|
$superGlobal->put('groupes_visibles', $groupesVisibles, 'SESSION'); |
479
|
|
|
$superGlobal->put('all_non_personal_folders', $groupesVisibles, 'SESSION'); |
480
|
|
|
// Exclude all PF |
481
|
|
|
$where = new WhereClause('and'); |
482
|
|
|
// create a WHERE statement of pieces joined by ANDs |
483
|
|
|
$where->add('personal_folder=%i', 1); |
484
|
|
|
if ( |
485
|
|
|
isset($SETTINGS['enable_pf_feature']) === true |
486
|
|
|
&& (int) $SETTINGS['enable_pf_feature'] === 1 |
487
|
|
|
) { |
488
|
|
|
$where->add('title=%s', $globalsUserId); |
489
|
|
|
$where->negateLast(); |
490
|
|
|
} |
491
|
|
|
// Get ID of personal folder |
492
|
|
|
$persfld = DB::queryfirstrow( |
493
|
|
|
'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s', |
494
|
|
|
$globalsUserId |
495
|
|
|
); |
496
|
|
|
if (empty($persfld['id']) === false) { |
497
|
|
|
if (in_array($persfld['id'], $globalsVisibleFolders) === false) { |
498
|
|
|
array_push($globalsVisibleFolders, $persfld['id']); |
499
|
|
|
array_push($globalsPersonalVisibleFolders, $persfld['id']); |
500
|
|
|
// get all descendants |
501
|
|
|
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
502
|
|
|
$tree->rebuild(); |
503
|
|
|
$tst = $tree->getDescendants($persfld['id']); |
504
|
|
|
foreach ($tst as $t) { |
505
|
|
|
array_push($globalsVisibleFolders, $t->id); |
506
|
|
|
array_push($globalsPersonalVisibleFolders, $t->id); |
507
|
|
|
} |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
// get complete list of ROLES |
512
|
|
|
$tmp = explode(';', $idFonctions); |
513
|
|
|
$rows = DB::query( |
514
|
|
|
'SELECT * FROM ' . prefixTable('roles_title') . ' |
515
|
|
|
ORDER BY title ASC' |
516
|
|
|
); |
517
|
|
|
foreach ($rows as $record) { |
518
|
|
|
if (! empty($record['id']) && ! in_array($record['id'], $tmp)) { |
519
|
|
|
array_push($tmp, $record['id']); |
520
|
|
|
} |
521
|
|
|
} |
522
|
|
|
$superGlobal->put('fonction_id', implode(';', $tmp), 'SESSION'); |
523
|
|
|
$superGlobal->put('is_admin', 1, 'SESSION'); |
524
|
|
|
// Check if admin has created Folders and Roles |
525
|
|
|
DB::query('SELECT * FROM ' . prefixTable('nested_tree') . ''); |
526
|
|
|
$superGlobal->put('nb_folders', DB::count(), 'SESSION'); |
527
|
|
|
DB::query('SELECT * FROM ' . prefixTable('roles_title')); |
528
|
|
|
$superGlobal->put('nb_roles', DB::count(), 'SESSION'); |
529
|
|
|
|
530
|
|
|
return true; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Permits to convert an element to array. |
535
|
|
|
* |
536
|
|
|
* @param string|array $element Any value to be returned as array |
537
|
|
|
* |
538
|
|
|
* @return array |
539
|
|
|
*/ |
540
|
|
|
function convertToArray($element): array |
541
|
|
|
{ |
542
|
|
|
if (is_string($element) === true) { |
543
|
|
|
if (empty($element) === true) { |
544
|
|
|
return []; |
545
|
|
|
} |
546
|
|
|
return explode( |
547
|
|
|
';', |
548
|
|
|
trimElement($element, ';') |
549
|
|
|
); |
550
|
|
|
} |
551
|
|
|
return $element; |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* Defines the rights the user has. |
556
|
|
|
* |
557
|
|
|
* @param string|array $allowedFolders Allowed folders |
558
|
|
|
* @param string|array $noAccessFolders Not allowed folders |
559
|
|
|
* @param string|array $userRoles Roles of user |
560
|
|
|
* @param array $SETTINGS Teampass settings |
561
|
|
|
* @param object $tree Tree of folders |
562
|
|
|
* |
563
|
|
|
* @return bool |
564
|
|
|
*/ |
565
|
|
|
function identUser( |
566
|
|
|
$allowedFolders, |
567
|
|
|
$noAccessFolders, |
568
|
|
|
$userRoles, |
569
|
|
|
array $SETTINGS, |
570
|
|
|
object $tree |
571
|
|
|
) { |
572
|
|
|
// Load superglobal |
573
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
574
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
575
|
|
|
// Init |
576
|
|
|
$superGlobal->put('groupes_visibles', [], 'SESSION'); |
577
|
|
|
$superGlobal->put('personal_folders', [], 'SESSION'); |
578
|
|
|
$superGlobal->put('no_access_folders', [], 'SESSION'); |
579
|
|
|
$superGlobal->put('personal_visible_groups', [], 'SESSION'); |
580
|
|
|
$superGlobal->put('read_only_folders', [], 'SESSION'); |
581
|
|
|
$superGlobal->put('fonction_id', $userRoles, 'SESSION'); |
582
|
|
|
$superGlobal->put('is_admin', 0, 'SESSION'); |
583
|
|
|
// init |
584
|
|
|
$personalFolders = []; |
585
|
|
|
$readOnlyFolders = []; |
586
|
|
|
$noAccessPersonalFolders = []; |
587
|
|
|
$restrictedFoldersForItems = []; |
588
|
|
|
$foldersLimited = []; |
589
|
|
|
$foldersLimitedFull = []; |
590
|
|
|
$allowedFoldersByRoles = []; |
591
|
|
|
// Get superglobals |
592
|
|
|
$globalsUserId = $superGlobal->get('user_id', 'SESSION'); |
593
|
|
|
$globalsPersonalFolders = $superGlobal->get('personal_folder', 'SESSION'); |
594
|
|
|
// Ensure consistency in array format |
595
|
|
|
$noAccessFolders = convertToArray($noAccessFolders); |
596
|
|
|
$userRoles = convertToArray($userRoles); |
597
|
|
|
$allowedFolders = convertToArray($allowedFolders); |
598
|
|
|
|
599
|
|
|
// Get list of folders depending on Roles |
600
|
|
|
$arrays = identUserGetFoldersFromRoles( |
601
|
|
|
$userRoles, |
602
|
|
|
$allowedFoldersByRoles, |
603
|
|
|
$readOnlyFolders, |
604
|
|
|
$allowedFolders |
605
|
|
|
); |
606
|
|
|
$allowedFoldersByRoles = $arrays['allowedFoldersByRoles']; |
607
|
|
|
$readOnlyFolders = $arrays['readOnlyFolders']; |
608
|
|
|
|
609
|
|
|
// Does this user is allowed to see other items |
610
|
|
|
$inc = 0; |
611
|
|
|
$rows = DB::query( |
612
|
|
|
'SELECT id, id_tree FROM ' . prefixTable('items') . ' |
613
|
|
|
WHERE restricted_to LIKE %ss AND inactif = %s'. |
614
|
|
|
(count($allowedFolders) > 0 ? ' AND id_tree NOT IN ('.implode(',', $allowedFolders).')' : ''), |
615
|
|
|
$globalsUserId . ';', |
616
|
|
|
'0' |
617
|
|
|
); |
618
|
|
|
foreach ($rows as $record) { |
619
|
|
|
// Exclude restriction on item if folder is fully accessible |
620
|
|
|
//if (in_array($record['id_tree'], $allowedFolders) === false) { |
621
|
|
|
$restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id']; |
622
|
|
|
++$inc; |
623
|
|
|
//} |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
// Check for the users roles if some specific rights exist on items |
627
|
|
|
$rows = DB::query( |
628
|
|
|
'SELECT i.id_tree, r.item_id |
629
|
|
|
FROM ' . prefixTable('items') . ' as i |
630
|
|
|
INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id) |
631
|
|
|
WHERE r.role_id IN %li AND i.id_tree <> "" |
632
|
|
|
ORDER BY i.id_tree ASC', |
633
|
|
|
$userRoles |
634
|
|
|
); |
635
|
|
|
$inc = 0; |
636
|
|
|
foreach ($rows as $record) { |
637
|
|
|
//if (isset($record['id_tree'])) { |
638
|
|
|
$foldersLimited[$record['id_tree']][$inc] = $record['item_id']; |
639
|
|
|
array_push($foldersLimitedFull, $record['item_id']); |
640
|
|
|
++$inc; |
641
|
|
|
//} |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
// Get list of Personal Folders |
645
|
|
|
$arrays = identUserGetPFList( |
646
|
|
|
$globalsPersonalFolders, |
647
|
|
|
$allowedFolders, |
648
|
|
|
$globalsUserId, |
649
|
|
|
$personalFolders, |
650
|
|
|
$noAccessPersonalFolders, |
651
|
|
|
$foldersLimitedFull, |
652
|
|
|
$allowedFoldersByRoles, |
653
|
|
|
$restrictedFoldersForItems, |
654
|
|
|
$readOnlyFolders, |
655
|
|
|
$noAccessFolders |
656
|
|
|
); |
657
|
|
|
$allowedFolders = $arrays['allowedFolders']; |
658
|
|
|
$personalFolders = $arrays['personalFolders']; |
659
|
|
|
$noAccessPersonalFolders = $arrays['noAccessPersonalFolders']; |
660
|
|
|
|
661
|
|
|
// Return data |
662
|
|
|
$superGlobal->put('all_non_personal_folders', $allowedFolders, 'SESSION'); |
663
|
|
|
$superGlobal->put('groupes_visibles', array_merge($allowedFolders, $personalFolders), 'SESSION'); |
664
|
|
|
$superGlobal->put('read_only_folders', $readOnlyFolders, 'SESSION'); |
665
|
|
|
$superGlobal->put('no_access_folders', $noAccessFolders, 'SESSION'); |
666
|
|
|
$superGlobal->put('personal_folders', $personalFolders, 'SESSION'); |
667
|
|
|
$superGlobal->put('list_folders_limited', $foldersLimited, 'SESSION'); |
668
|
|
|
$superGlobal->put('list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION'); |
669
|
|
|
$superGlobal->put('list_restricted_folders_for_items', $restrictedFoldersForItems, 'SESSION'); |
670
|
|
|
$superGlobal->put('forbiden_pfs', $noAccessPersonalFolders, 'SESSION'); |
671
|
|
|
$superGlobal->put( |
672
|
|
|
'all_folders_including_no_access', |
673
|
|
|
array_merge( |
674
|
|
|
$allowedFolders, |
675
|
|
|
$personalFolders, |
676
|
|
|
$noAccessFolders, |
677
|
|
|
$readOnlyFolders |
678
|
|
|
), |
679
|
|
|
'SESSION' |
680
|
|
|
); |
681
|
|
|
// Folders and Roles numbers |
682
|
|
|
DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . ''); |
683
|
|
|
$superGlobal->put('nb_folders', DB::count(), 'SESSION'); |
684
|
|
|
DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title')); |
685
|
|
|
$superGlobal->put('nb_roles', DB::count(), 'SESSION'); |
686
|
|
|
// check if change proposals on User's items |
687
|
|
|
if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) { |
688
|
|
|
$countNewItems = DB::query( |
689
|
|
|
'SELECT COUNT(*) |
690
|
|
|
FROM ' . prefixTable('items_change') . ' AS c |
691
|
|
|
LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item) |
692
|
|
|
WHERE i.action = %s AND i.id_user = %i', |
693
|
|
|
'at_creation', |
694
|
|
|
$globalsUserId |
695
|
|
|
); |
696
|
|
|
$superGlobal->put('nb_item_change_proposals', $countNewItems, 'SESSION'); |
697
|
|
|
} else { |
698
|
|
|
$superGlobal->put('nb_item_change_proposals', 0, 'SESSION'); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
return true; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/** |
705
|
|
|
* Get list of folders depending on Roles |
706
|
|
|
* |
707
|
|
|
* @param array $userRoles |
708
|
|
|
* @param array $allowedFoldersByRoles |
709
|
|
|
* @param array $readOnlyFolders |
710
|
|
|
* @param array $allowedFolders |
711
|
|
|
* |
712
|
|
|
* @return array |
713
|
|
|
*/ |
714
|
|
|
function identUserGetFoldersFromRoles($userRoles, $allowedFoldersByRoles, $readOnlyFolders, $allowedFolders) : array |
715
|
|
|
{ |
716
|
|
|
|
717
|
|
|
$rows = DB::query( |
718
|
|
|
'SELECT * |
719
|
|
|
FROM ' . prefixTable('roles_values') . ' |
720
|
|
|
WHERE role_id IN %li AND type IN %ls', |
721
|
|
|
$userRoles, |
722
|
|
|
['W', 'ND', 'NE', 'NDNE', 'R'] |
723
|
|
|
); |
724
|
|
|
foreach ($rows as $record) { |
725
|
|
|
if ($record['type'] === 'R') { |
726
|
|
|
array_push($readOnlyFolders, $record['folder_id']); |
727
|
|
|
} elseif (in_array($record['folder_id'], $allowedFolders) === false) { |
728
|
|
|
array_push($allowedFoldersByRoles, $record['folder_id']); |
729
|
|
|
} |
730
|
|
|
} |
731
|
|
|
$allowedFoldersByRoles = array_unique($allowedFoldersByRoles); |
732
|
|
|
$readOnlyFolders = array_unique($readOnlyFolders); |
733
|
|
|
// Clean arrays |
734
|
|
|
foreach ($allowedFoldersByRoles as $value) { |
735
|
|
|
$key = array_search($value, $readOnlyFolders); |
736
|
|
|
if ($key !== false) { |
737
|
|
|
unset($readOnlyFolders[$key]); |
738
|
|
|
} |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
return [ |
742
|
|
|
'readOnlyFolders' => $readOnlyFolders, |
743
|
|
|
'allowedFoldersByRoles' => $allowedFoldersByRoles |
744
|
|
|
]; |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
/** |
748
|
|
|
* Get list of Personal Folders |
749
|
|
|
* |
750
|
|
|
* @param int $globalsPersonalFolders |
751
|
|
|
* @param array $allowedFolders |
752
|
|
|
* @param int $globalsUserId |
753
|
|
|
* @param array $personalFolders |
754
|
|
|
* @param array $noAccessPersonalFolders |
755
|
|
|
* @param array $foldersLimitedFull |
756
|
|
|
* @param array $allowedFoldersByRoles |
757
|
|
|
* @param array $restrictedFoldersForItems |
758
|
|
|
* @param array $readOnlyFolders |
759
|
|
|
* @param array $noAccessFolders |
760
|
|
|
* |
761
|
|
|
* @return array |
762
|
|
|
*/ |
763
|
|
|
function identUserGetPFList( |
764
|
|
|
$globalsPersonalFolders, |
765
|
|
|
$allowedFolders, |
766
|
|
|
$globalsUserId, |
767
|
|
|
$personalFolders, |
768
|
|
|
$noAccessPersonalFolders, |
769
|
|
|
$foldersLimitedFull, |
770
|
|
|
$allowedFoldersByRoles, |
771
|
|
|
$restrictedFoldersForItems, |
772
|
|
|
$readOnlyFolders, |
773
|
|
|
$noAccessFolders |
774
|
|
|
) |
775
|
|
|
{ |
776
|
|
|
// |
777
|
|
|
if ( |
778
|
|
|
isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1 |
|
|
|
|
779
|
|
|
&& isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1 |
780
|
|
|
) { |
781
|
|
|
$persoFld = DB::queryfirstrow( |
782
|
|
|
'SELECT id |
783
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
784
|
|
|
WHERE title = %s AND personal_folder = %i'. |
785
|
|
|
(count($allowedFolders) > 0 ? ' AND id NOT IN ('.implode(',', $allowedFolders).')' : ''), |
786
|
|
|
$globalsUserId, |
787
|
|
|
1 |
788
|
|
|
); |
789
|
|
|
if (empty($persoFld['id']) === false) { |
790
|
|
|
//if (in_array($persoFld['id'], $allowedFolders) === false) { |
791
|
|
|
array_push($personalFolders, $persoFld['id']); |
792
|
|
|
array_push($allowedFolders, $persoFld['id']); |
793
|
|
|
// get all descendants |
794
|
|
|
$ids = $tree->getChildren($persoFld['id'], false); |
|
|
|
|
795
|
|
|
foreach ($ids as $ident) { |
796
|
|
|
if ((int) $ident->personal_folder === 1) { |
797
|
|
|
array_push($allowedFolders, $ident->id); |
798
|
|
|
array_push($personalFolders, $ident->id); |
799
|
|
|
} |
800
|
|
|
} |
801
|
|
|
//} |
802
|
|
|
} |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
// Exclude all other PF |
806
|
|
|
$where = new WhereClause('and'); |
807
|
|
|
$where->add('personal_folder=%i', 1); |
808
|
|
|
if ( |
809
|
|
|
isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1 |
810
|
|
|
&& isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1 |
811
|
|
|
) { |
812
|
|
|
$where->add('title=%s', $globalsUserId); |
813
|
|
|
$where->negateLast(); |
814
|
|
|
} |
815
|
|
|
$persoFlds = DB::query( |
816
|
|
|
'SELECT id |
817
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
818
|
|
|
WHERE %l', |
819
|
|
|
$where |
820
|
|
|
); |
821
|
|
|
foreach ($persoFlds as $persoFldId) { |
822
|
|
|
array_push($noAccessPersonalFolders, $persoFldId['id']); |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
// All folders visibles |
826
|
|
|
$allowedFolders = array_merge( |
827
|
|
|
$allowedFolders, |
828
|
|
|
$foldersLimitedFull, |
829
|
|
|
$allowedFoldersByRoles, |
830
|
|
|
$restrictedFoldersForItems, |
831
|
|
|
$readOnlyFolders |
832
|
|
|
); |
833
|
|
|
// Exclude from allowed folders all the specific user forbidden folders |
834
|
|
|
if (count($noAccessFolders) > 0) { |
835
|
|
|
$allowedFolders = array_diff($allowedFolders, $noAccessFolders); |
836
|
|
|
} |
837
|
|
|
|
838
|
|
|
return [ |
839
|
|
|
'allowedFolders' => $allowedFolders, |
840
|
|
|
'personalFolders' => $personalFolders, |
841
|
|
|
'noAccessPersonalFolders' => $noAccessPersonalFolders |
842
|
|
|
]; |
843
|
|
|
} |
844
|
|
|
|
845
|
|
|
|
846
|
|
|
/** |
847
|
|
|
* Update the CACHE table. |
848
|
|
|
* |
849
|
|
|
* @param string $action What to do |
850
|
|
|
* @param array $SETTINGS Teampass settings |
851
|
|
|
* @param int $ident Ident format |
852
|
|
|
* |
853
|
|
|
* @return void |
854
|
|
|
*/ |
855
|
|
|
function updateCacheTable(string $action, array $SETTINGS, ?int $ident = null): void |
856
|
|
|
{ |
857
|
|
|
if ($action === 'reload') { |
858
|
|
|
// Rebuild full cache table |
859
|
|
|
cacheTableRefresh($SETTINGS); |
860
|
|
|
} elseif ($action === 'update_value' && is_null($ident) === false) { |
861
|
|
|
// UPDATE an item |
862
|
|
|
cacheTableUpdate($SETTINGS, $ident); |
863
|
|
|
} elseif ($action === 'add_value' && is_null($ident) === false) { |
864
|
|
|
// ADD an item |
865
|
|
|
cacheTableAdd($SETTINGS, $ident); |
866
|
|
|
} elseif ($action === 'delete_value' && is_null($ident) === false) { |
867
|
|
|
// DELETE an item |
868
|
|
|
DB::delete(prefixTable('cache'), 'id = %i', $ident); |
869
|
|
|
} |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
/** |
873
|
|
|
* Cache table - refresh. |
874
|
|
|
* |
875
|
|
|
* @param array $SETTINGS Teampass settings |
876
|
|
|
* |
877
|
|
|
* @return void |
878
|
|
|
*/ |
879
|
|
|
function cacheTableRefresh(array $SETTINGS): void |
880
|
|
|
{ |
881
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
882
|
|
|
//Connect to DB |
883
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
884
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
885
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
886
|
|
|
} |
887
|
|
|
DB::$host = DB_HOST; |
888
|
|
|
DB::$user = DB_USER; |
889
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
890
|
|
|
DB::$dbName = DB_NAME; |
891
|
|
|
DB::$port = DB_PORT; |
892
|
|
|
DB::$encoding = DB_ENCODING; |
893
|
|
|
//Load Tree |
894
|
|
|
$tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries'); |
895
|
|
|
$tree->register(); |
896
|
|
|
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
897
|
|
|
// truncate table |
898
|
|
|
DB::query('TRUNCATE TABLE ' . prefixTable('cache')); |
899
|
|
|
// reload date |
900
|
|
|
$rows = DB::query( |
901
|
|
|
'SELECT * |
902
|
|
|
FROM ' . prefixTable('items') . ' as i |
903
|
|
|
INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id) |
904
|
|
|
AND l.action = %s |
905
|
|
|
AND i.inactif = %i', |
906
|
|
|
'at_creation', |
907
|
|
|
0 |
908
|
|
|
); |
909
|
|
|
foreach ($rows as $record) { |
910
|
|
|
if (empty($record['id_tree']) === false) { |
911
|
|
|
// Get all TAGS |
912
|
|
|
$tags = ''; |
913
|
|
|
$itemTags = DB::query( |
914
|
|
|
'SELECT tag |
915
|
|
|
FROM ' . prefixTable('tags') . ' |
916
|
|
|
WHERE item_id = %i AND tag != ""', |
917
|
|
|
$record['id'] |
918
|
|
|
); |
919
|
|
|
foreach ($itemTags as $itemTag) { |
920
|
|
|
$tags .= $itemTag['tag'] . ' '; |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
// Get renewal period |
924
|
|
|
$resNT = DB::queryfirstrow( |
925
|
|
|
'SELECT renewal_period |
926
|
|
|
FROM ' . prefixTable('nested_tree') . ' |
927
|
|
|
WHERE id = %i', |
928
|
|
|
$record['id_tree'] |
929
|
|
|
); |
930
|
|
|
// form id_tree to full foldername |
931
|
|
|
$folder = []; |
932
|
|
|
$arbo = $tree->getPath($record['id_tree'], true); |
933
|
|
|
foreach ($arbo as $elem) { |
934
|
|
|
// Check if title is the ID of a user |
935
|
|
|
if (is_numeric($elem->title) === true) { |
936
|
|
|
// Is this a User id? |
937
|
|
|
$user = DB::queryfirstrow( |
938
|
|
|
'SELECT id, login |
939
|
|
|
FROM ' . prefixTable('users') . ' |
940
|
|
|
WHERE id = %i', |
941
|
|
|
$elem->title |
942
|
|
|
); |
943
|
|
|
if (count($user) > 0) { |
944
|
|
|
$elem->title = $user['login']; |
945
|
|
|
} |
946
|
|
|
} |
947
|
|
|
// Build path |
948
|
|
|
array_push($folder, stripslashes($elem->title)); |
949
|
|
|
} |
950
|
|
|
// store data |
951
|
|
|
DB::insert( |
952
|
|
|
prefixTable('cache'), |
953
|
|
|
[ |
954
|
|
|
'id' => $record['id'], |
955
|
|
|
'label' => $record['label'], |
956
|
|
|
'description' => $record['description'] ?? '', |
957
|
|
|
'url' => isset($record['url']) && ! empty($record['url']) ? $record['url'] : '0', |
958
|
|
|
'tags' => $tags, |
959
|
|
|
'id_tree' => $record['id_tree'], |
960
|
|
|
'perso' => $record['perso'], |
961
|
|
|
'restricted_to' => isset($record['restricted_to']) && ! empty($record['restricted_to']) ? $record['restricted_to'] : '0', |
962
|
|
|
'login' => $record['login'] ?? '', |
963
|
|
|
'folder' => implode(' > ', $folder), |
964
|
|
|
'author' => $record['id_user'], |
965
|
|
|
'renewal_period' => $resNT['renewal_period'] ?? '0', |
966
|
|
|
'timestamp' => $record['date'], |
967
|
|
|
] |
968
|
|
|
); |
969
|
|
|
} |
970
|
|
|
} |
971
|
|
|
} |
972
|
|
|
|
973
|
|
|
/** |
974
|
|
|
* Cache table - update existing value. |
975
|
|
|
* |
976
|
|
|
* @param array $SETTINGS Teampass settings |
977
|
|
|
* @param int $ident Ident format |
978
|
|
|
* |
979
|
|
|
* @return void |
980
|
|
|
*/ |
981
|
|
|
function cacheTableUpdate(array $SETTINGS, ?int $ident = null): void |
982
|
|
|
{ |
983
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
984
|
|
|
// Load superglobal |
985
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
986
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
987
|
|
|
//Connect to DB |
988
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
989
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
990
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
991
|
|
|
} |
992
|
|
|
DB::$host = DB_HOST; |
993
|
|
|
DB::$user = DB_USER; |
994
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
995
|
|
|
DB::$dbName = DB_NAME; |
996
|
|
|
DB::$port = DB_PORT; |
997
|
|
|
DB::$encoding = DB_ENCODING; |
998
|
|
|
//Load Tree |
999
|
|
|
$tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries'); |
1000
|
|
|
$tree->register(); |
1001
|
|
|
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
1002
|
|
|
// get new value from db |
1003
|
|
|
$data = DB::queryfirstrow( |
1004
|
|
|
'SELECT label, description, id_tree, perso, restricted_to, login, url |
1005
|
|
|
FROM ' . prefixTable('items') . ' |
1006
|
|
|
WHERE id=%i', |
1007
|
|
|
$ident |
1008
|
|
|
); |
1009
|
|
|
// Get all TAGS |
1010
|
|
|
$tags = ''; |
1011
|
|
|
$itemTags = DB::query( |
1012
|
|
|
'SELECT tag |
1013
|
|
|
FROM ' . prefixTable('tags') . ' |
1014
|
|
|
WHERE item_id = %i AND tag != ""', |
1015
|
|
|
$ident |
1016
|
|
|
); |
1017
|
|
|
foreach ($itemTags as $itemTag) { |
1018
|
|
|
$tags .= $itemTag['tag'] . ' '; |
1019
|
|
|
} |
1020
|
|
|
// form id_tree to full foldername |
1021
|
|
|
$folder = []; |
1022
|
|
|
$arbo = $tree->getPath($data['id_tree'], true); |
1023
|
|
|
foreach ($arbo as $elem) { |
1024
|
|
|
// Check if title is the ID of a user |
1025
|
|
|
if (is_numeric($elem->title) === true) { |
1026
|
|
|
// Is this a User id? |
1027
|
|
|
$user = DB::queryfirstrow( |
1028
|
|
|
'SELECT id, login |
1029
|
|
|
FROM ' . prefixTable('users') . ' |
1030
|
|
|
WHERE id = %i', |
1031
|
|
|
$elem->title |
1032
|
|
|
); |
1033
|
|
|
if (count($user) > 0) { |
1034
|
|
|
$elem->title = $user['login']; |
1035
|
|
|
} |
1036
|
|
|
} |
1037
|
|
|
// Build path |
1038
|
|
|
array_push($folder, stripslashes($elem->title)); |
1039
|
|
|
} |
1040
|
|
|
// finaly update |
1041
|
|
|
DB::update( |
1042
|
|
|
prefixTable('cache'), |
1043
|
|
|
[ |
1044
|
|
|
'label' => $data['label'], |
1045
|
|
|
'description' => $data['description'], |
1046
|
|
|
'tags' => $tags, |
1047
|
|
|
'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0', |
1048
|
|
|
'id_tree' => $data['id_tree'], |
1049
|
|
|
'perso' => $data['perso'], |
1050
|
|
|
'restricted_to' => isset($data['restricted_to']) && ! empty($data['restricted_to']) ? $data['restricted_to'] : '0', |
1051
|
|
|
'login' => $data['login'] ?? '', |
1052
|
|
|
'folder' => implode(' » ', $folder), |
1053
|
|
|
'author' => $superGlobal->get('user_id', 'SESSION'), |
1054
|
|
|
], |
1055
|
|
|
'id = %i', |
1056
|
|
|
$ident |
1057
|
|
|
); |
1058
|
|
|
} |
1059
|
|
|
|
1060
|
|
|
/** |
1061
|
|
|
* Cache table - add new value. |
1062
|
|
|
* |
1063
|
|
|
* @param array $SETTINGS Teampass settings |
1064
|
|
|
* @param int $ident Ident format |
1065
|
|
|
* |
1066
|
|
|
* @return void |
1067
|
|
|
*/ |
1068
|
|
|
function cacheTableAdd(array $SETTINGS, ?int $ident = null): void |
1069
|
|
|
{ |
1070
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
1071
|
|
|
// Load superglobal |
1072
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
1073
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
1074
|
|
|
// Get superglobals |
1075
|
|
|
$globalsUserId = $superGlobal->get('user_id', 'SESSION'); |
1076
|
|
|
//Connect to DB |
1077
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
1078
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
1079
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
1080
|
|
|
} |
1081
|
|
|
DB::$host = DB_HOST; |
1082
|
|
|
DB::$user = DB_USER; |
1083
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
1084
|
|
|
DB::$dbName = DB_NAME; |
1085
|
|
|
DB::$port = DB_PORT; |
1086
|
|
|
DB::$encoding = DB_ENCODING; |
1087
|
|
|
//Load Tree |
1088
|
|
|
$tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries'); |
1089
|
|
|
$tree->register(); |
1090
|
|
|
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
1091
|
|
|
// get new value from db |
1092
|
|
|
$data = DB::queryFirstRow( |
1093
|
|
|
'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date |
1094
|
|
|
FROM ' . prefixTable('items') . ' as i |
1095
|
|
|
INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id) |
1096
|
|
|
WHERE i.id = %i |
1097
|
|
|
AND l.action = %s', |
1098
|
|
|
$ident, |
1099
|
|
|
'at_creation' |
1100
|
|
|
); |
1101
|
|
|
// Get all TAGS |
1102
|
|
|
$tags = ''; |
1103
|
|
|
$itemTags = DB::query( |
1104
|
|
|
'SELECT tag |
1105
|
|
|
FROM ' . prefixTable('tags') . ' |
1106
|
|
|
WHERE item_id = %i AND tag != ""', |
1107
|
|
|
$ident |
1108
|
|
|
); |
1109
|
|
|
foreach ($itemTags as $itemTag) { |
1110
|
|
|
$tags .= $itemTag['tag'] . ' '; |
1111
|
|
|
} |
1112
|
|
|
// form id_tree to full foldername |
1113
|
|
|
$folder = []; |
1114
|
|
|
$arbo = $tree->getPath($data['id_tree'], true); |
1115
|
|
|
foreach ($arbo as $elem) { |
1116
|
|
|
// Check if title is the ID of a user |
1117
|
|
|
if (is_numeric($elem->title) === true) { |
1118
|
|
|
// Is this a User id? |
1119
|
|
|
$user = DB::queryfirstrow( |
1120
|
|
|
'SELECT id, login |
1121
|
|
|
FROM ' . prefixTable('users') . ' |
1122
|
|
|
WHERE id = %i', |
1123
|
|
|
$elem->title |
1124
|
|
|
); |
1125
|
|
|
if (count($user) > 0) { |
1126
|
|
|
$elem->title = $user['login']; |
1127
|
|
|
} |
1128
|
|
|
} |
1129
|
|
|
// Build path |
1130
|
|
|
array_push($folder, stripslashes($elem->title)); |
1131
|
|
|
} |
1132
|
|
|
// finaly update |
1133
|
|
|
DB::insert( |
1134
|
|
|
prefixTable('cache'), |
1135
|
|
|
[ |
1136
|
|
|
'id' => $data['id'], |
1137
|
|
|
'label' => $data['label'], |
1138
|
|
|
'description' => $data['description'], |
1139
|
|
|
'tags' => isset($tags) && empty($tags) === false ? $tags : 'None', |
1140
|
|
|
'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0', |
1141
|
|
|
'id_tree' => $data['id_tree'], |
1142
|
|
|
'perso' => isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None' ? $data['perso'] : '0', |
1143
|
|
|
'restricted_to' => isset($data['restricted_to']) && empty($data['restricted_to']) === false ? $data['restricted_to'] : '0', |
1144
|
|
|
'login' => $data['login'] ?? '', |
1145
|
|
|
'folder' => implode(' » ', $folder), |
1146
|
|
|
'author' => $globalsUserId, |
1147
|
|
|
'timestamp' => $data['date'], |
1148
|
|
|
] |
1149
|
|
|
); |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
/** |
1153
|
|
|
* Do statistics. |
1154
|
|
|
* |
1155
|
|
|
* @param array $SETTINGS Teampass settings |
1156
|
|
|
* |
1157
|
|
|
* @return array |
1158
|
|
|
*/ |
1159
|
|
|
function getStatisticsData(array $SETTINGS): array |
1160
|
|
|
{ |
1161
|
|
|
DB::query( |
1162
|
|
|
'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', |
1163
|
|
|
0 |
1164
|
|
|
); |
1165
|
|
|
$counter_folders = DB::count(); |
1166
|
|
|
DB::query( |
1167
|
|
|
'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', |
1168
|
|
|
1 |
1169
|
|
|
); |
1170
|
|
|
$counter_folders_perso = DB::count(); |
1171
|
|
|
DB::query( |
1172
|
|
|
'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i', |
1173
|
|
|
0 |
1174
|
|
|
); |
1175
|
|
|
$counter_items = DB::count(); |
1176
|
|
|
DB::query( |
1177
|
|
|
'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i', |
1178
|
|
|
1 |
1179
|
|
|
); |
1180
|
|
|
$counter_items_perso = DB::count(); |
1181
|
|
|
DB::query( |
1182
|
|
|
'SELECT id FROM ' . prefixTable('users') . '' |
1183
|
|
|
); |
1184
|
|
|
$counter_users = DB::count(); |
1185
|
|
|
DB::query( |
1186
|
|
|
'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i', |
1187
|
|
|
1 |
1188
|
|
|
); |
1189
|
|
|
$admins = DB::count(); |
1190
|
|
|
DB::query( |
1191
|
|
|
'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i', |
1192
|
|
|
1 |
1193
|
|
|
); |
1194
|
|
|
$managers = DB::count(); |
1195
|
|
|
DB::query( |
1196
|
|
|
'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i', |
1197
|
|
|
1 |
1198
|
|
|
); |
1199
|
|
|
$readOnly = DB::count(); |
1200
|
|
|
// list the languages |
1201
|
|
|
$usedLang = []; |
1202
|
|
|
$tp_languages = DB::query( |
1203
|
|
|
'SELECT name FROM ' . prefixTable('languages') |
1204
|
|
|
); |
1205
|
|
|
foreach ($tp_languages as $tp_language) { |
1206
|
|
|
DB::query( |
1207
|
|
|
'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s', |
1208
|
|
|
$tp_language['name'] |
1209
|
|
|
); |
1210
|
|
|
$usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0); |
1211
|
|
|
} |
1212
|
|
|
|
1213
|
|
|
// get list of ips |
1214
|
|
|
$usedIp = []; |
1215
|
|
|
$tp_ips = DB::query( |
1216
|
|
|
'SELECT user_ip FROM ' . prefixTable('users') |
1217
|
|
|
); |
1218
|
|
|
foreach ($tp_ips as $ip) { |
1219
|
|
|
if (array_key_exists($ip['user_ip'], $usedIp)) { |
1220
|
|
|
$usedIp[$ip['user_ip']] += $usedIp[$ip['user_ip']]; |
1221
|
|
|
} elseif (! empty($ip['user_ip']) && $ip['user_ip'] !== 'none') { |
1222
|
|
|
$usedIp[$ip['user_ip']] = 1; |
1223
|
|
|
} |
1224
|
|
|
} |
1225
|
|
|
|
1226
|
|
|
return [ |
1227
|
|
|
'error' => '', |
1228
|
|
|
'stat_phpversion' => phpversion(), |
1229
|
|
|
'stat_folders' => $counter_folders, |
1230
|
|
|
'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso), |
1231
|
|
|
'stat_items' => $counter_items, |
1232
|
|
|
'stat_items_shared' => intval($counter_items) - intval($counter_items_perso), |
1233
|
|
|
'stat_users' => $counter_users, |
1234
|
|
|
'stat_admins' => $admins, |
1235
|
|
|
'stat_managers' => $managers, |
1236
|
|
|
'stat_ro' => $readOnly, |
1237
|
|
|
'stat_kb' => $SETTINGS['enable_kb'], |
1238
|
|
|
'stat_pf' => $SETTINGS['enable_pf_feature'], |
1239
|
|
|
'stat_fav' => $SETTINGS['enable_favourites'], |
1240
|
|
|
'stat_teampassversion' => TP_VERSION_FULL, |
1241
|
|
|
'stat_ldap' => $SETTINGS['ldap_mode'], |
1242
|
|
|
'stat_agses' => $SETTINGS['agses_authentication_enabled'], |
1243
|
|
|
'stat_duo' => $SETTINGS['duo'], |
1244
|
|
|
'stat_suggestion' => $SETTINGS['enable_suggestion'], |
1245
|
|
|
'stat_api' => $SETTINGS['api'], |
1246
|
|
|
'stat_customfields' => $SETTINGS['item_extra_fields'], |
1247
|
|
|
'stat_syslog' => $SETTINGS['syslog_enable'], |
1248
|
|
|
'stat_2fa' => $SETTINGS['google_authentication'], |
1249
|
|
|
'stat_stricthttps' => $SETTINGS['enable_sts'], |
1250
|
|
|
'stat_mysqlversion' => DB::serverVersion(), |
1251
|
|
|
'stat_languages' => $usedLang, |
1252
|
|
|
'stat_country' => $usedIp, |
1253
|
|
|
]; |
1254
|
|
|
} |
1255
|
|
|
|
1256
|
|
|
/** |
1257
|
|
|
* Permits to send an email. |
1258
|
|
|
* |
1259
|
|
|
* @param string $subject email subject |
1260
|
|
|
* @param string $textMail email message |
1261
|
|
|
* @param string $email email |
1262
|
|
|
* @param array $SETTINGS settings |
1263
|
|
|
* @param string $textMailAlt email message alt |
1264
|
|
|
* @param bool $silent no errors |
1265
|
|
|
* |
1266
|
|
|
* @return string some json info |
1267
|
|
|
*/ |
1268
|
|
|
function sendEmail( |
1269
|
|
|
$subject, |
1270
|
|
|
$textMail, |
1271
|
|
|
$email, |
1272
|
|
|
$SETTINGS, |
1273
|
|
|
$textMailAlt = null, |
1274
|
|
|
$silent = true |
1275
|
|
|
) { |
1276
|
|
|
// CAse where email not defined |
1277
|
|
|
if ($email === 'none' || empty($email) === true) { |
1278
|
|
|
return json_encode( |
1279
|
|
|
[ |
1280
|
|
|
'error' => true, |
1281
|
|
|
'message' => langHdl('forgot_my_pw_email_sent'), |
1282
|
|
|
] |
1283
|
|
|
); |
1284
|
|
|
} |
1285
|
|
|
|
1286
|
|
|
// Load settings |
1287
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php'; |
1288
|
|
|
// Load superglobal |
1289
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
1290
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
1291
|
|
|
// Get user language |
1292
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $superGlobal->get('user_language', 'SESSION') . '.php'; |
1293
|
|
|
// Load library |
1294
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
1295
|
|
|
// load PHPMailer |
1296
|
|
|
$mail = new SplClassLoader('PHPMailer\PHPMailer', '../includes/libraries'); |
1297
|
|
|
$mail->register(); |
1298
|
|
|
$mail = new PHPMailer\PHPMailer\PHPMailer(true); |
1299
|
|
|
|
1300
|
|
|
try { |
1301
|
|
|
// send to user |
1302
|
|
|
$mail->setLanguage('en', $SETTINGS['cpassman_dir'] . '/includes/libraries/PHPMailer/PHPMailer/language/'); |
1303
|
|
|
$mail->SMTPDebug = isset($SETTINGS['email_debug_level']) === true ? $SETTINGS['email_debug_level'] : 0; |
1304
|
|
|
$mail->Port = $SETTINGS['email_port']; |
1305
|
|
|
//COULD BE USED |
1306
|
|
|
$mail->CharSet = 'utf-8'; |
1307
|
|
|
$mail->SMTPSecure = $SETTINGS['email_security'] === 'tls' |
1308
|
|
|
|| $SETTINGS['email_security'] === 'ssl' ? $SETTINGS['email_security'] : ''; |
1309
|
|
|
$mail->SMTPAutoTLS = $SETTINGS['email_security'] === 'tls' |
1310
|
|
|
|| $SETTINGS['email_security'] === 'ssl' ? true : false; |
1311
|
|
|
$mail->SMTPOptions = [ |
1312
|
|
|
'ssl' => [ |
1313
|
|
|
'verify_peer' => false, |
1314
|
|
|
'verify_peer_name' => false, |
1315
|
|
|
'allow_self_signed' => true, |
1316
|
|
|
], |
1317
|
|
|
]; |
1318
|
|
|
$mail->isSmtp(); |
1319
|
|
|
// send via SMTP |
1320
|
|
|
$mail->Host = $SETTINGS['email_smtp_server']; |
1321
|
|
|
// SMTP servers |
1322
|
|
|
$mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1 ? true : false; |
1323
|
|
|
// turn on SMTP authentication |
1324
|
|
|
$mail->Username = $SETTINGS['email_auth_username']; |
1325
|
|
|
// SMTP username |
1326
|
|
|
$mail->Password = $SETTINGS['email_auth_pwd']; |
1327
|
|
|
// SMTP password |
1328
|
|
|
$mail->From = $SETTINGS['email_from']; |
1329
|
|
|
$mail->FromName = $SETTINGS['email_from_name']; |
1330
|
|
|
// Prepare for each person |
1331
|
|
|
foreach (array_filter(explode(',', $email)) as $dest) { |
1332
|
|
|
$mail->addAddress($dest); |
1333
|
|
|
} |
1334
|
|
|
|
1335
|
|
|
// Prepare HTML |
1336
|
|
|
$text_html = emailBody($textMail); |
1337
|
|
|
$mail->WordWrap = 80; |
1338
|
|
|
// set word wrap |
1339
|
|
|
$mail->isHtml(true); |
1340
|
|
|
// send as HTML |
1341
|
|
|
$mail->Subject = $subject; |
1342
|
|
|
$mail->Body = $text_html; |
1343
|
|
|
$mail->AltBody = is_null($textMailAlt) === false ? $textMailAlt : ''; |
1344
|
|
|
// send email |
1345
|
|
|
$mail->send(); |
1346
|
|
|
$mail->smtpClose(); |
1347
|
|
|
if ($silent === false) { |
1348
|
|
|
return json_encode( |
1349
|
|
|
[ |
1350
|
|
|
'error' => false, |
1351
|
|
|
'message' => langHdl('forgot_my_pw_email_sent'), |
1352
|
|
|
] |
1353
|
|
|
); |
1354
|
|
|
} |
1355
|
|
|
// Debug purpose |
1356
|
|
|
if ((int) $SETTINGS['email_debug_level'] !== 0) { |
1357
|
|
|
return json_encode( |
1358
|
|
|
[ |
1359
|
|
|
'error' => true, |
1360
|
|
|
'message' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo), |
1361
|
|
|
] |
1362
|
|
|
); |
1363
|
|
|
} |
1364
|
|
|
} catch (Exception $e) { |
1365
|
|
|
if ($silent === false || (int) $SETTINGS['email_debug_level'] !== 0) { |
1366
|
|
|
return json_encode( |
1367
|
|
|
[ |
1368
|
|
|
'error' => true, |
1369
|
|
|
'message' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo), |
1370
|
|
|
] |
1371
|
|
|
); |
1372
|
|
|
} |
1373
|
|
|
return ''; |
1374
|
|
|
} |
1375
|
|
|
} |
1376
|
|
|
|
1377
|
|
|
/** |
1378
|
|
|
* Returns the email body. |
1379
|
|
|
* |
1380
|
|
|
* @param string $textMail Text for the email |
1381
|
|
|
*/ |
1382
|
|
|
function emailBody(string $textMail): string |
1383
|
|
|
{ |
1384
|
|
|
return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.= |
1385
|
|
|
w3.org/TR/html4/loose.dtd"><html> |
1386
|
|
|
<head><title>Email Template</title> |
1387
|
|
|
<style type="text/css"> |
1388
|
|
|
body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; } |
1389
|
|
|
</style></head> |
1390
|
|
|
<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"> |
1391
|
|
|
<table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;"> |
1392
|
|
|
<tr><td style="border-collapse: collapse;"><br> |
1393
|
|
|
<table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;"> |
1394
|
|
|
<tr><td style="border-collapse: collapse; padding: 11px 20px;"> |
1395
|
|
|
<div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div> |
1396
|
|
|
</td></tr></table></td> |
1397
|
|
|
</tr> |
1398
|
|
|
<tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;"> |
1399
|
|
|
<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;"> |
1400
|
|
|
<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;"> |
1401
|
|
|
<br><div style="float:right;">' . |
1402
|
|
|
$textMail . |
1403
|
|
|
'<br><br></td></tr></table> |
1404
|
|
|
</td></tr></table> |
1405
|
|
|
<br></body></html>'; |
1406
|
|
|
} |
1407
|
|
|
|
1408
|
|
|
/** |
1409
|
|
|
* Generate a Key. |
1410
|
|
|
* |
1411
|
|
|
* @return string |
1412
|
|
|
*/ |
1413
|
|
|
function generateKey(): string |
1414
|
|
|
{ |
1415
|
|
|
return substr(md5(rand() . rand()), 0, 15); |
1416
|
|
|
} |
1417
|
|
|
|
1418
|
|
|
/** |
1419
|
|
|
* Convert date to timestamp. |
1420
|
|
|
* |
1421
|
|
|
* @param string $date The date |
1422
|
|
|
* @param array $SETTINGS Teampass settings |
1423
|
|
|
* |
1424
|
|
|
* @return string |
1425
|
|
|
*/ |
1426
|
|
|
function dateToStamp(string $date, array $SETTINGS): string |
1427
|
|
|
{ |
1428
|
|
|
$date = date_parse_from_format($SETTINGS['date_format'], $date); |
1429
|
|
|
if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) { |
1430
|
|
|
return mktime(23, 59, 59, $date['month'], $date['day'], $date['year']); |
1431
|
|
|
} |
1432
|
|
|
return ''; |
1433
|
|
|
} |
1434
|
|
|
|
1435
|
|
|
/** |
1436
|
|
|
* Is this a date. |
1437
|
|
|
* |
1438
|
|
|
* @param string $date Date |
1439
|
|
|
* |
1440
|
|
|
* @return bool |
1441
|
|
|
*/ |
1442
|
|
|
function isDate(string $date): bool |
1443
|
|
|
{ |
1444
|
|
|
return strtotime($date) !== false; |
1445
|
|
|
} |
1446
|
|
|
|
1447
|
|
|
/** |
1448
|
|
|
* Check if isUTF8(). |
1449
|
|
|
* |
1450
|
|
|
* @param string|array $string Is the string |
1451
|
|
|
* |
1452
|
|
|
* @return int is the string in UTF8 format |
1453
|
|
|
*/ |
1454
|
|
|
function isUTF8($string): int |
1455
|
|
|
{ |
1456
|
|
|
if (is_array($string) === true) { |
1457
|
|
|
$string = $string['string']; |
1458
|
|
|
} |
1459
|
|
|
|
1460
|
|
|
return preg_match( |
1461
|
|
|
'%^(?: |
1462
|
|
|
[\x09\x0A\x0D\x20-\x7E] # ASCII |
1463
|
|
|
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte |
1464
|
|
|
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs |
1465
|
|
|
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte |
1466
|
|
|
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates |
1467
|
|
|
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 |
1468
|
|
|
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 |
1469
|
|
|
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 |
1470
|
|
|
)*$%xs', |
1471
|
|
|
$string |
1472
|
|
|
); |
1473
|
|
|
} |
1474
|
|
|
|
1475
|
|
|
/** |
1476
|
|
|
* Prepare an array to UTF8 format before JSON_encode. |
1477
|
|
|
* |
1478
|
|
|
* @param array $array Array of values |
1479
|
|
|
* |
1480
|
|
|
* @return array |
1481
|
|
|
*/ |
1482
|
|
|
function utf8Converter(array $array): array |
1483
|
|
|
{ |
1484
|
|
|
array_walk_recursive( |
1485
|
|
|
$array, |
1486
|
|
|
static function (&$item): void { |
1487
|
|
|
if (mb_detect_encoding((string) $item, 'utf-8', true) === false) { |
1488
|
|
|
$item = utf8_encode($item); |
1489
|
|
|
} |
1490
|
|
|
} |
1491
|
|
|
); |
1492
|
|
|
return $array; |
1493
|
|
|
} |
1494
|
|
|
|
1495
|
|
|
/** |
1496
|
|
|
* Permits to prepare data to be exchanged. |
1497
|
|
|
* |
1498
|
|
|
* @param array|string $data Text |
1499
|
|
|
* @param string $type Parameter |
1500
|
|
|
* @param string $key Optional key |
1501
|
|
|
* |
1502
|
|
|
* @return resource|string|array |
1503
|
|
|
*/ |
1504
|
|
|
function prepareExchangedData($data, string $type, ?string $key = null) |
1505
|
|
|
{ |
1506
|
|
|
if (file_exists('../includes/config/tp.config.php')) { |
1507
|
|
|
include '../includes/config/tp.config.php'; |
1508
|
|
|
} elseif (file_exists('./includes/config/tp.config.php')) { |
1509
|
|
|
include './includes/config/tp.config.php'; |
1510
|
|
|
} elseif (file_exists('../../includes/config/tp.config.php')) { |
1511
|
|
|
include '../../includes/config/tp.config.php'; |
1512
|
|
|
} else { |
1513
|
|
|
throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1); |
1514
|
|
|
} |
1515
|
|
|
|
1516
|
|
|
if (isset($SETTINGS) === false) { |
1517
|
|
|
return 'ERROR'; |
1518
|
|
|
} |
1519
|
|
|
|
1520
|
|
|
// Load superglobal |
1521
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
1522
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
1523
|
|
|
// Get superglobals |
1524
|
|
|
if ($key !== null) { |
1525
|
|
|
$superGlobal->put('key', $key, 'SESSION'); |
1526
|
|
|
$globalsKey = $key; |
1527
|
|
|
} else { |
1528
|
|
|
$globalsKey = $superGlobal->get('key', 'SESSION'); |
1529
|
|
|
} |
1530
|
|
|
|
1531
|
|
|
//load ClassLoader |
1532
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
1533
|
|
|
//Load AES |
1534
|
|
|
$aes = new SplClassLoader('Encryption\Crypt', $SETTINGS['cpassman_dir'] . '/includes/libraries'); |
1535
|
|
|
$aes->register(); |
1536
|
|
|
if ($type === 'encode' && is_array($data) === true) { |
1537
|
|
|
// Ensure UTF8 format |
1538
|
|
|
$data = utf8Converter($data); |
1539
|
|
|
// Now encode |
1540
|
|
|
return Encryption\Crypt\aesctr::encrypt( |
1541
|
|
|
json_encode( |
1542
|
|
|
$data, |
1543
|
|
|
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP |
1544
|
|
|
), |
1545
|
|
|
$globalsKey, |
1546
|
|
|
256 |
1547
|
|
|
); |
1548
|
|
|
} |
1549
|
|
|
if ($type === 'decode' && is_array($data) === false) { |
1550
|
|
|
return json_decode( |
1551
|
|
|
Encryption\Crypt\aesctr::decrypt( |
1552
|
|
|
/** @scrutinizer ignore-type */ |
1553
|
|
|
(string) $data, |
1554
|
|
|
$globalsKey, |
1555
|
|
|
256 |
1556
|
|
|
), |
1557
|
|
|
true |
1558
|
|
|
); |
1559
|
|
|
} |
1560
|
|
|
} |
1561
|
|
|
|
1562
|
|
|
/** |
1563
|
|
|
* Create a thumbnail. |
1564
|
|
|
* |
1565
|
|
|
* @param string $src Source |
1566
|
|
|
* @param string $dest Destination |
1567
|
|
|
* @param int $desired_width Size of width |
1568
|
|
|
* |
1569
|
|
|
* @return void|string|bool |
1570
|
|
|
*/ |
1571
|
|
|
function makeThumbnail(string $src, string $dest, int $desired_width) |
1572
|
|
|
{ |
1573
|
|
|
/* read the source image */ |
1574
|
|
|
if (is_file($src) === true && mime_content_type($src) === 'image/png') { |
1575
|
|
|
$source_image = imagecreatefrompng($src); |
1576
|
|
|
if ($source_image === false) { |
1577
|
|
|
return "Error: Not a valid PNG file! It's type is ".mime_content_type($src); |
1578
|
|
|
} |
1579
|
|
|
} else { |
1580
|
|
|
return "Error: Not a valid PNG file! It's type is ".mime_content_type($src); |
1581
|
|
|
} |
1582
|
|
|
|
1583
|
|
|
// Get height and width |
1584
|
|
|
$width = imagesx($source_image); |
1585
|
|
|
$height = imagesy($source_image); |
1586
|
|
|
/* find the "desired height" of this thumbnail, relative to the desired width */ |
1587
|
|
|
$desired_height = (int) floor($height * $desired_width / $width); |
1588
|
|
|
/* create a new, "virtual" image */ |
1589
|
|
|
$virtual_image = imagecreatetruecolor($desired_width, $desired_height); |
1590
|
|
|
if ($virtual_image === false) { |
1591
|
|
|
return false; |
1592
|
|
|
} |
1593
|
|
|
/* copy source image at a resized size */ |
1594
|
|
|
imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height); |
1595
|
|
|
/* create the physical thumbnail image to its destination */ |
1596
|
|
|
imagejpeg($virtual_image, $dest); |
1597
|
|
|
} |
1598
|
|
|
|
1599
|
|
|
/** |
1600
|
|
|
* Check table prefix in SQL query. |
1601
|
|
|
* |
1602
|
|
|
* @param string $table Table name |
1603
|
|
|
* |
1604
|
|
|
* @return string |
1605
|
|
|
*/ |
1606
|
|
|
function prefixTable(string $table): string |
1607
|
|
|
{ |
1608
|
|
|
$safeTable = htmlspecialchars(DB_PREFIX . $table); |
1609
|
|
|
if (! empty($safeTable)) { |
1610
|
|
|
// sanitize string |
1611
|
|
|
return $safeTable; |
1612
|
|
|
} |
1613
|
|
|
// stop error no table |
1614
|
|
|
return 'table_not_exists'; |
1615
|
|
|
} |
1616
|
|
|
|
1617
|
|
|
/** |
1618
|
|
|
* GenerateCryptKey |
1619
|
|
|
* |
1620
|
|
|
* @param int $size Length |
1621
|
|
|
* @param bool $secure Secure |
1622
|
|
|
* @param bool $numerals Numerics |
1623
|
|
|
* @param bool $uppercase Uppercase letters |
1624
|
|
|
* @param bool $symbols Symbols |
1625
|
|
|
* @param bool $lowercase Lowercase |
1626
|
|
|
* @param array $SETTINGS SETTINGS |
1627
|
|
|
* |
1628
|
|
|
* @return string |
1629
|
|
|
*/ |
1630
|
|
|
function GenerateCryptKey( |
1631
|
|
|
int $size = 10, |
1632
|
|
|
bool $secure = false, |
1633
|
|
|
bool $numerals = false, |
1634
|
|
|
bool $uppercase = false, |
1635
|
|
|
bool $symbols = false, |
1636
|
|
|
bool $lowercase = false, |
1637
|
|
|
array $SETTINGS = [] |
1638
|
|
|
): string { |
1639
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
1640
|
|
|
$generator = new SplClassLoader('PasswordGenerator\Generator', $SETTINGS['cpassman_dir'] . '/includes/libraries'); |
1641
|
|
|
$generator->register(); |
1642
|
|
|
$generator = new PasswordGenerator\Generator\ComputerPasswordGenerator(); |
1643
|
|
|
// Is PHP7 being used? |
1644
|
|
|
if (version_compare(PHP_VERSION, '7.0.0', '>=')) { |
1645
|
|
|
$php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', $SETTINGS['cpassman_dir'] . '/includes/libraries'); |
1646
|
|
|
$php7generator->register(); |
1647
|
|
|
$generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator()); |
1648
|
|
|
} |
1649
|
|
|
|
1650
|
|
|
// Manage size |
1651
|
|
|
$generator->setLength((int) $size); |
1652
|
|
|
if ($secure === true) { |
1653
|
|
|
$generator->setSymbols(true); |
1654
|
|
|
$generator->setLowercase(true); |
1655
|
|
|
$generator->setUppercase(true); |
1656
|
|
|
$generator->setNumbers(true); |
1657
|
|
|
} else { |
1658
|
|
|
$generator->setLowercase($lowercase); |
1659
|
|
|
$generator->setUppercase($uppercase); |
1660
|
|
|
$generator->setNumbers($numerals); |
1661
|
|
|
$generator->setSymbols($symbols); |
1662
|
|
|
} |
1663
|
|
|
|
1664
|
|
|
return $generator->generatePasswords()[0]; |
1665
|
|
|
} |
1666
|
|
|
|
1667
|
|
|
/** |
1668
|
|
|
* Send sysLOG message |
1669
|
|
|
* |
1670
|
|
|
* @param string $message |
1671
|
|
|
* @param string $host |
1672
|
|
|
* @param int $port |
1673
|
|
|
* @param string $component |
1674
|
|
|
* |
1675
|
|
|
* @return void |
1676
|
|
|
*/ |
1677
|
|
|
function send_syslog($message, $host, $port, $component = 'teampass'): void |
1678
|
|
|
{ |
1679
|
|
|
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); |
1680
|
|
|
$syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message; |
1681
|
|
|
socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port); |
1682
|
|
|
socket_close($sock); |
1683
|
|
|
} |
1684
|
|
|
|
1685
|
|
|
/** |
1686
|
|
|
* Permits to log events into DB |
1687
|
|
|
* |
1688
|
|
|
* @param array $SETTINGS Teampass settings |
1689
|
|
|
* @param string $type Type |
1690
|
|
|
* @param string $label Label |
1691
|
|
|
* @param string $who Who |
1692
|
|
|
* @param string $login Login |
1693
|
|
|
* @param string $field_1 Field |
1694
|
|
|
* |
1695
|
|
|
* @return void |
1696
|
|
|
*/ |
1697
|
|
|
function logEvents(array $SETTINGS, string $type, string $label, string $who, ?string $login = null, ?string $field_1 = null): void |
1698
|
|
|
{ |
1699
|
|
|
if (empty($who)) { |
1700
|
|
|
$who = getClientIpServer(); |
1701
|
|
|
} |
1702
|
|
|
|
1703
|
|
|
// include librairies & connect to DB |
1704
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
1705
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
1706
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
1707
|
|
|
} |
1708
|
|
|
DB::$host = DB_HOST; |
1709
|
|
|
DB::$user = DB_USER; |
1710
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
1711
|
|
|
DB::$dbName = DB_NAME; |
1712
|
|
|
DB::$port = DB_PORT; |
1713
|
|
|
DB::$encoding = DB_ENCODING; |
1714
|
|
|
DB::insert( |
1715
|
|
|
prefixTable('log_system'), |
1716
|
|
|
[ |
1717
|
|
|
'type' => $type, |
1718
|
|
|
'date' => time(), |
1719
|
|
|
'label' => $label, |
1720
|
|
|
'qui' => $who, |
1721
|
|
|
'field_1' => $field_1 === null ? '' : $field_1, |
1722
|
|
|
] |
1723
|
|
|
); |
1724
|
|
|
// If SYSLOG |
1725
|
|
|
if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) { |
1726
|
|
|
if ($type === 'user_mngt') { |
1727
|
|
|
send_syslog( |
1728
|
|
|
'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ', |
1729
|
|
|
$SETTINGS['syslog_host'], |
1730
|
|
|
$SETTINGS['syslog_port'], |
1731
|
|
|
'teampass' |
1732
|
|
|
); |
1733
|
|
|
} else { |
1734
|
|
|
send_syslog( |
1735
|
|
|
'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ', |
1736
|
|
|
$SETTINGS['syslog_host'], |
1737
|
|
|
$SETTINGS['syslog_port'], |
1738
|
|
|
'teampass' |
1739
|
|
|
); |
1740
|
|
|
} |
1741
|
|
|
} |
1742
|
|
|
} |
1743
|
|
|
|
1744
|
|
|
/** |
1745
|
|
|
* Log events. |
1746
|
|
|
* |
1747
|
|
|
* @param array $SETTINGS Teampass settings |
1748
|
|
|
* @param int $item_id Item id |
1749
|
|
|
* @param string $item_label Item label |
1750
|
|
|
* @param int $id_user User id |
1751
|
|
|
* @param string $action Code for reason |
1752
|
|
|
* @param string $login User login |
1753
|
|
|
* @param string $raison Code for reason |
1754
|
|
|
* @param string $encryption_type Encryption on |
1755
|
|
|
* |
1756
|
|
|
* @return void |
1757
|
|
|
*/ |
1758
|
|
|
function logItems( |
1759
|
|
|
array $SETTINGS, |
1760
|
|
|
int $item_id, |
1761
|
|
|
string $item_label, |
1762
|
|
|
int $id_user, |
1763
|
|
|
string $action, |
1764
|
|
|
?string $login = null, |
1765
|
|
|
?string $raison = null, |
1766
|
|
|
?string $encryption_type = null |
1767
|
|
|
): void { |
1768
|
|
|
// include librairies & connect to DB |
1769
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
1770
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
1771
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
1772
|
|
|
} |
1773
|
|
|
DB::$host = DB_HOST; |
1774
|
|
|
DB::$user = DB_USER; |
1775
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
1776
|
|
|
DB::$dbName = DB_NAME; |
1777
|
|
|
DB::$port = DB_PORT; |
1778
|
|
|
DB::$encoding = DB_ENCODING; |
1779
|
|
|
// Insert log in DB |
1780
|
|
|
DB::insert( |
1781
|
|
|
prefixTable('log_items'), |
1782
|
|
|
[ |
1783
|
|
|
'id_item' => $item_id, |
1784
|
|
|
'date' => time(), |
1785
|
|
|
'id_user' => $id_user, |
1786
|
|
|
'action' => $action, |
1787
|
|
|
'raison' => $raison, |
1788
|
|
|
'raison_iv' => '', |
1789
|
|
|
'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type, |
1790
|
|
|
] |
1791
|
|
|
); |
1792
|
|
|
// Timestamp the last change |
1793
|
|
|
if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') { |
1794
|
|
|
DB::update( |
1795
|
|
|
prefixTable('misc'), |
1796
|
|
|
[ |
1797
|
|
|
'valeur' => time(), |
1798
|
|
|
], |
1799
|
|
|
'type = %s AND intitule = %s', |
1800
|
|
|
'timestamp', |
1801
|
|
|
'last_item_change' |
1802
|
|
|
); |
1803
|
|
|
} |
1804
|
|
|
|
1805
|
|
|
// SYSLOG |
1806
|
|
|
if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') { |
1807
|
|
|
// Extract reason |
1808
|
|
|
$attribute = is_null($raison) === true ? '' : explode(' : ', $raison); |
1809
|
|
|
// Get item info if not known |
1810
|
|
|
if (empty($item_label) === true) { |
1811
|
|
|
$dataItem = DB::queryfirstrow( |
1812
|
|
|
'SELECT id, id_tree, label |
1813
|
|
|
FROM ' . prefixTable('items') . ' |
1814
|
|
|
WHERE id = %i', |
1815
|
|
|
$item_id |
1816
|
|
|
); |
1817
|
|
|
$item_label = $dataItem['label']; |
1818
|
|
|
} |
1819
|
|
|
|
1820
|
|
|
send_syslog( |
1821
|
|
|
'action=' . str_replace('at_', '', $action) . |
1822
|
|
|
' attribute=' . str_replace('at_', '', $attribute[0]) . |
1823
|
|
|
' itemno=' . $item_id . |
1824
|
|
|
' user=' . is_null($login) === true ? '' : addslashes((string) $login) . |
1825
|
|
|
' itemname="' . addslashes($item_label) . '"', |
1826
|
|
|
$SETTINGS['syslog_host'], |
1827
|
|
|
$SETTINGS['syslog_port'], |
1828
|
|
|
'teampass' |
1829
|
|
|
); |
1830
|
|
|
} |
1831
|
|
|
|
1832
|
|
|
// send notification if enabled |
1833
|
|
|
notifyOnChange($item_id, $action, $SETTINGS); |
1834
|
|
|
} |
1835
|
|
|
|
1836
|
|
|
/** |
1837
|
|
|
* If enabled, then notify admin/manager. |
1838
|
|
|
* |
1839
|
|
|
* @param int $item_id Item id |
1840
|
|
|
* @param string $action Action to do |
1841
|
|
|
* @param array $SETTINGS Teampass settings |
1842
|
|
|
* |
1843
|
|
|
* @return void |
1844
|
|
|
*/ |
1845
|
|
|
function notifyOnChange(int $item_id, string $action, array $SETTINGS): void |
1846
|
|
|
{ |
1847
|
|
|
if ( |
1848
|
|
|
isset($SETTINGS['enable_email_notification_on_item_shown']) === true |
1849
|
|
|
&& (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1 |
1850
|
|
|
&& $action === 'at_shown' |
1851
|
|
|
) { |
1852
|
|
|
// Load superglobal |
1853
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
1854
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
1855
|
|
|
// Get superglobals |
1856
|
|
|
$globalsLastname = $superGlobal->get('lastname', 'SESSION'); |
1857
|
|
|
$globalsName = $superGlobal->get('name', 'SESSION'); |
1858
|
|
|
$globalsNotifiedEmails = $superGlobal->get('listNotificationEmails', 'SESSION'); |
1859
|
|
|
// Get info about item |
1860
|
|
|
$dataItem = DB::queryfirstrow( |
1861
|
|
|
'SELECT id, id_tree, label |
1862
|
|
|
FROM ' . prefixTable('items') . ' |
1863
|
|
|
WHERE id = %i', |
1864
|
|
|
$item_id |
1865
|
|
|
); |
1866
|
|
|
$item_label = $dataItem['label']; |
1867
|
|
|
// send back infos |
1868
|
|
|
DB::insert( |
1869
|
|
|
prefixTable('emails'), |
1870
|
|
|
[ |
1871
|
|
|
'timestamp' => time(), |
1872
|
|
|
'subject' => langHdl('email_on_open_notification_subject'), |
1873
|
|
|
'body' => str_replace( |
1874
|
|
|
['#tp_user#', '#tp_item#', '#tp_link#'], |
1875
|
|
|
[ |
1876
|
|
|
addslashes($globalsName . ' ' . $globalsLastname), |
1877
|
|
|
addslashes($item_label), |
1878
|
|
|
$SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $item_id, |
1879
|
|
|
], |
1880
|
|
|
langHdl('email_on_open_notification_mail') |
1881
|
|
|
), |
1882
|
|
|
'receivers' => $globalsNotifiedEmails, |
1883
|
|
|
'status' => '', |
1884
|
|
|
] |
1885
|
|
|
); |
1886
|
|
|
} |
1887
|
|
|
} |
1888
|
|
|
|
1889
|
|
|
/** |
1890
|
|
|
* Prepare notification email to subscribers. |
1891
|
|
|
* |
1892
|
|
|
* @param int $item_id Item id |
1893
|
|
|
* @param string $label Item label |
1894
|
|
|
* @param array $changes List of changes |
1895
|
|
|
* @param array $SETTINGS Teampass settings |
1896
|
|
|
* |
1897
|
|
|
* @return void |
1898
|
|
|
*/ |
1899
|
|
|
function notifyChangesToSubscribers(int $item_id, string $label, array $changes, array $SETTINGS): void |
1900
|
|
|
{ |
1901
|
|
|
// Load superglobal |
1902
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
1903
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
1904
|
|
|
// Get superglobals |
1905
|
|
|
$globalsUserId = $superGlobal->get('user_id', 'SESSION'); |
1906
|
|
|
$globalsLastname = $superGlobal->get('lastname', 'SESSION'); |
1907
|
|
|
$globalsName = $superGlobal->get('name', 'SESSION'); |
1908
|
|
|
// send email to user that what to be notified |
1909
|
|
|
$notification = DB::queryOneColumn( |
1910
|
|
|
'email', |
1911
|
|
|
'SELECT * |
1912
|
|
|
FROM ' . prefixTable('notification') . ' AS n |
1913
|
|
|
INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id) |
1914
|
|
|
WHERE n.item_id = %i AND n.user_id != %i', |
1915
|
|
|
$item_id, |
1916
|
|
|
$globalsUserId |
1917
|
|
|
); |
1918
|
|
|
if (DB::count() > 0) { |
1919
|
|
|
// Prepare path |
1920
|
|
|
$path = geItemReadablePath($item_id, '', $SETTINGS); |
1921
|
|
|
// Get list of changes |
1922
|
|
|
$htmlChanges = '<ul>'; |
1923
|
|
|
foreach ($changes as $change) { |
1924
|
|
|
$htmlChanges .= '<li>' . $change . '</li>'; |
1925
|
|
|
} |
1926
|
|
|
$htmlChanges .= '</ul>'; |
1927
|
|
|
// send email |
1928
|
|
|
DB::insert( |
1929
|
|
|
prefixTable('emails'), |
1930
|
|
|
[ |
1931
|
|
|
'timestamp' => time(), |
1932
|
|
|
'subject' => langHdl('email_subject_item_updated'), |
1933
|
|
|
'body' => str_replace( |
1934
|
|
|
['#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'], |
1935
|
|
|
[$label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges], |
1936
|
|
|
langHdl('email_body_item_updated') |
1937
|
|
|
), |
1938
|
|
|
'receivers' => implode(',', $notification), |
1939
|
|
|
'status' => '', |
1940
|
|
|
] |
1941
|
|
|
); |
1942
|
|
|
} |
1943
|
|
|
} |
1944
|
|
|
|
1945
|
|
|
/** |
1946
|
|
|
* Returns the Item + path. |
1947
|
|
|
* |
1948
|
|
|
* @param int $id_tree Node id |
1949
|
|
|
* @param string $label Label |
1950
|
|
|
* @param array $SETTINGS TP settings |
1951
|
|
|
* |
1952
|
|
|
* @return string |
1953
|
|
|
*/ |
1954
|
|
|
function geItemReadablePath(int $id_tree, string $label, array $SETTINGS): string |
1955
|
|
|
{ |
1956
|
|
|
// Class loader |
1957
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php'; |
1958
|
|
|
//Load Tree |
1959
|
|
|
$tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries'); |
1960
|
|
|
$tree->register(); |
1961
|
|
|
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); |
1962
|
|
|
$arbo = $tree->getPath($id_tree, true); |
1963
|
|
|
$path = ''; |
1964
|
|
|
foreach ($arbo as $elem) { |
1965
|
|
|
if (empty($path) === true) { |
1966
|
|
|
$path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' '; |
1967
|
|
|
} else { |
1968
|
|
|
$path .= '→ ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES); |
1969
|
|
|
} |
1970
|
|
|
} |
1971
|
|
|
|
1972
|
|
|
// Build text to show user |
1973
|
|
|
if (empty($label) === false) { |
1974
|
|
|
return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')'; |
1975
|
|
|
} |
1976
|
|
|
return empty($path) === true ? '' : $path; |
1977
|
|
|
} |
1978
|
|
|
|
1979
|
|
|
/** |
1980
|
|
|
* Get the client ip address. |
1981
|
|
|
* |
1982
|
|
|
* @return string IP address |
1983
|
|
|
*/ |
1984
|
|
|
function getClientIpServer(): string |
1985
|
|
|
{ |
1986
|
|
|
if (getenv('HTTP_CLIENT_IP')) { |
1987
|
|
|
$ipaddress = getenv('HTTP_CLIENT_IP'); |
1988
|
|
|
} elseif (getenv('HTTP_X_FORWARDED_FOR')) { |
1989
|
|
|
$ipaddress = getenv('HTTP_X_FORWARDED_FOR'); |
1990
|
|
|
} elseif (getenv('HTTP_X_FORWARDED')) { |
1991
|
|
|
$ipaddress = getenv('HTTP_X_FORWARDED'); |
1992
|
|
|
} elseif (getenv('HTTP_FORWARDED_FOR')) { |
1993
|
|
|
$ipaddress = getenv('HTTP_FORWARDED_FOR'); |
1994
|
|
|
} elseif (getenv('HTTP_FORWARDED')) { |
1995
|
|
|
$ipaddress = getenv('HTTP_FORWARDED'); |
1996
|
|
|
} elseif (getenv('REMOTE_ADDR')) { |
1997
|
|
|
$ipaddress = getenv('REMOTE_ADDR'); |
1998
|
|
|
} else { |
1999
|
|
|
$ipaddress = 'UNKNOWN'; |
2000
|
|
|
} |
2001
|
|
|
|
2002
|
|
|
return $ipaddress; |
2003
|
|
|
} |
2004
|
|
|
|
2005
|
|
|
/** |
2006
|
|
|
* Escape all HTML, JavaScript, and CSS. |
2007
|
|
|
* |
2008
|
|
|
* @param string $input The input string |
2009
|
|
|
* @param string $encoding Which character encoding are we using? |
2010
|
|
|
* |
2011
|
|
|
* @return string |
2012
|
|
|
*/ |
2013
|
|
|
function noHTML(string $input, string $encoding = 'UTF-8'): string |
2014
|
|
|
{ |
2015
|
|
|
return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false); |
2016
|
|
|
} |
2017
|
|
|
|
2018
|
|
|
/** |
2019
|
|
|
* Permits to handle the Teampass config file |
2020
|
|
|
* $action accepts "rebuild" and "update" |
2021
|
|
|
* |
2022
|
|
|
* @param string $action Action to perform |
2023
|
|
|
* @param array $SETTINGS Teampass settings |
2024
|
|
|
* @param string $field Field to refresh |
2025
|
|
|
* @param string $value Value to set |
2026
|
|
|
* |
2027
|
|
|
* @return string|bool |
2028
|
|
|
*/ |
2029
|
|
|
function handleConfigFile($action, $SETTINGS, $field = null, $value = null) |
2030
|
|
|
{ |
2031
|
|
|
$tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php'; |
2032
|
|
|
// include librairies & connect to DB |
2033
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
2034
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
2035
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
2036
|
|
|
} |
2037
|
|
|
DB::$host = DB_HOST; |
2038
|
|
|
DB::$user = DB_USER; |
2039
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
2040
|
|
|
DB::$dbName = DB_NAME; |
2041
|
|
|
DB::$port = DB_PORT; |
2042
|
|
|
DB::$encoding = DB_ENCODING; |
2043
|
|
|
if (file_exists($tp_config_file) === false || $action === 'rebuild') { |
2044
|
|
|
// perform a copy |
2045
|
|
|
if (file_exists($tp_config_file)) { |
2046
|
|
|
if (! copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) { |
2047
|
|
|
return "ERROR: Could not copy file '" . $tp_config_file . "'"; |
2048
|
|
|
} |
2049
|
|
|
} |
2050
|
|
|
|
2051
|
|
|
// regenerate |
2052
|
|
|
$data = []; |
2053
|
|
|
$data[0] = "<?php\n"; |
2054
|
|
|
$data[1] = "global \$SETTINGS;\n"; |
2055
|
|
|
$data[2] = "\$SETTINGS = array (\n"; |
2056
|
|
|
$rows = DB::query( |
2057
|
|
|
'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s', |
2058
|
|
|
'admin' |
2059
|
|
|
); |
2060
|
|
|
foreach ($rows as $record) { |
2061
|
|
|
array_push($data, " '" . $record['intitule'] . "' => '" . $record['valeur'] . "',\n"); |
2062
|
|
|
} |
2063
|
|
|
array_push($data, ");\n"); |
2064
|
|
|
$data = array_unique($data); |
2065
|
|
|
// --- |
2066
|
|
|
} elseif ($action === 'update' && empty($field) === false) { |
2067
|
|
|
$data = file($tp_config_file); |
2068
|
|
|
$inc = 0; |
2069
|
|
|
$bFound = false; |
2070
|
|
|
foreach ($data as $line) { |
2071
|
|
|
if (stristr($line, ');')) { |
2072
|
|
|
break; |
2073
|
|
|
} |
2074
|
|
|
|
2075
|
|
|
if (stristr($line, "'" . $field . "' => '")) { |
2076
|
|
|
$data[$inc] = " '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n"; |
2077
|
|
|
$bFound = true; |
2078
|
|
|
break; |
2079
|
|
|
} |
2080
|
|
|
++$inc; |
2081
|
|
|
} |
2082
|
|
|
if ($bFound === false) { |
2083
|
|
|
$data[$inc] = " '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n);\n"; |
2084
|
|
|
} |
2085
|
|
|
} |
2086
|
|
|
|
2087
|
|
|
// update file |
2088
|
|
|
file_put_contents($tp_config_file, implode('', $data ?? [])); |
2089
|
|
|
return true; |
2090
|
|
|
} |
2091
|
|
|
|
2092
|
|
|
/** |
2093
|
|
|
* Permits to replace \ to permit correct display |
2094
|
|
|
* |
2095
|
|
|
* @param string $input Some text |
2096
|
|
|
* |
2097
|
|
|
* @return string |
2098
|
|
|
*/ |
2099
|
|
|
function handleBackslash(string $input): string |
2100
|
|
|
{ |
2101
|
|
|
return str_replace('&#92;', '\', $input); |
2102
|
|
|
} |
2103
|
|
|
|
2104
|
|
|
/** |
2105
|
|
|
* Permits to load settings |
2106
|
|
|
* |
2107
|
|
|
* @return void |
2108
|
|
|
*/ |
2109
|
|
|
function loadSettings(): void |
2110
|
|
|
{ |
2111
|
|
|
global $SETTINGS; |
2112
|
|
|
/* LOAD CPASSMAN SETTINGS */ |
2113
|
|
|
if (! isset($SETTINGS['loaded']) || $SETTINGS['loaded'] !== 1) { |
2114
|
|
|
$SETTINGS = []; |
2115
|
|
|
$SETTINGS['duplicate_folder'] = 0; |
2116
|
|
|
//by default, this is set to 0; |
2117
|
|
|
$SETTINGS['duplicate_item'] = 0; |
2118
|
|
|
//by default, this is set to 0; |
2119
|
|
|
$SETTINGS['number_of_used_pw'] = 5; |
2120
|
|
|
//by default, this value is set to 5; |
2121
|
|
|
$settings = []; |
2122
|
|
|
$rows = DB::query( |
2123
|
|
|
'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2', |
2124
|
|
|
[ |
2125
|
|
|
'type' => 'admin', |
2126
|
|
|
'type2' => 'settings', |
2127
|
|
|
] |
2128
|
|
|
); |
2129
|
|
|
foreach ($rows as $record) { |
2130
|
|
|
if ($record['type'] === 'admin') { |
2131
|
|
|
$SETTINGS[$record['intitule']] = $record['valeur']; |
2132
|
|
|
} else { |
2133
|
|
|
$settings[$record['intitule']] = $record['valeur']; |
2134
|
|
|
} |
2135
|
|
|
} |
2136
|
|
|
$SETTINGS['loaded'] = 1; |
2137
|
|
|
$SETTINGS['default_session_expiration_time'] = 5; |
2138
|
|
|
} |
2139
|
|
|
} |
2140
|
|
|
|
2141
|
|
|
/** |
2142
|
|
|
* check if folder has custom fields. |
2143
|
|
|
* Ensure that target one also has same custom fields |
2144
|
|
|
* |
2145
|
|
|
* @param int $source_id |
2146
|
|
|
* @param int $target_id |
2147
|
|
|
* |
2148
|
|
|
* @return bool |
2149
|
|
|
*/ |
2150
|
|
|
function checkCFconsistency(int $source_id, int $target_id): bool |
2151
|
|
|
{ |
2152
|
|
|
$source_cf = []; |
2153
|
|
|
$rows = DB::QUERY( |
2154
|
|
|
'SELECT id_category |
2155
|
|
|
FROM ' . prefixTable('categories_folders') . ' |
2156
|
|
|
WHERE id_folder = %i', |
2157
|
|
|
$source_id |
2158
|
|
|
); |
2159
|
|
|
foreach ($rows as $record) { |
2160
|
|
|
array_push($source_cf, $record['id_category']); |
2161
|
|
|
} |
2162
|
|
|
|
2163
|
|
|
$target_cf = []; |
2164
|
|
|
$rows = DB::QUERY( |
2165
|
|
|
'SELECT id_category |
2166
|
|
|
FROM ' . prefixTable('categories_folders') . ' |
2167
|
|
|
WHERE id_folder = %i', |
2168
|
|
|
$target_id |
2169
|
|
|
); |
2170
|
|
|
foreach ($rows as $record) { |
2171
|
|
|
array_push($target_cf, $record['id_category']); |
2172
|
|
|
} |
2173
|
|
|
|
2174
|
|
|
$cf_diff = array_diff($source_cf, $target_cf); |
2175
|
|
|
if (count($cf_diff) > 0) { |
2176
|
|
|
return false; |
2177
|
|
|
} |
2178
|
|
|
|
2179
|
|
|
return true; |
2180
|
|
|
} |
2181
|
|
|
|
2182
|
|
|
/** |
2183
|
|
|
* Will encrypte/decrypt a fil eusing Defuse. |
2184
|
|
|
* |
2185
|
|
|
* @param string $type can be either encrypt or decrypt |
2186
|
|
|
* @param string $source_file path to source file |
2187
|
|
|
* @param string $target_file path to target file |
2188
|
|
|
* @param array $SETTINGS Settings |
2189
|
|
|
* @param string $password A password |
2190
|
|
|
* |
2191
|
|
|
* @return string|bool |
2192
|
|
|
*/ |
2193
|
|
|
function prepareFileWithDefuse( |
2194
|
|
|
string $type, |
2195
|
|
|
string $source_file, |
2196
|
|
|
string $target_file, |
2197
|
|
|
array $SETTINGS, |
2198
|
|
|
string $password = null |
2199
|
|
|
) { |
2200
|
|
|
// Load AntiXSS |
2201
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php'; |
2202
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php'; |
2203
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php'; |
2204
|
|
|
$antiXss = new voku\helper\AntiXSS(); |
2205
|
|
|
// Protect against bad inputs |
2206
|
|
|
if (is_array($source_file) === true || is_array($target_file) === true) { |
2207
|
|
|
return 'error_cannot_be_array'; |
2208
|
|
|
} |
2209
|
|
|
|
2210
|
|
|
// Sanitize |
2211
|
|
|
$source_file = $antiXss->xss_clean($source_file); |
2212
|
|
|
$target_file = $antiXss->xss_clean($target_file); |
2213
|
|
|
if (empty($password) === true || is_null($password) === true) { |
2214
|
|
|
// get KEY to define password |
2215
|
|
|
$ascii_key = file_get_contents(SECUREPATH . '/teampass-seckey.txt'); |
2216
|
|
|
$password = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key); |
2217
|
|
|
} |
2218
|
|
|
|
2219
|
|
|
$err = ''; |
2220
|
|
|
if ($type === 'decrypt') { |
2221
|
|
|
// Decrypt file |
2222
|
|
|
$err = defuseFileDecrypt( |
2223
|
|
|
$source_file, |
2224
|
|
|
$target_file, |
2225
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
2226
|
|
|
$password |
2227
|
|
|
); |
2228
|
|
|
} elseif ($type === 'encrypt') { |
2229
|
|
|
// Encrypt file |
2230
|
|
|
$err = defuseFileEncrypt( |
2231
|
|
|
$source_file, |
2232
|
|
|
$target_file, |
2233
|
|
|
$SETTINGS, /** @scrutinizer ignore-type */ |
2234
|
|
|
$password |
2235
|
|
|
); |
2236
|
|
|
} |
2237
|
|
|
|
2238
|
|
|
// return error |
2239
|
|
|
return empty($err) === false ? $err : ''; |
2240
|
|
|
} |
2241
|
|
|
|
2242
|
|
|
/** |
2243
|
|
|
* Encrypt a file with Defuse. |
2244
|
|
|
* |
2245
|
|
|
* @param string $source_file path to source file |
2246
|
|
|
* @param string $target_file path to target file |
2247
|
|
|
* @param array $SETTINGS Settings |
2248
|
|
|
* @param string $password A password |
2249
|
|
|
* |
2250
|
|
|
* @return string|bool |
2251
|
|
|
*/ |
2252
|
|
|
function defuseFileEncrypt( |
2253
|
|
|
string $source_file, |
2254
|
|
|
string $target_file, |
2255
|
|
|
array $SETTINGS, |
2256
|
|
|
string $password = null |
2257
|
|
|
) { |
2258
|
|
|
// load PhpEncryption library |
2259
|
|
|
$path_to_encryption = '/includes/libraries/Encryption/Encryption/'; |
2260
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php'; |
2261
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php'; |
2262
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php'; |
2263
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php'; |
2264
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php'; |
2265
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php'; |
2266
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php'; |
2267
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php'; |
2268
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php'; |
2269
|
|
|
try { |
2270
|
|
|
\Defuse\Crypto\File::encryptFileWithPassword( |
2271
|
|
|
$source_file, |
2272
|
|
|
$target_file, |
2273
|
|
|
$password |
2274
|
|
|
); |
2275
|
|
|
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) { |
2276
|
|
|
$err = 'wrong_key'; |
2277
|
|
|
} catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) { |
2278
|
|
|
$err = $ex; |
2279
|
|
|
} catch (Defuse\Crypto\Exception\IOException $ex) { |
2280
|
|
|
$err = $ex; |
2281
|
|
|
} |
2282
|
|
|
|
2283
|
|
|
// return error |
2284
|
|
|
return empty($err) === false ? $err : true; |
2285
|
|
|
} |
2286
|
|
|
|
2287
|
|
|
/** |
2288
|
|
|
* Decrypt a file with Defuse. |
2289
|
|
|
* |
2290
|
|
|
* @param string $source_file path to source file |
2291
|
|
|
* @param string $target_file path to target file |
2292
|
|
|
* @param array $SETTINGS Settings |
2293
|
|
|
* @param string $password A password |
2294
|
|
|
* |
2295
|
|
|
* @return string|bool |
2296
|
|
|
*/ |
2297
|
|
|
function defuseFileDecrypt( |
2298
|
|
|
string $source_file, |
2299
|
|
|
string $target_file, |
2300
|
|
|
array $SETTINGS, |
2301
|
|
|
string $password = null |
2302
|
|
|
) { |
2303
|
|
|
// load PhpEncryption library |
2304
|
|
|
$path_to_encryption = '/includes/libraries/Encryption/Encryption/'; |
2305
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php'; |
2306
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php'; |
2307
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php'; |
2308
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php'; |
2309
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php'; |
2310
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php'; |
2311
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php'; |
2312
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php'; |
2313
|
|
|
include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php'; |
2314
|
|
|
try { |
2315
|
|
|
\Defuse\Crypto\File::decryptFileWithPassword( |
2316
|
|
|
$source_file, |
2317
|
|
|
$target_file, |
2318
|
|
|
$password |
2319
|
|
|
); |
2320
|
|
|
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) { |
2321
|
|
|
$err = 'wrong_key'; |
2322
|
|
|
} catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) { |
2323
|
|
|
$err = $ex; |
2324
|
|
|
} catch (Defuse\Crypto\Exception\IOException $ex) { |
2325
|
|
|
$err = $ex; |
2326
|
|
|
} |
2327
|
|
|
|
2328
|
|
|
// return error |
2329
|
|
|
return empty($err) === false ? $err : true; |
2330
|
|
|
} |
2331
|
|
|
|
2332
|
|
|
/* |
2333
|
|
|
* NOT TO BE USED |
2334
|
|
|
*/ |
2335
|
|
|
/** |
2336
|
|
|
* Undocumented function. |
2337
|
|
|
* |
2338
|
|
|
* @param string $text Text to debug |
2339
|
|
|
*/ |
2340
|
|
|
function debugTeampass(string $text): void |
2341
|
|
|
{ |
2342
|
|
|
$debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+'); |
2343
|
|
|
if ($debugFile !== false) { |
2344
|
|
|
fputs($debugFile, $text); |
2345
|
|
|
fclose($debugFile); |
2346
|
|
|
} |
2347
|
|
|
} |
2348
|
|
|
|
2349
|
|
|
/** |
2350
|
|
|
* DELETE the file with expected command depending on server type. |
2351
|
|
|
* |
2352
|
|
|
* @param string $file Path to file |
2353
|
|
|
* @param array $SETTINGS Teampass settings |
2354
|
|
|
* |
2355
|
|
|
* @return void |
2356
|
|
|
*/ |
2357
|
|
|
function fileDelete(string $file, array $SETTINGS): void |
2358
|
|
|
{ |
2359
|
|
|
// Load AntiXSS |
2360
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php'; |
2361
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php'; |
2362
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php'; |
2363
|
|
|
$antiXss = new voku\helper\AntiXSS(); |
2364
|
|
|
$file = $antiXss->xss_clean($file); |
2365
|
|
|
if (is_file($file)) { |
2366
|
|
|
unlink($file); |
2367
|
|
|
} |
2368
|
|
|
} |
2369
|
|
|
|
2370
|
|
|
/** |
2371
|
|
|
* Permits to extract the file extension. |
2372
|
|
|
* |
2373
|
|
|
* @param string $file File name |
2374
|
|
|
* |
2375
|
|
|
* @return string |
2376
|
|
|
*/ |
2377
|
|
|
function getFileExtension(string $file): string |
2378
|
|
|
{ |
2379
|
|
|
if (strpos($file, '.') === false) { |
2380
|
|
|
return $file; |
2381
|
|
|
} |
2382
|
|
|
|
2383
|
|
|
return substr($file, strrpos($file, '.') + 1); |
2384
|
|
|
} |
2385
|
|
|
|
2386
|
|
|
/** |
2387
|
|
|
* Chmods files and folders with different permissions. |
2388
|
|
|
* |
2389
|
|
|
* This is an all-PHP alternative to using: \n |
2390
|
|
|
* <tt>exec("find ".$path." -type f -exec chmod 644 {} \;");</tt> \n |
2391
|
|
|
* <tt>exec("find ".$path." -type d -exec chmod 755 {} \;");</tt> |
2392
|
|
|
* |
2393
|
|
|
* @author Jeppe Toustrup (tenzer at tenzer dot dk) |
2394
|
|
|
* |
2395
|
|
|
* @param string $path An either relative or absolute path to a file or directory which should be processed. |
2396
|
|
|
* @param int $filePerm The permissions any found files should get. |
2397
|
|
|
* @param int $dirPerm The permissions any found folder should get. |
2398
|
|
|
* |
2399
|
|
|
* @return bool Returns TRUE if the path if found and FALSE if not. |
2400
|
|
|
* |
2401
|
|
|
* @warning The permission levels has to be entered in octal format, which |
2402
|
|
|
* normally means adding a zero ("0") in front of the permission level. \n |
2403
|
|
|
* More info at: http://php.net/chmod. |
2404
|
|
|
*/ |
2405
|
|
|
|
2406
|
|
|
function recursiveChmod( |
2407
|
|
|
string $path, |
2408
|
|
|
int $filePerm = 0644, |
2409
|
|
|
int $dirPerm = 0755 |
2410
|
|
|
) { |
2411
|
|
|
// Check if the path exists |
2412
|
|
|
if (! file_exists($path)) { |
2413
|
|
|
return false; |
2414
|
|
|
} |
2415
|
|
|
|
2416
|
|
|
// See whether this is a file |
2417
|
|
|
if (is_file($path)) { |
2418
|
|
|
// Chmod the file with our given filepermissions |
2419
|
|
|
chmod($path, $filePerm); |
2420
|
|
|
// If this is a directory... |
2421
|
|
|
} elseif (is_dir($path)) { |
2422
|
|
|
// Then get an array of the contents |
2423
|
|
|
$foldersAndFiles = scandir($path); |
2424
|
|
|
// Remove "." and ".." from the list |
2425
|
|
|
$entries = array_slice($foldersAndFiles, 2); |
2426
|
|
|
// Parse every result... |
2427
|
|
|
foreach ($entries as $entry) { |
2428
|
|
|
// And call this function again recursively, with the same permissions |
2429
|
|
|
recursiveChmod($path.'/'.$entry, $filePerm, $dirPerm); |
2430
|
|
|
} |
2431
|
|
|
|
2432
|
|
|
// When we are done with the contents of the directory, we chmod the directory itself |
2433
|
|
|
chmod($path, $dirPerm); |
2434
|
|
|
} |
2435
|
|
|
|
2436
|
|
|
// Everything seemed to work out well, return true |
2437
|
|
|
return true; |
2438
|
|
|
} |
2439
|
|
|
|
2440
|
|
|
/** |
2441
|
|
|
* Check if user can access to this item. |
2442
|
|
|
* |
2443
|
|
|
* @param int $item_id ID of item |
2444
|
|
|
* @param array $SETTINGS |
2445
|
|
|
* |
2446
|
|
|
* @return bool|string |
2447
|
|
|
*/ |
2448
|
|
|
function accessToItemIsGranted(int $item_id, array $SETTINGS) |
2449
|
|
|
{ |
2450
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
2451
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
2452
|
|
|
// Prepare superGlobal variables |
2453
|
|
|
$session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION'); |
2454
|
|
|
$session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION'); |
2455
|
|
|
// Load item data |
2456
|
|
|
$data = DB::queryFirstRow( |
2457
|
|
|
'SELECT id_tree |
2458
|
|
|
FROM ' . prefixTable('items') . ' |
2459
|
|
|
WHERE id = %i', |
2460
|
|
|
$item_id |
2461
|
|
|
); |
2462
|
|
|
// Check if user can access this folder |
2463
|
|
|
if (in_array($data['id_tree'], $session_groupes_visibles) === false) { |
2464
|
|
|
// Now check if this folder is restricted to user |
2465
|
|
|
if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true |
2466
|
|
|
&& in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false |
2467
|
|
|
) { |
2468
|
|
|
return 'ERR_FOLDER_NOT_ALLOWED'; |
2469
|
|
|
} |
2470
|
|
|
} |
2471
|
|
|
|
2472
|
|
|
return true; |
2473
|
|
|
} |
2474
|
|
|
|
2475
|
|
|
/** |
2476
|
|
|
* Creates a unique key. |
2477
|
|
|
* |
2478
|
|
|
* @param int $lenght Key lenght |
2479
|
|
|
* |
2480
|
|
|
* @return string |
2481
|
|
|
*/ |
2482
|
|
|
function uniqidReal(int $lenght = 13): string |
2483
|
|
|
{ |
2484
|
|
|
if (function_exists('random_bytes')) { |
2485
|
|
|
$bytes = random_bytes(intval(ceil($lenght / 2))); |
2486
|
|
|
} elseif (function_exists('openssl_random_pseudo_bytes')) { |
2487
|
|
|
$bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2))); |
2488
|
|
|
} else { |
2489
|
|
|
throw new Exception('no cryptographically secure random function available'); |
2490
|
|
|
} |
2491
|
|
|
|
2492
|
|
|
return substr(bin2hex($bytes), 0, $lenght); |
2493
|
|
|
} |
2494
|
|
|
|
2495
|
|
|
/** |
2496
|
|
|
* Obfuscate an email. |
2497
|
|
|
* |
2498
|
|
|
* @param string $email Email address |
2499
|
|
|
* |
2500
|
|
|
* @return string |
2501
|
|
|
*/ |
2502
|
|
|
function obfuscateEmail(string $email): string |
2503
|
|
|
{ |
2504
|
|
|
$email = explode("@", $email); |
2505
|
|
|
$name = $email[0]; |
2506
|
|
|
if (strlen($name) > 3) { |
2507
|
|
|
$name = substr($name, 0, 2); |
2508
|
|
|
for ($i = 0; $i < strlen($email[0]) - 3; $i++) { |
2509
|
|
|
$name .= "*"; |
2510
|
|
|
} |
2511
|
|
|
$name .= substr($email[0], -1, 1); |
2512
|
|
|
} |
2513
|
|
|
$host = explode(".", $email[1])[0]; |
2514
|
|
|
if (strlen($host) > 3) { |
2515
|
|
|
$host = substr($host, 0, 1); |
2516
|
|
|
for ($i = 0; $i < strlen(explode(".", $email[1])[0]) - 2; $i++) { |
2517
|
|
|
$host .= "*"; |
2518
|
|
|
} |
2519
|
|
|
$host .= substr(explode(".", $email[1])[0], -1, 1); |
2520
|
|
|
} |
2521
|
|
|
$email = $name . "@" . $host . "." . explode(".", $email[1])[1]; |
2522
|
|
|
return $email; |
2523
|
|
|
} |
2524
|
|
|
|
2525
|
|
|
/** |
2526
|
|
|
* Perform a Query. |
2527
|
|
|
* |
2528
|
|
|
* @param array $SETTINGS Teamapss settings |
2529
|
|
|
* @param string $fields Fields to use |
2530
|
|
|
* @param string $table Table to use |
2531
|
|
|
* |
2532
|
|
|
* @return array |
2533
|
|
|
*/ |
2534
|
|
|
function performDBQuery(array $SETTINGS, string $fields, string $table): array |
2535
|
|
|
{ |
2536
|
|
|
// include librairies & connect to DB |
2537
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php'; |
2538
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
2539
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
2540
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
2541
|
|
|
} |
2542
|
|
|
DB::$host = DB_HOST; |
2543
|
|
|
DB::$user = DB_USER; |
2544
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
2545
|
|
|
DB::$dbName = DB_NAME; |
2546
|
|
|
DB::$port = DB_PORT; |
2547
|
|
|
DB::$encoding = DB_ENCODING; |
2548
|
|
|
// Insert log in DB |
2549
|
|
|
return DB::query( |
2550
|
|
|
'SELECT ' . $fields . ' |
2551
|
|
|
FROM ' . prefixTable($table) |
2552
|
|
|
); |
2553
|
|
|
} |
2554
|
|
|
|
2555
|
|
|
/** |
2556
|
|
|
* Undocumented function. |
2557
|
|
|
* |
2558
|
|
|
* @param int $bytes Size of file |
2559
|
|
|
* |
2560
|
|
|
* @return string |
2561
|
|
|
*/ |
2562
|
|
|
function formatSizeUnits(int $bytes): string |
2563
|
|
|
{ |
2564
|
|
|
if ($bytes >= 1073741824) { |
2565
|
|
|
$bytes = number_format($bytes / 1073741824, 2) . ' GB'; |
2566
|
|
|
} elseif ($bytes >= 1048576) { |
2567
|
|
|
$bytes = number_format($bytes / 1048576, 2) . ' MB'; |
2568
|
|
|
} elseif ($bytes >= 1024) { |
2569
|
|
|
$bytes = number_format($bytes / 1024, 2) . ' KB'; |
2570
|
|
|
} elseif ($bytes > 1) { |
2571
|
|
|
$bytes .= ' bytes'; |
2572
|
|
|
} elseif ($bytes === 1) { |
2573
|
|
|
$bytes .= ' byte'; |
2574
|
|
|
} else { |
2575
|
|
|
$bytes = '0 bytes'; |
2576
|
|
|
} |
2577
|
|
|
|
2578
|
|
|
return $bytes; |
2579
|
|
|
} |
2580
|
|
|
|
2581
|
|
|
/** |
2582
|
|
|
* Generate user pair of keys. |
2583
|
|
|
* |
2584
|
|
|
* @param string $userPwd User password |
2585
|
|
|
* |
2586
|
|
|
* @return array |
2587
|
|
|
*/ |
2588
|
|
|
function generateUserKeys(string $userPwd): array |
2589
|
|
|
{ |
2590
|
|
|
// include library |
2591
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php'; |
2592
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php'; |
2593
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2594
|
|
|
// Load classes |
2595
|
|
|
$rsa = new Crypt_RSA(); |
2596
|
|
|
$cipher = new Crypt_AES(); |
2597
|
|
|
// Create the private and public key |
2598
|
|
|
$res = $rsa->createKey(4096); |
2599
|
|
|
// Encrypt the privatekey |
2600
|
|
|
$cipher->setPassword($userPwd); |
2601
|
|
|
$privatekey = $cipher->encrypt($res['privatekey']); |
2602
|
|
|
return [ |
2603
|
|
|
'private_key' => base64_encode($privatekey), |
2604
|
|
|
'public_key' => base64_encode($res['publickey']), |
2605
|
|
|
'private_key_clear' => base64_encode($res['privatekey']), |
2606
|
|
|
]; |
2607
|
|
|
} |
2608
|
|
|
|
2609
|
|
|
/** |
2610
|
|
|
* Permits to decrypt the user's privatekey. |
2611
|
|
|
* |
2612
|
|
|
* @param string $userPwd User password |
2613
|
|
|
* @param string $userPrivateKey User private key |
2614
|
|
|
* |
2615
|
|
|
* @return string |
2616
|
|
|
*/ |
2617
|
|
|
function decryptPrivateKey(string $userPwd, string $userPrivateKey): string |
2618
|
|
|
{ |
2619
|
|
|
if (empty($userPwd) === false) { |
2620
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2621
|
|
|
// Load classes |
2622
|
|
|
$cipher = new Crypt_AES(); |
2623
|
|
|
// Encrypt the privatekey |
2624
|
|
|
$cipher->setPassword($userPwd); |
2625
|
|
|
try { |
2626
|
|
|
return base64_encode($cipher->decrypt(base64_decode($userPrivateKey))); |
2627
|
|
|
} catch (Exception $e) { |
2628
|
|
|
return $e; |
2629
|
|
|
} |
2630
|
|
|
} |
2631
|
|
|
return ''; |
2632
|
|
|
} |
2633
|
|
|
|
2634
|
|
|
/** |
2635
|
|
|
* Permits to encrypt the user's privatekey. |
2636
|
|
|
* |
2637
|
|
|
* @param string $userPwd User password |
2638
|
|
|
* @param string $userPrivateKey User private key |
2639
|
|
|
* |
2640
|
|
|
* @return string |
2641
|
|
|
*/ |
2642
|
|
|
function encryptPrivateKey(string $userPwd, string $userPrivateKey): string |
2643
|
|
|
{ |
2644
|
|
|
if (empty($userPwd) === false) { |
2645
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2646
|
|
|
// Load classes |
2647
|
|
|
$cipher = new Crypt_AES(); |
2648
|
|
|
// Encrypt the privatekey |
2649
|
|
|
$cipher->setPassword($userPwd); |
2650
|
|
|
try { |
2651
|
|
|
return base64_encode($cipher->encrypt(base64_decode($userPrivateKey))); |
2652
|
|
|
} catch (Exception $e) { |
2653
|
|
|
return $e; |
2654
|
|
|
} |
2655
|
|
|
} |
2656
|
|
|
return ''; |
2657
|
|
|
} |
2658
|
|
|
|
2659
|
|
|
/** |
2660
|
|
|
* Encrypts a string using AES. |
2661
|
|
|
* |
2662
|
|
|
* @param string $data String to encrypt |
2663
|
|
|
* |
2664
|
|
|
* @return array |
2665
|
|
|
*/ |
2666
|
|
|
function doDataEncryption(string $data): array |
2667
|
|
|
{ |
2668
|
|
|
// Includes |
2669
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2670
|
|
|
// Load classes |
2671
|
|
|
$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC); |
2672
|
|
|
// Generate an object key |
2673
|
|
|
$objectKey = uniqidReal(32); |
2674
|
|
|
// Set it as password |
2675
|
|
|
$cipher->setPassword($objectKey); |
2676
|
|
|
return [ |
2677
|
|
|
'encrypted' => base64_encode($cipher->encrypt($data)), |
2678
|
|
|
'objectKey' => base64_encode($objectKey), |
2679
|
|
|
]; |
2680
|
|
|
} |
2681
|
|
|
|
2682
|
|
|
/** |
2683
|
|
|
* Decrypts a string using AES. |
2684
|
|
|
* |
2685
|
|
|
* @param string $data Encrypted data |
2686
|
|
|
* @param string $key Key to uncrypt |
2687
|
|
|
* |
2688
|
|
|
* @return string |
2689
|
|
|
*/ |
2690
|
|
|
function doDataDecryption(string $data, string $key): string |
2691
|
|
|
{ |
2692
|
|
|
// Includes |
2693
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2694
|
|
|
// Load classes |
2695
|
|
|
$cipher = new Crypt_AES(); |
2696
|
|
|
// Set the object key |
2697
|
|
|
$cipher->setPassword(base64_decode($key)); |
2698
|
|
|
return base64_encode($cipher->decrypt(base64_decode($data))); |
2699
|
|
|
} |
2700
|
|
|
|
2701
|
|
|
/** |
2702
|
|
|
* Encrypts using RSA a string using a public key. |
2703
|
|
|
* |
2704
|
|
|
* @param string $key Key to be encrypted |
2705
|
|
|
* @param string $publicKey User public key |
2706
|
|
|
* |
2707
|
|
|
* @return string |
2708
|
|
|
*/ |
2709
|
|
|
function encryptUserObjectKey(string $key, string $publicKey): string |
2710
|
|
|
{ |
2711
|
|
|
// Includes |
2712
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php'; |
2713
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php'; |
2714
|
|
|
// Load classes |
2715
|
|
|
$rsa = new Crypt_RSA(); |
2716
|
|
|
$rsa->loadKey(base64_decode($publicKey)); |
2717
|
|
|
// Encrypt |
2718
|
|
|
return base64_encode($rsa->encrypt(base64_decode($key))); |
2719
|
|
|
} |
2720
|
|
|
|
2721
|
|
|
/** |
2722
|
|
|
* Decrypts using RSA an encrypted string using a private key. |
2723
|
|
|
* |
2724
|
|
|
* @param string $key Encrypted key |
2725
|
|
|
* @param string $privateKey User private key |
2726
|
|
|
* |
2727
|
|
|
* @return string |
2728
|
|
|
*/ |
2729
|
|
|
function decryptUserObjectKey(string $key, string $privateKey): string |
2730
|
|
|
{ |
2731
|
|
|
// Includes |
2732
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php'; |
2733
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php'; |
2734
|
|
|
// Load classes |
2735
|
|
|
$rsa = new Crypt_RSA(); |
2736
|
|
|
$rsa->loadKey(base64_decode($privateKey)); |
2737
|
|
|
// Decrypt |
2738
|
|
|
try { |
2739
|
|
|
$ret = base64_encode($rsa->decrypt(base64_decode($key))); |
2740
|
|
|
} catch (Exception $e) { |
2741
|
|
|
return $e; |
2742
|
|
|
} |
2743
|
|
|
|
2744
|
|
|
return $ret; |
2745
|
|
|
} |
2746
|
|
|
|
2747
|
|
|
/** |
2748
|
|
|
* Encrypts a file. |
2749
|
|
|
* |
2750
|
|
|
* @param string $fileInName File name |
2751
|
|
|
* @param string $fileInPath Path to file |
2752
|
|
|
* |
2753
|
|
|
* @return array |
2754
|
|
|
*/ |
2755
|
|
|
function encryptFile(string $fileInName, string $fileInPath): array |
2756
|
|
|
{ |
2757
|
|
|
if (defined('FILE_BUFFER_SIZE') === false) { |
2758
|
|
|
define('FILE_BUFFER_SIZE', 128 * 1024); |
2759
|
|
|
} |
2760
|
|
|
|
2761
|
|
|
// Includes |
2762
|
|
|
include_once '../includes/config/include.php'; |
2763
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php'; |
2764
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php'; |
2765
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2766
|
|
|
// Load classes |
2767
|
|
|
$cipher = new Crypt_AES(); |
2768
|
|
|
// Generate an object key |
2769
|
|
|
$objectKey = uniqidReal(32); |
2770
|
|
|
// Set it as password |
2771
|
|
|
$cipher->setPassword($objectKey); |
2772
|
|
|
// Prevent against out of memory |
2773
|
|
|
$cipher->enableContinuousBuffer(); |
2774
|
|
|
//$cipher->disablePadding(); |
2775
|
|
|
|
2776
|
|
|
// Encrypt the file content |
2777
|
|
|
$plaintext = file_get_contents( |
2778
|
|
|
filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL) |
2779
|
|
|
); |
2780
|
|
|
$ciphertext = $cipher->encrypt($plaintext); |
2781
|
|
|
// Save new file |
2782
|
|
|
$hash = md5($plaintext); |
2783
|
|
|
$fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash; |
2784
|
|
|
file_put_contents($fileOut, $ciphertext); |
2785
|
|
|
unlink($fileInPath . '/' . $fileInName); |
2786
|
|
|
return [ |
2787
|
|
|
'fileHash' => base64_encode($hash), |
2788
|
|
|
'objectKey' => base64_encode($objectKey), |
2789
|
|
|
]; |
2790
|
|
|
} |
2791
|
|
|
|
2792
|
|
|
/** |
2793
|
|
|
* Decrypt a file. |
2794
|
|
|
* |
2795
|
|
|
* @param string $fileName File name |
2796
|
|
|
* @param string $filePath Path to file |
2797
|
|
|
* @param string $key Key to use |
2798
|
|
|
* |
2799
|
|
|
* @return string |
2800
|
|
|
*/ |
2801
|
|
|
function decryptFile(string $fileName, string $filePath, string $key): string |
2802
|
|
|
{ |
2803
|
|
|
if (! defined('FILE_BUFFER_SIZE')) { |
2804
|
|
|
define('FILE_BUFFER_SIZE', 128 * 1024); |
2805
|
|
|
} |
2806
|
|
|
|
2807
|
|
|
// Includes |
2808
|
|
|
include_once '../includes/config/include.php'; |
2809
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php'; |
2810
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php'; |
2811
|
|
|
include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php'; |
2812
|
|
|
// Get file name |
2813
|
|
|
$fileName = base64_decode($fileName); |
2814
|
|
|
// Load classes |
2815
|
|
|
$cipher = new Crypt_AES(); |
2816
|
|
|
// Set the object key |
2817
|
|
|
$cipher->setPassword(base64_decode($key)); |
2818
|
|
|
// Prevent against out of memory |
2819
|
|
|
$cipher->enableContinuousBuffer(); |
2820
|
|
|
$cipher->disablePadding(); |
2821
|
|
|
// Get file content |
2822
|
|
|
$ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName); |
2823
|
|
|
// Decrypt file content and return |
2824
|
|
|
return base64_encode($cipher->decrypt($ciphertext)); |
2825
|
|
|
} |
2826
|
|
|
|
2827
|
|
|
/** |
2828
|
|
|
* Generate a simple password |
2829
|
|
|
* |
2830
|
|
|
* @param int $length Length of string |
2831
|
|
|
* @param bool $symbolsincluded Allow symbols |
2832
|
|
|
* |
2833
|
|
|
* @return string |
2834
|
|
|
*/ |
2835
|
|
|
function generateQuickPassword(int $length = 16, bool $symbolsincluded = true): string |
2836
|
|
|
{ |
2837
|
|
|
// Generate new user password |
2838
|
|
|
$small_letters = range('a', 'z'); |
2839
|
|
|
$big_letters = range('A', 'Z'); |
2840
|
|
|
$digits = range(0, 9); |
2841
|
|
|
$symbols = $symbolsincluded === true ? |
2842
|
|
|
['#', '_', '-', '@', '$', '+', '&'] : []; |
2843
|
|
|
$res = array_merge($small_letters, $big_letters, $digits, $symbols); |
2844
|
|
|
$count = count($res); |
2845
|
|
|
// first variant |
2846
|
|
|
|
2847
|
|
|
$random_string = ''; |
2848
|
|
|
for ($i = 0; $i < $length; ++$i) { |
2849
|
|
|
$random_string .= $res[random_int(0, $count - 1)]; |
2850
|
|
|
} |
2851
|
|
|
|
2852
|
|
|
return $random_string; |
2853
|
|
|
} |
2854
|
|
|
|
2855
|
|
|
/** |
2856
|
|
|
* Permit to store the sharekey of an object for users. |
2857
|
|
|
* |
2858
|
|
|
* @param string $object_name Type for table selection |
2859
|
|
|
* @param int $post_folder_is_personal Personal |
2860
|
|
|
* @param int $post_folder_id Folder |
2861
|
|
|
* @param int $post_object_id Object |
2862
|
|
|
* @param string $objectKey Object key |
2863
|
|
|
* @param array $SETTINGS Teampass settings |
2864
|
|
|
* |
2865
|
|
|
* @return void |
2866
|
|
|
*/ |
2867
|
|
|
function storeUsersShareKey( |
2868
|
|
|
string $object_name, |
2869
|
|
|
int $post_folder_is_personal, |
2870
|
|
|
int $post_folder_id, |
2871
|
|
|
int $post_object_id, |
2872
|
|
|
string $objectKey, |
2873
|
|
|
array $SETTINGS |
2874
|
|
|
): void { |
2875
|
|
|
// include librairies & connect to DB |
2876
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php'; |
2877
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
2878
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
2879
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
2880
|
|
|
} |
2881
|
|
|
DB::$host = DB_HOST; |
2882
|
|
|
DB::$user = DB_USER; |
2883
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
2884
|
|
|
DB::$dbName = DB_NAME; |
2885
|
|
|
DB::$port = DB_PORT; |
2886
|
|
|
DB::$encoding = DB_ENCODING; |
2887
|
|
|
// Delete existing entries for this object |
2888
|
|
|
DB::delete( |
2889
|
|
|
$object_name, |
2890
|
|
|
'object_id = %i', |
2891
|
|
|
$post_object_id |
2892
|
|
|
); |
2893
|
|
|
// Superglobals |
2894
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php'; |
2895
|
|
|
$superGlobal = new protect\SuperGlobal\SuperGlobal(); |
2896
|
|
|
// Prepare superGlobal variables |
2897
|
|
|
$sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION'); |
2898
|
|
|
$sessionUserId = $superGlobal->get('user_id', 'SESSION'); |
2899
|
|
|
$sessionUserPublicKey = $superGlobal->get('public_key', 'SESSION', 'user'); |
2900
|
|
|
if ( |
2901
|
|
|
(int) $post_folder_is_personal === 1 |
2902
|
|
|
&& in_array($post_folder_id, $sessionPpersonaFolders) === true |
2903
|
|
|
) { |
2904
|
|
|
// If this is a personal object |
2905
|
|
|
// Only create the sharekey for user |
2906
|
|
|
DB::insert( |
2907
|
|
|
$object_name, |
2908
|
|
|
[ |
2909
|
|
|
'object_id' => (int) $post_object_id, |
2910
|
|
|
'user_id' => (int) $sessionUserId, |
2911
|
|
|
'share_key' => encryptUserObjectKey($objectKey, $sessionUserPublicKey), |
2912
|
|
|
] |
2913
|
|
|
); |
2914
|
|
|
} else { |
2915
|
|
|
// This is a public object |
2916
|
|
|
// Create sharekey for each user |
2917
|
|
|
$users = DB::query( |
2918
|
|
|
'SELECT id, public_key |
2919
|
|
|
FROM ' . prefixTable('users') . ' |
2920
|
|
|
WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '") |
2921
|
|
|
AND public_key != ""' |
2922
|
|
|
); |
2923
|
|
|
foreach ($users as $user) { |
2924
|
|
|
// Insert in DB the new object key for this item by user |
2925
|
|
|
DB::insert( |
2926
|
|
|
$object_name, |
2927
|
|
|
[ |
2928
|
|
|
'object_id' => $post_object_id, |
2929
|
|
|
'user_id' => (int) $user['id'], |
2930
|
|
|
'share_key' => encryptUserObjectKey( |
2931
|
|
|
$objectKey, |
2932
|
|
|
$user['public_key'] |
2933
|
|
|
), |
2934
|
|
|
] |
2935
|
|
|
); |
2936
|
|
|
} |
2937
|
|
|
} |
2938
|
|
|
} |
2939
|
|
|
|
2940
|
|
|
/** |
2941
|
|
|
* Is this string base64 encoded? |
2942
|
|
|
* |
2943
|
|
|
* @param string $str Encoded string? |
2944
|
|
|
* |
2945
|
|
|
* @return bool |
2946
|
|
|
*/ |
2947
|
|
|
function isBase64(string $str): bool |
2948
|
|
|
{ |
2949
|
|
|
$str = (string) trim($str); |
2950
|
|
|
if (! isset($str[0])) { |
2951
|
|
|
return false; |
2952
|
|
|
} |
2953
|
|
|
|
2954
|
|
|
$base64String = (string) base64_decode($str, true); |
2955
|
|
|
if ($base64String && base64_encode($base64String) === $str) { |
2956
|
|
|
return true; |
2957
|
|
|
} |
2958
|
|
|
|
2959
|
|
|
return false; |
2960
|
|
|
} |
2961
|
|
|
|
2962
|
|
|
/** |
2963
|
|
|
* Undocumented function |
2964
|
|
|
* |
2965
|
|
|
* @param string $field Parameter |
2966
|
|
|
* |
2967
|
|
|
* @return array|bool|resource|string |
2968
|
|
|
*/ |
2969
|
|
|
function filterString(string $field) |
2970
|
|
|
{ |
2971
|
|
|
// Sanitize string |
2972
|
|
|
$field = filter_var(trim($field), FILTER_SANITIZE_STRING); |
2973
|
|
|
if (empty($field) === false) { |
2974
|
|
|
// Load AntiXSS |
2975
|
|
|
include_once '../includes/libraries/voku/helper/AntiXSS.php'; |
2976
|
|
|
$antiXss = new voku\helper\AntiXSS(); |
2977
|
|
|
// Return |
2978
|
|
|
return $antiXss->xss_clean($field); |
2979
|
|
|
} |
2980
|
|
|
|
2981
|
|
|
return false; |
2982
|
|
|
} |
2983
|
|
|
|
2984
|
|
|
/** |
2985
|
|
|
* CHeck if provided credentials are allowed on server |
2986
|
|
|
* |
2987
|
|
|
* @param string $login User Login |
2988
|
|
|
* @param string $password User Pwd |
2989
|
|
|
* @param array $SETTINGS Teampass settings |
2990
|
|
|
* |
2991
|
|
|
* @return bool |
2992
|
|
|
*/ |
2993
|
|
|
function ldapCheckUserPassword(string $login, string $password, array $SETTINGS): bool |
2994
|
|
|
{ |
2995
|
|
|
// Build ldap configuration array |
2996
|
|
|
$config = [ |
2997
|
|
|
// Mandatory Configuration Options |
2998
|
|
|
'hosts' => [$SETTINGS['ldap_hosts']], |
2999
|
|
|
'base_dn' => $SETTINGS['ldap_bdn'], |
3000
|
|
|
'username' => $SETTINGS['ldap_username'], |
3001
|
|
|
'password' => $SETTINGS['ldap_password'], |
3002
|
|
|
|
3003
|
|
|
// Optional Configuration Options |
3004
|
|
|
'port' => $SETTINGS['ldap_port'], |
3005
|
|
|
'use_ssl' => (int) $SETTINGS['ldap_ssl'] === 1 ? true : false, |
3006
|
|
|
'use_tls' => (int) $SETTINGS['ldap_tls'] === 1 ? true : false, |
3007
|
|
|
'version' => 3, |
3008
|
|
|
'timeout' => 5, |
3009
|
|
|
'follow_referrals' => false, |
3010
|
|
|
|
3011
|
|
|
// Custom LDAP Options |
3012
|
|
|
'options' => [ |
3013
|
|
|
// See: http://php.net/ldap_set_option |
3014
|
|
|
LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD, |
3015
|
|
|
], |
3016
|
|
|
]; |
3017
|
|
|
// Load expected libraries |
3018
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php'; |
3019
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php'; |
3020
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php'; |
3021
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php'; |
3022
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapInterface.php'; |
3023
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapBase.php'; |
3024
|
|
|
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php'; |
3025
|
|
|
$ad = new SplClassLoader('LdapRecord', '../includes/libraries'); |
3026
|
|
|
$ad->register(); |
3027
|
|
|
$connection = new Connection($config); |
3028
|
|
|
// Connect to LDAP |
3029
|
|
|
try { |
3030
|
|
|
$connection->connect(); |
3031
|
|
|
} catch (\LdapRecord\Auth\BindException $e) { |
3032
|
|
|
$error = $e->getDetailedError(); |
3033
|
|
|
echo 'Error : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(); |
3034
|
|
|
return false; |
3035
|
|
|
} |
3036
|
|
|
|
3037
|
|
|
// Authenticate user |
3038
|
|
|
try { |
3039
|
|
|
if ($SETTINGS['ldap_type'] === 'ActiveDirectory') { |
3040
|
|
|
$connection->auth()->attempt($login, $password, $stayAuthenticated = true); |
3041
|
|
|
} else { |
3042
|
|
|
$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); |
3043
|
|
|
} |
3044
|
|
|
} catch (\LdapRecord\Auth\BindException $e) { |
3045
|
|
|
$error = $e->getDetailedError(); |
3046
|
|
|
echo 'Error : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(); |
3047
|
|
|
return false; |
3048
|
|
|
} |
3049
|
|
|
|
3050
|
|
|
return true; |
3051
|
|
|
} |
3052
|
|
|
|
3053
|
|
|
/** |
3054
|
|
|
* Removes from DB all sharekeys of this user |
3055
|
|
|
* |
3056
|
|
|
* @param int $userId User's id |
3057
|
|
|
* @param array $SETTINGS Teampass settings |
3058
|
|
|
* |
3059
|
|
|
* @return bool |
3060
|
|
|
*/ |
3061
|
|
|
function deleteUserObjetsKeys(int $userId, array $SETTINGS): bool |
3062
|
|
|
{ |
3063
|
|
|
// include librairies & connect to DB |
3064
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php'; |
3065
|
|
|
include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; |
3066
|
|
|
if (defined('DB_PASSWD_CLEAR') === false) { |
3067
|
|
|
define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS)); |
3068
|
|
|
} |
3069
|
|
|
DB::$host = DB_HOST; |
3070
|
|
|
DB::$user = DB_USER; |
3071
|
|
|
DB::$password = DB_PASSWD_CLEAR; |
3072
|
|
|
DB::$dbName = DB_NAME; |
3073
|
|
|
DB::$port = DB_PORT; |
3074
|
|
|
DB::$encoding = DB_ENCODING; |
3075
|
|
|
// Remove all item sharekeys items |
3076
|
|
|
DB::delete( |
3077
|
|
|
prefixTable('sharekeys_items'), |
3078
|
|
|
'user_id = %i', |
3079
|
|
|
$userId |
3080
|
|
|
); |
3081
|
|
|
// Remove all item sharekeys files |
3082
|
|
|
DB::delete( |
3083
|
|
|
prefixTable('sharekeys_files'), |
3084
|
|
|
'user_id = %i', |
3085
|
|
|
$userId |
3086
|
|
|
); |
3087
|
|
|
// Remove all item sharekeys fields |
3088
|
|
|
DB::delete( |
3089
|
|
|
prefixTable('sharekeys_fields'), |
3090
|
|
|
'user_id = %i', |
3091
|
|
|
$userId |
3092
|
|
|
); |
3093
|
|
|
// Remove all item sharekeys logs |
3094
|
|
|
DB::delete( |
3095
|
|
|
prefixTable('sharekeys_logs'), |
3096
|
|
|
'user_id = %i', |
3097
|
|
|
$userId |
3098
|
|
|
); |
3099
|
|
|
// Remove all item sharekeys suggestions |
3100
|
|
|
DB::delete( |
3101
|
|
|
prefixTable('sharekeys_suggestions'), |
3102
|
|
|
'user_id = %i', |
3103
|
|
|
$userId |
3104
|
|
|
); |
3105
|
|
|
return false; |
3106
|
|
|
} |
3107
|
|
|
|
3108
|
|
|
/** |
3109
|
|
|
* Manage list of timezones $SETTINGS Teampass settings |
3110
|
|
|
* |
3111
|
|
|
* @return array |
3112
|
|
|
*/ |
3113
|
|
|
function timezone_list() |
3114
|
|
|
{ |
3115
|
|
|
static $timezones = null; |
3116
|
|
|
if ($timezones === null) { |
3117
|
|
|
$timezones = []; |
3118
|
|
|
$offsets = []; |
3119
|
|
|
$now = new DateTime('now', new DateTimeZone('UTC')); |
3120
|
|
|
foreach (DateTimeZone::listIdentifiers() as $timezone) { |
3121
|
|
|
$now->setTimezone(new DateTimeZone($timezone)); |
3122
|
|
|
$offsets[] = $offset = $now->getOffset(); |
3123
|
|
|
$timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone); |
3124
|
|
|
} |
3125
|
|
|
|
3126
|
|
|
array_multisort($offsets, $timezones); |
3127
|
|
|
} |
3128
|
|
|
|
3129
|
|
|
return $timezones; |
3130
|
|
|
} |
3131
|
|
|
|
3132
|
|
|
/** |
3133
|
|
|
* Provide timezone offset |
3134
|
|
|
* |
3135
|
|
|
* @param int $offset Timezone offset |
3136
|
|
|
* |
3137
|
|
|
* @return string |
3138
|
|
|
*/ |
3139
|
|
|
function format_GMT_offset($offset): string |
3140
|
|
|
{ |
3141
|
|
|
$hours = intval($offset / 3600); |
3142
|
|
|
$minutes = abs(intval($offset % 3600 / 60)); |
3143
|
|
|
return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : ''); |
3144
|
|
|
} |
3145
|
|
|
|
3146
|
|
|
/** |
3147
|
|
|
* Provides timezone name |
3148
|
|
|
* |
3149
|
|
|
* @param string $name Timezone name |
3150
|
|
|
* |
3151
|
|
|
* @return string |
3152
|
|
|
*/ |
3153
|
|
|
function format_timezone_name($name): string |
3154
|
|
|
{ |
3155
|
|
|
$name = str_replace('/', ', ', $name); |
3156
|
|
|
$name = str_replace('_', ' ', $name); |
3157
|
|
|
|
3158
|
|
|
return str_replace('St ', 'St. ', $name); |
3159
|
|
|
} |
3160
|
|
|
|
3161
|
|
|
/** |
3162
|
|
|
* Provides info about if user should use MFA |
3163
|
|
|
* |
3164
|
|
|
* @param string $userRolesIds User roles ids |
3165
|
|
|
* @param string $mfaRoles Roles for which MFA is requested |
3166
|
|
|
* |
3167
|
|
|
* @return bool |
3168
|
|
|
*/ |
3169
|
|
|
function mfa_auth_requested(string $userRolesIds, string $mfaRoles): bool |
3170
|
|
|
{ |
3171
|
|
|
if (empty($mfaRoles) === true) { |
3172
|
|
|
return true; |
3173
|
|
|
} |
3174
|
|
|
|
3175
|
|
|
$mfaRoles = array_values(json_decode($mfaRoles, true)); |
3176
|
|
|
$userRolesIds = array_filter(explode(';', $userRolesIds)); |
3177
|
|
|
if (count($mfaRoles) === 0 || count($mfaRoles) === 0) { |
3178
|
|
|
return true; |
3179
|
|
|
} |
3180
|
|
|
|
3181
|
|
|
if (count(array_intersect($mfaRoles, $userRolesIds)) > 0) { |
3182
|
|
|
return true; |
3183
|
|
|
} |
3184
|
|
|
return false; |
3185
|
|
|
} |
3186
|
|
|
|