dolibarr 23.0.4
security.lib.php
Go to the documentation of this file.
1<?php
2
3/* Copyright (C) 2008-2021 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2008-2021 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2020 Ferran Marcet <fmarcet@2byte.es>
6 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 * or see https://www.gnu.org/
22 */
23
41function dol_encode($chain, $key = '1')
42{
43 if (is_numeric($key) && $key == '1') { // rule 1 is offset of 17 for char
44 $output_tab = array();
45 $strlength = dol_strlen($chain);
46 for ($i = 0; $i < $strlength; $i++) {
47 $output_tab[$i] = chr(ord(substr($chain, $i, 1)) + 17);
48 }
49 $chain = implode("", $output_tab);
50 } elseif ($key) {
51 $result = '';
52 $strlength = dol_strlen($chain);
53 for ($i = 0; $i < $strlength; $i++) {
54 $keychar = substr($key, ($i % strlen($key)) - 1, 1);
55 $result .= chr(ord(substr($chain, $i, 1)) + (ord($keychar) - 65));
56 }
57 $chain = $result;
58 }
59
60 return base64_encode($chain);
61}
62
72function dol_decode($chain, $key = '1')
73{
74 $chain = base64_decode($chain);
75
76 if (is_numeric($key) && $key == '1') { // rule 1 is offset of 17 for char
77 $output_tab = array();
78 $strlength = dol_strlen($chain);
79 for ($i = 0; $i < $strlength; $i++) {
80 $output_tab[$i] = chr(ord(substr($chain, $i, 1)) - 17);
81 }
82
83 $chain = implode("", $output_tab);
84 } elseif ($key) {
85 $result = '';
86 $strlength = dol_strlen($chain);
87 for ($i = 0; $i < $strlength; $i++) {
88 $keychar = substr($key, ($i % strlen($key)) - 1, 1);
89 $result .= chr(ord(substr($chain, $i, 1)) - (ord($keychar) - 65));
90 }
91 $chain = $result;
92 }
93
94 return $chain;
95}
96
103function dolGetRandomBytes($length)
104{
105 if (function_exists('random_bytes')) { // Available with PHP 7 only.
106 return bin2hex(random_bytes((int) floor($length / 2))); // the bin2hex will double the number of bytes so we take length / 2
107 }
108
109 return bin2hex(openssl_random_pseudo_bytes((int) floor($length / 2))); // the bin2hex will double the number of bytes so we take length / 2. May be very slow on Windows.
110}
111
112
113define('MAIN_SECURITY_REVERSIBLE_ALGO', 'AES-256-CTR');
114
128function dolEncrypt($chain, $key = '', $ciphering = '', $forceseed = '')
129{
130 global $conf;
131 global $dolibarr_disable_dolcrypt_for_debug;
132
133 if ($chain === '' || is_null($chain)) {
134 return '';
135 }
136
137 $reg = array();
138 if (preg_match('/^dolcrypt:([^:]+):(.+)$/', $chain, $reg)) {
139 // The $chain is already a encrypted string
140 return $chain;
141 }
142
143 if (empty($key)) {
144 if (!empty($conf->file->dolcrypt_key)) {
145 // If dolcrypt_key is defined, we used it in priority. Note: this param was never been set for the moment.
146 $key = $conf->file->dolcrypt_key;
147 } else {
148 // We fall back on the instance_unique_id (coming from $dolibarr_main_instance_unique_id, for backward compatibility).
149 $key = $conf->file->instance_unique_id;
150 }
151 }
152 if (empty($ciphering)) {
153 $ciphering = constant('MAIN_SECURITY_REVERSIBLE_ALGO');
154 }
155
156 $newchain = $chain;
157
158 if (function_exists('openssl_encrypt') && empty($dolibarr_disable_dolcrypt_for_debug)) {
159 if (empty($key)) {
160 return $chain;
161 }
162
163 $ivlen = 16;
164 if (function_exists('openssl_cipher_iv_length')) {
165 $ivlen = openssl_cipher_iv_length($ciphering);
166 }
167 if ($ivlen === false || $ivlen < 1 || $ivlen > 32) {
168 $ivlen = 16;
169 }
170 if (empty($forceseed)) {
171 $ivseed = dolGetRandomBytes($ivlen);
172 } else {
173 $ivseed = dol_substr(md5($forceseed), 0, $ivlen, 'ascii', 1);
174 }
175
176 $newchain = openssl_encrypt($chain, $ciphering, $key, 0, $ivseed);
177 return 'dolcrypt:'.$ciphering.':'.$ivseed.':'.$newchain;
178 } else {
179 return $chain;
180 }
181}
182
193function dolDecrypt($chain, $key = '')
194{
195 global $conf;
196
197 if ($chain === '' || is_null($chain)) {
198 return '';
199 }
200
201 if (empty($key)) {
202 if (!empty($conf->file->dolcrypt_key)) {
203 // If dolcrypt_key is defined, we used it in priority. Note: this param was never been set for the moment.
204 $key = $conf->file->dolcrypt_key;
205 } else {
206 // We fall back on the instance_unique_id (coming from $dolibarr_main_instance_unique_id, for backward compatibility).
207 $key = !empty($conf->file->instance_unique_id) ? $conf->file->instance_unique_id : "";
208 }
209 }
210
211 $reg = array();
212
213 // Old method (no more used, kept for compatibility)
214 if (preg_match('/^crypted:(.+)$/', $chain, $reg)) {
215 return dol_decode($reg[1]);
216 }
217
218 // New method
219 if (preg_match('/^dolcrypt:([^:]+):(.+)$/', $chain, $reg)) {
220 // Do not enable this log, except during debug
221 //dol_syslog("We try to decrypt the chain: ".$chain, LOG_DEBUG);
222
223 $ciphering = $reg[1];
224 if (function_exists('openssl_decrypt')) {
225 if (empty($key)) {
226 dol_syslog("Error dolDecrypt decrypt key is empty", LOG_WARNING);
227 return $chain;
228 }
229 $tmpexplode = explode(':', $reg[2]);
230 if (!empty($tmpexplode[1]) && is_string($tmpexplode[0])) {
231 $newchain = openssl_decrypt($tmpexplode[1], $ciphering, $key, 0, $tmpexplode[0]);
232 } else {
233 $newchain = openssl_decrypt((string) $tmpexplode[0], $ciphering, $key, 0, '');
234 }
235 // Test validity of decryption
236 if (!ascii_check($newchain) && !utf8_check($newchain)) {
237 dol_syslog("Error dolDecrypt failed: The key dolibarr_main_dolcrypt or dolibarr_main_instance_unique_id, found in conf.php file, is the the one used to encrypt this encrypted string", LOG_ERR);
238 return $chain;
239 }
240 } else {
241 dol_syslog("Error dolDecrypt openssl_decrypt is not available", LOG_ERR);
242 return $chain;
243 }
244 return $newchain;
245 } else {
246 return $chain;
247 }
248}
249
270function dol_hash($chain, $type = '0', $nosalt = 0, $mode = 0)
271{
272 // No need to add salt for password_hash
273 if (($type == '0' || $type == 'auto') && getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'password_hash' && function_exists('password_hash')) {
274 if (strpos($chain, "\0") !== false) {
275 // String contains a null character that can't be encoded. Return an error instead of fatal error.
276 if ($mode == 1) {
277 return array('pass_encrypted' => 'Invalid string to encrypt. Contains a null character', 'pass_encoding' => '');
278 } else {
279 return 'Invalid string to encrypt. Contains a null character.';
280 }
281 }
282
283 if ($mode == 1) {
284 return array('pass_encrypted' => password_hash($chain, PASSWORD_DEFAULT), 'pass_encoding' => 'password_hash');
285 } else {
286 return password_hash($chain, PASSWORD_DEFAULT);
287 }
288 }
289
290 // Salt value
291 if (getDolGlobalString('MAIN_SECURITY_SALT') && $type != '4' && $type !== 'openldap' && empty($nosalt)) {
292 $chain = getDolGlobalString('MAIN_SECURITY_SALT') . $chain;
293 }
294
295 if ($type == '1' || $type == 'sha1') {
296 if ($mode == 1) {
297 return array('pass_encrypted' => sha1($chain), 'pass_encoding' => 'sha1');
298 } else {
299 return sha1($chain);
300 }
301 } elseif ($type == '2' || $type == 'sha1md5') {
302 if ($mode == 1) {
303 return array('pass_encrypted' => sha1(md5($chain)), 'pass_encoding' => 'sha1md5');
304 } else {
305 return sha1(md5($chain));
306 }
307 } elseif ($type == '3' || $type == 'md5') { // For hashing with no need of security
308 if ($mode == 1) {
309 return array('pass_encrypted' => md5($chain), 'pass_encoding' => 'md5');
310 } else {
311 return md5($chain);
312 }
313 } elseif ($type == '4' || $type == 'openldap') {
314 if ($mode == 1) {
315 return array('pass_encrypted' => dolGetLdapPasswordHash($chain, getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5')), 'pass_encoding' => 'ldappasswordhash'.getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5'));
316 } else {
317 return dolGetLdapPasswordHash($chain, getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5'));
318 }
319 } elseif ($type == '5' || $type == 'sha256') {
320 if ($mode == 1) {
321 return array('pass_encrypted' => hash('sha256', $chain), 'pass_encoding' => 'sha256');
322 } else {
323 return hash('sha256', $chain);
324 }
325 } elseif ($type == '6' || $type == 'password_hash') {
326 if ($mode == 1) {
327 return array('pass_encrypted' => password_hash($chain, PASSWORD_DEFAULT), 'pass_encoding' => 'password_hash');
328 } else {
329 return password_hash($chain, PASSWORD_DEFAULT);
330 }
331 } elseif (getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'sha1') {
332 if ($mode == 1) {
333 return array('pass_encrypted' => sha1($chain), 'pass_encoding' => 'sha1');
334 } else {
335 return sha1($chain);
336 }
337 } elseif (getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'sha1md5') {
338 if ($mode == 1) {
339 return array('pass_encrypted' => sha1(md5($chain)), 'pass_encoding' => 'sha1md5');
340 } else {
341 return sha1(md5($chain));
342 }
343 }
344
345 // No particular encoding defined, use default
346 if ($mode == 1) {
347 return array('pass_encrypted' => md5($chain), 'pass_encoding' => 'md5');
348 } else {
349 return md5($chain);
350 }
351}
352
365function dol_verifyHash($chain, $hash, $type = '0')
366{
367 if ($type == '0' && getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'password_hash' && function_exists('password_verify')) {
368 // Try to autodetect which algo we used
369 if (! empty($hash[0]) && $hash[0] == '$') {
370 return password_verify($chain, $hash);
371 } elseif (dol_strlen($hash) == 32) {
372 return dol_verifyHash($chain, $hash, '3'); // md5
373 } elseif (dol_strlen($hash) == 40) {
374 return dol_verifyHash($chain, $hash, '2'); // sha1md5
375 }
376
377 return false;
378 }
379
380 return dol_hash($chain, $type) == $hash;
381}
382
390function dolGetLdapPasswordHash($password, $type = 'md5')
391{
392 if (empty($type)) {
393 $type = 'md5';
394 }
395
396 $salt = substr(sha1((string) time()), 0, 8);
397
398 if ($type === 'md5') {
399 return '{MD5}' . base64_encode(hash("md5", $password, true)); //For OpenLdap with md5 (based on an unencrypted password in base)
400 } elseif ($type === 'md5frommd5') {
401 return '{MD5}' . base64_encode(hex2bin($password)); // Create OpenLDAP MD5 password from Dolibarr MD5 password
402 } elseif ($type === 'smd5') {
403 return "{SMD5}" . base64_encode(hash("md5", $password . $salt, true) . $salt);
404 } elseif ($type === 'sha') {
405 return '{SHA}' . base64_encode(hash("sha1", $password, true));
406 } elseif ($type === 'ssha') {
407 return "{SSHA}" . base64_encode(hash("sha1", $password . $salt, true) . $salt);
408 } elseif ($type === 'sha256') {
409 return "{SHA256}" . base64_encode(hash("sha256", $password, true));
410 } elseif ($type === 'ssha256') {
411 return "{SSHA256}" . base64_encode(hash("sha256", $password . $salt, true) . $salt);
412 } elseif ($type === 'sha384') {
413 return "{SHA384}" . base64_encode(hash("sha384", $password, true));
414 } elseif ($type === 'ssha384') {
415 return "{SSHA384}" . base64_encode(hash("sha384", $password . $salt, true) . $salt);
416 } elseif ($type === 'sha512') {
417 return "{SHA512}" . base64_encode(hash("sha512", $password, true));
418 } elseif ($type === 'ssha512') {
419 return "{SSHA512}" . base64_encode(hash("sha512", $password . $salt, true) . $salt);
420 } elseif ($type === 'crypt') {
421 return '{CRYPT}' . crypt($password, $salt);
422 } elseif ($type === 'clear') {
423 return '{CLEAR}' . $password; // Just for test, plain text password is not secured !
424 }
425 return "";
426}
427
448function restrictedArea(User $user, $features, $object = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = 'fk_soc', $dbt_select = 'rowid', $isdraft = 0, $mode = 0)
449{
450 global $hookmanager;
451
452 // Define $objectid
453 if (is_object($object)) {
454 $objectid = $object->id;
455 } else {
456 $objectid = $object; // $objectid can be X or 'X,Y,Z'
457 }
458 if ($objectid == "-1") {
459 $objectid = 0;
460 }
461 if ($objectid) {
462 $objectid = preg_replace('/[^0-9\.\,]/', '', (string) $objectid); // For the case value is coming from a non sanitized user input
463 }
464
465 //dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename, $feature2, $dbt_socfield, $dbt_select, $isdraft");
466 /*print "user_id=".$user->id.", features=".$features.", feature2=".$feature2.", objectid=".$objectid;
467 print ", dbtablename=".$tableandshare.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select;
468 print ", perm: user->hasRight(".$features.($feature2 ? ",".$feature2 : "").", lire) = ".($feature2 ? $user->hasRight($features, $feature2, 'lire') : $user->hasRight($features, 'lire'))."<br>";
469 */
470
471 $parentfortableentity = '';
472
473 // Fix syntax of $features param to support non standard module names.
474 // @todo : use elseif ?
475 $originalfeatures = $features;
476 if ($features == 'agenda') {
477 $tableandshare = 'actioncomm&societe';
478 $feature2 = 'myactions|allactions';
479 $dbt_select = 'id';
480 }
481 if ($features == 'bank') {
482 $features = 'banque';
483 }
484 if ($features == 'facturerec') {
485 $features = 'facture';
486 }
487 if ($features == 'supplier_invoicerec') {
488 $features = 'fournisseur';
489 $feature2 = 'facture';
490 }
491 if ($features == 'mo') {
492 $features = 'mrp';
493 }
494 if ($features == 'member') {
495 $features = 'adherent';
496 }
497 if ($features == 'subscription') {
498 $features = 'adherent';
499 $feature2 = 'cotisation';
500 $tableandshare = 'subscription&adherent';
501 $parentfortableentity = 'fk_adherent@adherent'; // A subscription has no entity, the entity is the one of its member
502 }
503 if ($features == 'website' && is_object($object) && $object->element == 'websitepage') {
504 $parentfortableentity = 'fk_website@website';
505 }
506 if ($features == 'project') {
507 $features = 'projet';
508 }
509 if ($features == 'product') {
510 $features = 'produit';
511 }
512 if ($features == 'productbatch') {
513 $features = 'produit';
514 }
515 if ($features == 'tax') {
516 $feature2 = 'charges';
517 }
518 if ($features == 'workstation') {
519 $feature2 = 'workstation';
520 }
521 if ($features == 'fournisseur') { // When vendor invoice and purchase order are into module 'fournisseur'
522 $features = 'fournisseur';
523 if (is_object($object) && $object->element == 'invoice_supplier') {
524 $feature2 = 'facture';
525 } elseif (is_object($object) && $object->element == 'order_supplier') {
526 $feature2 = 'commande';
527 }
528 }
529 if ($features == 'payment_sc') {
530 $tableandshare = 'paiementcharge';
531 $parentfortableentity = 'fk_charge@chargesociales';
532 }
533
534 // if commonObjectLine : Using many2one related commonObject
535 // @see commonObjectLine::parentElement
536 if (in_array($features, ['commandedet', 'propaldet', 'facturedet', 'supplier_proposaldet', 'evaluationdet', 'skilldet', 'deliverydet', 'contratdet'])) {
537 $features = substr($features, 0, -3);
538 } elseif (in_array($features, ['stocktransferline', 'inventoryline', 'bomline', 'expensereport_det', 'facture_fourn_det'])) {
539 $features = substr($features, 0, -4);
540 } elseif ($features == 'commandefournisseurdispatch') {
541 $features = 'commandefournisseur';
542 } elseif ($features == 'invoice_supplier_det_rec') {
543 $features = 'invoice_supplier_rec';
544 }
545 if ($features == 'evaluation') {
546 $features = 'hrm';
547 $feature2 = 'evaluation';
548 }
549
550 // @todo check : project_task
551 // @todo possible ?
552 // elseif (substr($features, -3, 3) == 'det') {
553 // $features = substr($features, 0, -3);
554 // } elseif (substr($features, -4, 4) == '_det' || substr($features, -4, 4) == 'line') {
555 // $features = substr($features, 0, -4);
556 // }
557
558 // print $features.' - '.$tableandshare.' - '.$feature2.' - '.$dbt_select."\n";
559
560 // Get more permissions checks from hooks
561 $parameters = array(
562 'features' => $features,
563 'feature2' => $feature2,
564 'originalfeatures' => $originalfeatures,
565 'tableandshare' => $tableandshare,
566 'object' => $object,
567 'objectid' => $objectid,
568 'dbt_keyfield' => $dbt_keyfield,
569 'dbt_select' => $dbt_select,
570 'idtype' => $dbt_select,
571 'isdraft' => $isdraft,
572 'mode' => $mode,
573 );
574 if (!empty($hookmanager)) {
575 $reshook = $hookmanager->executeHooks('restrictedArea', $parameters);
576
577 if (isset($hookmanager->resArray['result'])) {
578 if ($hookmanager->resArray['result'] == 0) {
579 if ($mode) {
580 return 0;
581 } else {
582 accessforbidden(); // Module returns 0, so access forbidden
583 }
584 }
585 }
586 if ($reshook > 0) { // No other test done.
587 return 1;
588 }
589 }
590
591 // Features/modules to check (to support the & and | operator)
592 $featuresarray = array($features);
593 if (preg_match('/&/', $features)) {
594 $featuresarray = explode("&", $features);
595 } elseif (preg_match('/\|/', $features)) {
596 $featuresarray = explode("|", $features);
597 }
598
599 // More subfeatures to check
600 if (!empty($feature2)) {
601 $feature2 = explode("|", $feature2);
602 }
603
604 $listofmodules = explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL'));
605
606 // Check read permission from module
607 $readok = 1;
608 $nbko = 0;
609 foreach ($featuresarray as $feature) { // first we check nb of test ko
610 $featureforlistofmodule = $feature;
611 if ($featureforlistofmodule == 'produit') {
612 $featureforlistofmodule = 'product';
613 }
614 if ($featureforlistofmodule == 'supplier_proposal') {
615 $featureforlistofmodule = 'supplierproposal';
616 }
617 if (!empty($user->socid) && getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL') && !in_array($featureforlistofmodule, $listofmodules)) { // If limits on modules for external users, module must be into list of modules for external users
618 $readok = 0;
619 $nbko++;
620 continue;
621 }
622
623 if ($feature == 'societe' && (empty($feature2) || !in_array('contact', $feature2))) {
624 if (!$user->hasRight('societe', 'lire') && !$user->hasRight('fournisseur', 'lire')) {
625 $readok = 0;
626 $nbko++;
627 }
628 } elseif (($feature == 'societe' && (!empty($feature2) && in_array('contact', $feature2))) || $feature == 'contact') {
629 if (!$user->hasRight('societe', 'contact', 'lire')) {
630 $readok = 0;
631 $nbko++;
632 }
633 } elseif ($feature == 'produit|service') {
634 if (!$user->hasRight('produit', 'lire') && !$user->hasRight('service', 'lire')) {
635 $readok = 0;
636 $nbko++;
637 }
638 } elseif ($feature == 'prelevement') {
639 if (!$user->hasRight('prelevement', 'bons', 'lire')) {
640 $readok = 0;
641 $nbko++;
642 }
643 } elseif ($feature == 'cheque') {
644 if (!$user->hasRight('banque', 'cheque')) {
645 $readok = 0;
646 $nbko++;
647 }
648 } elseif ($feature == 'projet') {
649 if (!$user->hasRight('projet', 'lire') && !$user->hasRight('projet', 'all', 'lire')) {
650 $readok = 0;
651 $nbko++;
652 }
653 } elseif ($feature == 'payment') {
654 if (!$user->hasRight('facture', 'lire')) {
655 $readok = 0;
656 $nbko++;
657 }
658 } elseif ($feature == 'payment_supplier') {
659 if (!$user->hasRight('fournisseur', 'facture', 'lire')) {
660 $readok = 0;
661 $nbko++;
662 }
663 } elseif ($feature == 'payment_sc') {
664 if (!$user->hasRight('tax', 'charges', 'lire')) {
665 $readok = 0;
666 $nbko++;
667 }
668 } elseif ($feature == 'webhook') {
669 if (empty($user->admin)) {
670 $readok = 0;
671 $nbko++;
672 }
673 } elseif (!empty($feature2)) { // This is for permissions on 2 levels (module->object->read)
674 $tmpreadok = 1;
675 foreach ($feature2 as $subfeature) {
676 if ($subfeature == 'user' && $user->id == $objectid) {
677 continue; // A user can always read its own card
678 }
679 if ($subfeature == 'fiscalyear' && $user->hasRight('accounting', 'fiscalyear', 'write')) {
680 // only one right for fiscalyear
681 $tmpreadok = 1;
682 continue;
683 }
684 if (!empty($subfeature) && !$user->hasRight($feature, $subfeature, 'lire') && !$user->hasRight($feature, $subfeature, 'read')) {
685 $tmpreadok = 0;
686 } elseif (empty($subfeature) && !$user->hasRight($feature, 'lire') && !$user->hasRight($feature, 'read')) {
687 $tmpreadok = 0;
688 } else {
689 $tmpreadok = 1;
690 break;
691 } // Break is to bypass second test if the first is ok
692 }
693 if (!$tmpreadok) { // We found a test on feature that is ko
694 $readok = 0; // All tests are ko (we manage here the and, the or will be managed later using $nbko).
695 $nbko++;
696 }
697 } elseif (!empty($feature) && ($feature != 'user' && $feature != 'usergroup')) { // This is permissions on 1 level (module->read)
698 if (!$user->hasRight($feature, 'lire')
699 && !$user->hasRight($feature, 'read')
700 && !$user->hasRight($feature, 'run')) {
701 $readok = 0;
702 $nbko++;
703 }
704 }
705 }
706
707 // If a or and at least one ok
708 if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) {
709 $readok = 1;
710 }
711
712 if (!$readok) {
713 if ($mode) {
714 return 0;
715 } else {
717 }
718 }
719 //print "Read access is ok";
720
721 // Check write permission from module (we need to know write permission to create but also to delete drafts record or to upload files)
722 $createok = 1;
723 $nbko = 0;
724 $wemustcheckpermissionforcreate = (GETPOST('sendit', 'alpha') || GETPOST('linkit', 'alpha') || in_array(GETPOST('action', 'aZ09'), array('create', 'update', 'set', 'upload', 'add_element_resource', 'confirm_deletebank', 'confirm_delete_linked_resource')) || GETPOST('roworder', 'alpha', 2));
725 $wemustcheckpermissionfordeletedraft = ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete');
726
727 if ($wemustcheckpermissionforcreate || $wemustcheckpermissionfordeletedraft) {
728 foreach ($featuresarray as $feature) {
729 if ($feature == 'contact') {
730 if (!$user->hasRight('societe', 'contact', 'creer')) {
731 $createok = 0;
732 $nbko++;
733 }
734 } elseif ($feature == 'produit|service') {
735 if (!$user->hasRight('produit', 'creer') && !$user->hasRight('service', 'creer')) {
736 $createok = 0;
737 $nbko++;
738 }
739 } elseif ($feature == 'prelevement') {
740 if (!$user->hasRight('prelevement', 'bons', 'creer')) {
741 $createok = 0;
742 $nbko++;
743 }
744 } elseif ($feature == 'commande_fournisseur') {
745 if (!$user->hasRight('fournisseur', 'commande', 'creer') || !$user->hasRight('supplier_order', 'creer')) {
746 $createok = 0;
747 $nbko++;
748 }
749 } elseif ($feature == 'banque') {
750 if (!$user->hasRight('banque', 'modifier')) {
751 $createok = 0;
752 $nbko++;
753 }
754 } elseif ($feature == 'cheque') {
755 if (!$user->hasRight('banque', 'cheque')) {
756 $createok = 0;
757 $nbko++;
758 }
759 } elseif ($feature == 'import') {
760 if (!$user->hasRight('import', 'run')) {
761 $createok = 0;
762 $nbko++;
763 }
764 } elseif ($feature == 'ecm') {
765 if (!$user->hasRight('ecm', 'upload')) {
766 $createok = 0;
767 $nbko++;
768 }
769 } elseif ($feature == 'modulebuilder') {
770 if (!$user->hasRight('modulebuilder', 'run')) {
771 $createok = 0;
772 $nbko++;
773 }
774 } elseif ($feature == 'webhook') {
775 if (empty($user->admin)) {
776 $createok = 0;
777 $nbko++;
778 }
779 } elseif (!empty($feature2)) { // This is for permissions on 2 levels (module->object->write)
780 foreach ($feature2 as $subfeature) {
781 if ($subfeature == 'user' && $user->id == $objectid && $user->hasRight('user', 'self', 'creer')) {
782 continue; // User can edit its own card
783 }
784 if ($subfeature == 'user' && $user->id == $objectid && $user->hasRight('user', 'self', 'password')) {
785 continue; // User can edit its own password
786 }
787 if ($subfeature == 'user' && $user->id != $objectid && $user->hasRight('user', 'user', 'password')) {
788 continue; // User can edit another user's password
789 }
790
791 if (!$user->hasRight($feature, $subfeature, 'creer')
792 && !$user->hasRight($feature, $subfeature, 'write')
793 && !$user->hasRight($feature, $subfeature, 'create')) {
794 $createok = 0;
795 $nbko++;
796 } else {
797 $createok = 1;
798 // Break to bypass second test if the first is ok
799 break;
800 }
801 }
802 } elseif (!empty($feature)) { // This is for permissions on 1 levels (module->write)
803 //print '<br>feature='.$feature.' creer='.$user->rights->$feature->creer.' write='.$user->rights->$feature->write; exit;
804 if (!$user->hasRight($feature, 'creer')
805 && !$user->hasRight($feature, 'write')
806 && !$user->hasRight($feature, 'create')) {
807 $createok = 0;
808 $nbko++;
809 }
810 }
811 }
812
813 // If a or and at least one ok
814 if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) {
815 $createok = 1;
816 }
817
818 if ($wemustcheckpermissionforcreate && !$createok) {
819 if ($mode) {
820 return 0;
821 } else {
823 }
824 }
825 //print "Write access is ok";
826 }
827
828 // Check create user permission
829 $createuserok = 1;
830 if (GETPOST('action', 'aZ09') == 'confirm_create_user' && GETPOST("confirm", 'aZ09') == 'yes') {
831 if (!$user->hasRight('user', 'user', 'creer')) {
832 $createuserok = 0;
833 }
834
835 if (!$createuserok) {
836 if ($mode) {
837 return 0;
838 } else {
840 }
841 }
842 //print "Create user access is ok";
843 }
844
845 // Check delete permission from module
846 $deleteok = 1;
847 $nbko = 0;
848 if ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete') {
849 foreach ($featuresarray as $feature) {
850 if ($feature == 'bookmark') {
851 if (!$user->hasRight('bookmark', 'supprimer')) {
852 if ($user->id != $object->fk_user || !$user->hasRight('bookmark', 'creer')) {
853 $deleteok = 0;
854 }
855 }
856 } elseif ($feature == 'contact') {
857 if (!$user->hasRight('societe', 'contact', 'supprimer')) {
858 $deleteok = 0;
859 }
860 } elseif ($feature == 'produit|service') {
861 if (!$user->hasRight('produit', 'supprimer') && !$user->hasRight('service', 'supprimer')) {
862 $deleteok = 0;
863 }
864 } elseif ($feature == 'commande_fournisseur') {
865 if (!$user->hasRight('fournisseur', 'commande', 'supprimer')) {
866 $deleteok = 0;
867 }
868 } elseif ($feature == 'payment_supplier') { // Permission to delete a payment of an invoice is permission to edit an invoice.
869 if (!$user->hasRight('fournisseur', 'facture', 'creer')) {
870 $deleteok = 0;
871 }
872 } elseif ($feature == 'payment') {
873 if (!$user->hasRight('facture', 'paiement')) {
874 $deleteok = 0;
875 }
876 } elseif ($feature == 'payment_sc') {
877 if (!$user->hasRight('tax', 'charges', 'creer')) {
878 $deleteok = 0;
879 }
880 } elseif ($feature == 'banque') {
881 if (!$user->hasRight('banque', 'modifier')) {
882 $deleteok = 0;
883 }
884 } elseif ($feature == 'cheque') {
885 if (!$user->hasRight('banque', 'cheque')) {
886 $deleteok = 0;
887 }
888 } elseif ($feature == 'ecm') {
889 if (!$user->hasRight('ecm', 'upload')) {
890 $deleteok = 0;
891 }
892 } elseif ($feature == 'ftp') {
893 if (!$user->hasRight('ftp', 'write')) {
894 $deleteok = 0;
895 }
896 } elseif ($feature == 'salaries') {
897 if (!$user->hasRight('salaries', 'delete')) {
898 $deleteok = 0;
899 }
900 } elseif ($feature == 'adherent') {
901 if (!$user->hasRight('adherent', 'supprimer')) {
902 $deleteok = 0;
903 }
904 } elseif ($feature == 'paymentbybanktransfer') {
905 if (!$user->hasRight('paymentbybanktransfer', 'create')) { // There is no delete permission
906 $deleteok = 0;
907 }
908 } elseif ($feature == 'prelevement') {
909 if (!$user->hasRight('prelevement', 'bons', 'creer')) { // There is no delete permission
910 $deleteok = 0;
911 }
912 } elseif (!empty($feature2)) { // This is for permissions on 2 levels
913 foreach ($feature2 as $subfeature) {
914 if (!$user->hasRight($feature, $subfeature, 'supprimer') && !$user->hasRight($feature, $subfeature, 'delete')) {
915 $deleteok = 0;
916 } else {
917 $deleteok = 1;
918 break;
919 } // For bypass the second test if the first is ok
920 }
921 } elseif (!empty($feature)) { // This is used for permissions on 1 level
922 //print '<br>feature='.$feature.' creer='.$user->rights->$feature->supprimer.' write='.$user->rights->$feature->delete;
923 if (!$user->hasRight($feature, 'supprimer')
924 && !$user->hasRight($feature, 'delete')
925 && !$user->hasRight($feature, 'run')) {
926 $deleteok = 0;
927 }
928 }
929 }
930
931 // If a or and at least one ok
932 if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) {
933 $deleteok = 1;
934 }
935
936 if (!$deleteok && !($isdraft && $createok)) {
937 if ($mode) {
938 return 0;
939 } else {
941 }
942 }
943 //print "Delete access is ok";
944 }
945
946 // If we have a particular object to check permissions on, we check if $user has permission
947 // for this given object (link to company, is contact for project, ...)
948 if (!empty($objectid) && $objectid > 0) {
949 $ok = checkUserAccessToObject($user, $featuresarray, $object, $tableandshare, $feature2, $dbt_keyfield, $dbt_select, $parentfortableentity);
950 $params = array('objectid' => $objectid, 'features' => implode(',', $featuresarray), 'features2' => $feature2);
951 //print 'checkUserAccessToObject ok='.$ok;
952 if ($mode) {
953 return $ok ? 1 : 0;
954 } else {
955 if ($ok) {
956 return 1;
957 } else {
958 accessforbidden('', 1, 1, 0, $params);
959 }
960 }
961 }
962
963 return 1;
964}
965
981function checkUserAccessToObject($user, array $featuresarray, $object = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = '', $dbt_select = 'rowid', $parenttableforentity = '')
982{
983 global $db, $conf;
984
985 if (is_object($object)) {
986 $objectid = $object->id;
987 } else {
988 $objectid = $object; // $objectid can be X or 'X,Y,Z'
989 }
990 $objectid = preg_replace('/[^0-9\.\,]/', '', $objectid); // For the case value is coming from a non sanitized user input
991
992 //dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename, $feature2, $dbt_socfield, $dbt_select, $isdraft");
993 //print "user_id=".$user->id.", features=".join(',', $featuresarray).", objectid=".$objectid;
994 //print ", tableandshare=".$tableandshare.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select."<br>";
995
996 // More parameters
997 $params = explode('&', $tableandshare);
998 $dbtablename = (!empty($params[0]) ? $params[0] : '');
999 $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename);
1000
1001 foreach ($featuresarray as $feature) {
1002 $sql = '';
1003
1004 //var_dump($feature);exit;
1005
1006 // For backward compatibility
1007 if ($feature == 'societe' && !empty($feature2) && is_array($feature2) && in_array('contact', $feature2)) {
1008 $feature = 'contact';
1009 $feature2 = '';
1010 }
1011 if ($feature == 'member') {
1012 $feature = 'adherent';
1013 }
1014 if ($feature == 'category') {
1015 $feature = 'categorie';
1016 }
1017 if ($feature == 'project') {
1018 $feature = 'projet';
1019 }
1020 if ($feature == 'projet' && !empty($feature2) && is_array($feature2) && !empty(array_intersect(array('project_task', 'projet_task'), $feature2))) {
1021 $feature = 'project_task';
1022 }
1023 if ($feature == 'task' || $feature == 'projet_task') {
1024 $feature = 'project_task';
1025 $dbtablename = 'projet_task';
1026 }
1027 if ($feature == 'eventorganization') {
1028 $feature = 'agenda';
1029 $dbtablename = 'actioncomm';
1030 }
1031 if ($feature == 'payment_sc' && empty($parenttableforentity)) {
1032 // If we check perm on payment page but $parenttableforentity not defined, we force value on parent table
1033 $parenttableforentity = '';
1034 $dbtablename = "chargesociales";
1035 $feature = "chargesociales";
1036 $objectid = $object->fk_charge;
1037 }
1038
1039 $checkonentitydone = 0;
1040
1041 // Array to define rules of checks to do
1042 $check = array('adherent', 'banque', 'bom', 'don', 'mrp', 'user', 'usergroup', 'payment', 'payment_supplier', 'payment_sc', 'product', 'produit', 'service', 'produit|service', 'categorie', 'resource', 'expensereport', 'holiday', 'salaries', 'website', 'recruitment', 'chargesociales', 'knowledgemanagement', 'stock'); // Test on entity only (Objects with no link to company)
1043 $checksoc = array('societe'); // Test for object Societe
1044 $checkparentsoc = array('agenda', 'contact', 'contrat'); // Test on entity + link to third party on field $dbt_keyfield. Allowed if link is empty (Ex: contacts...).
1045 $checkproject = array('projet', 'project'); // Test for project object
1046 $checktask = array('projet_task', 'project_task'); // Test for task object
1047 $checkhierarchy = array('expensereport', 'holiday', 'hrm'); // check permission among the hierarchy of user
1048 $checkuser = array('bookmark'); // check permission among the fk_user (must be myself or null)
1049 $nocheck = array('barcode', 'webhook'); // No test
1050
1051 //$checkdefault = 'all other not already defined'; // Test on entity + link to third party on field $dbt_keyfield. Not allowed if link is empty (Ex: invoice, orders...).
1052
1053 // If dbtablename not defined, we use same name for table than module name
1054 if (empty($dbtablename)) {
1055 $dbtablename = $feature;
1056 $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename); // We change dbtablename, so we set sharedelement too.
1057 }
1058
1059 // To avoid an access forbidden with a numeric ref
1060 if ($dbt_select != 'rowid' && $dbt_select != 'id') {
1061 $objectid = "'".$objectid."'"; // Note: $objectid was already cast into int at begin of this method.
1062 }
1063 // Check permission for objectid on entity only
1064 if (in_array($feature, $check) && $objectid > 0) { // For $objectid = 0, no check
1065 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1066 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1067 if (($feature == 'user' || $feature == 'usergroup') && isModEnabled('multicompany')) { // Special for multicompany
1068 if (getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1069 if ($conf->entity == 1 && $user->admin && !$user->entity) {
1070 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1071 $sql .= " AND dbt.entity IS NOT NULL";
1072 } else {
1073 $sql .= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1074 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1075 $sql .= " AND ((ug.fk_user = dbt.rowid";
1076 $sql .= " AND ug.entity IN (".getEntity('usergroup')."))";
1077 $sql .= " OR dbt.entity = 0)"; // Show always superadmin
1078 }
1079 } else {
1080 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1081 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1082 }
1083 } else {
1084 $reg = array();
1085 if ($parenttableforentity && preg_match('/(.*)@(.*)/', $parenttableforentity, $reg)) {
1086 $sql .= ", ".MAIN_DB_PREFIX.$reg[2]." as dbtp";
1087 $sql .= " WHERE dbt.".$reg[1]." = dbtp.rowid AND dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1088 $sql .= " AND dbtp.entity IN (".getEntity($sharedelement, 1).")";
1089 } else {
1090 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1091 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1092 }
1093 }
1094 $checkonentitydone = 1;
1095 }
1096 if (in_array($feature, $checksoc) && $objectid > 0) { // We check feature = checksoc. For $objectid = 0, no check
1097 // If external user: Check permission for external users
1098 if ($user->socid > 0) {
1099 if ($user->socid != $objectid) {
1100 return false;
1101 }
1102 } elseif (isModEnabled('societe') && !$user->hasRight('societe', 'lire') && !$user->hasRight('societe', 'client', 'voir')) {
1103 dol_syslog("security.lib.php::checkUserAccessToObject Deny access due: (isModEnabled('societe') && !user->hasRight('societe', 'lire') && !user->hasRight('societe', 'client', 'voir'))", LOG_DEBUG);
1104 return false;
1105 } elseif (isModEnabled("societe") && ($user->hasRight('societe', 'lire') && !$user->hasRight('societe', 'client', 'voir'))) {
1106 // If internal user: Check permission for internal users that are restricted on their objects
1107 $sql = "SELECT COUNT(sc.fk_soc) as nb";
1108 $sql .= " FROM (".MAIN_DB_PREFIX."societe_commerciaux as sc";
1109 $sql .= ", ".MAIN_DB_PREFIX."societe as s)";
1110 $sql .= " WHERE sc.fk_soc IN (".$db->sanitize($objectid, 1).")";
1111 $sql .= " AND (sc.fk_user = ".((int) $user->id);
1112 if (getDolGlobalInt('MAIN_SEE_SUBORDINATES')) {
1113 $userschilds = $user->getAllChildIds();
1114 if (!empty($userschilds)) $sql .= " OR sc.fk_user IN (".$db->sanitize(implode(',', $userschilds)).")";
1115 }
1116 $sql .= ")";
1117 $sql .= " AND sc.fk_soc = s.rowid";
1118 $sql .= " AND s.entity IN (".getEntity($sharedelement, 1).")";
1119 } elseif (isModEnabled('multicompany')) {
1120 // If multicompany and internal users with all permissions, check user is in correct entity
1121 $sql = "SELECT COUNT(s.rowid) as nb";
1122 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
1123 $sql .= " WHERE s.rowid IN (".$db->sanitize($objectid, 1).")";
1124 $sql .= " AND s.entity IN (".getEntity($sharedelement, 1).")";
1125 }
1126
1127 $checkonentitydone = 1;
1128 }
1129 if (in_array($feature, $checkparentsoc) && $objectid > 0) { // Test on entity + link to thirdparty. Allowed if link is empty (Ex: contacts...).
1130 // If external user: Check permission for external users
1131 if ($user->socid > 0) {
1132 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1133 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1134 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1135 $sql .= " AND dbt.fk_soc = ".((int) $user->socid);
1136 } elseif (isModEnabled("societe") && ($user->hasRight('societe', 'lire') && !$user->hasRight('societe', 'client', 'voir'))) {
1137 // If internal user: Check permission for internal users that are restricted on their objects
1138 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1139 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1140 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON dbt.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1141 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1142 $sql .= " AND (dbt.fk_soc IS NULL OR sc.fk_soc IS NOT NULL)"; // Contact not linked to a company or to a company of user
1143 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1144 } elseif (isModEnabled('multicompany')) {
1145 // If multicompany and internal users with all permissions, check user is in correct entity
1146 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1147 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1148 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1149 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1150 }
1151
1152 $checkonentitydone = 1;
1153 }
1154 if (in_array($feature, $checkproject) && $objectid > 0) {
1155 if (isModEnabled('project') && !$user->hasRight('projet', 'all', 'lire')) {
1156 $projectid = $objectid;
1157
1158 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1159 $projectstatic = new Project($db);
1160 $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0);
1161
1162 $tmparray = explode(',', $tmps);
1163 if (!in_array($projectid, $tmparray)) {
1164 return false;
1165 }
1166 } else {
1167 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1168 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1169 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1170 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1171 }
1172 $checkonentitydone = 1;
1173 }
1174 if (in_array($feature, $checktask) && (int) $objectid > 0) {
1175 if (isModEnabled('project') && !$user->hasRight('projet', 'all', 'lire')) {
1176 $task = new Task($db);
1177 $task->fetch((int) $objectid);
1178 $projectid = $task->fk_project;
1179
1180 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1181 $projectstatic = new Project($db);
1182 $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0);
1183
1184 $tmparray = explode(',', $tmps);
1185 if (!in_array($projectid, $tmparray)) {
1186 return false;
1187 }
1188 } else {
1189 $sharedelement = 'project'; // for multicompany compatibility
1190 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1191 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1192 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1193 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1194 }
1195
1196 $checkonentitydone = 1;
1197 }
1198 //var_dump($sql);
1199
1200 if (!$checkonentitydone && !in_array($feature, $nocheck) && $objectid > 0) { // By default (case of $checkdefault), we check on object entity + link to third party on field $dbt_keyfield
1201 // If external user: Check permission for external users
1202 if ($user->socid > 0) {
1203 if (empty($dbt_keyfield)) {
1204 dol_print_error(null, 'Param dbt_keyfield is required but not defined');
1205 }
1206 $sql = "SELECT COUNT(dbt.".$dbt_keyfield.") as nb";
1207 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1208 $sql .= " WHERE dbt.rowid IN (".$db->sanitize($objectid, 1).")";
1209 $sql .= " AND dbt.".$dbt_keyfield." = ".((int) $user->socid);
1210 } elseif (isModEnabled("societe") && !$user->hasRight('societe', 'client', 'voir')) {
1211 // If internal user without permission to see all thirdparties: Check permission for internal users that are restricted on their objects
1212 if (empty($dbt_keyfield)) {
1213 dol_print_error(null, 'Param dbt_keyfield is required but not defined');
1214 }
1215 if ($feature != 'ticket') {
1216 $sql = "SELECT COUNT(sc.fk_soc) as nb";
1217 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1218 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1219 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1220 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1221 $sql .= " AND sc.fk_soc = dbt.".$dbt_keyfield;
1222 $sql .= " AND (sc.fk_user = ".((int) $user->id);
1223 if (getDolGlobalInt('MAIN_SEE_SUBORDINATES')) {
1224 $userschilds = $user->getAllChildIds();
1225 if (!empty($userschilds)) $sql .= " OR sc.fk_user IN (".$db->sanitize(implode(',', $userschilds)).")";
1226 }
1227 $sql .= ')';
1228 } else {
1229 // On ticket, the thirdparty is not mandatory, so we need a special test to accept record with no thirdparties.
1230 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1231 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1232 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = dbt.".$dbt_keyfield." AND sc.fk_user = ".((int) $user->id);
1233 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1234 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1235 $sql .= " AND (sc.fk_user = ".((int) $user->id)." OR dbt.".$dbt_keyfield." IS NULL OR dbt.".$dbt_keyfield." = 0)";
1236 }
1237 } elseif (isModEnabled('multicompany') && (!empty($object->ismultientitymanaged) || !isset($object->ismultientitymanaged))) {
1238 // If multicompany, and user is an internal user with all permissions, check that object is in correct entity
1239 $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
1240 $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
1241 $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
1242 $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
1243 }
1244 }
1245
1246 // For events, check on users assigned to event
1247 if ($feature === 'agenda' && ((int) $objectid) > 0) {
1248 // Also check owner or attendee for users without allactions->read
1249 if (/* $objectid > 0 && */ !$user->hasRight('agenda', 'allactions', 'read')) {
1250 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1251 $action = new ActionComm($db);
1252 $action->fetch((int) $objectid);
1253 if ($action->authorid != $user->id && $action->userownerid != $user->id && !(array_key_exists($user->id, $action->userassigned))) {
1254 return false;
1255 }
1256 }
1257 }
1258
1259 // For some object, we also have to check it is in the user hierarchy
1260 // Param $object must be the full object and not a simple id to have this test possible.
1261 if (in_array($feature, $checkhierarchy) && is_object($object) && $objectid > 0) {
1262 $childids = $user->getAllChildIds(1);
1263 $useridtocheck = 0;
1264 if ($feature == 'holiday') {
1265 $useridtocheck = $object->fk_user;
1266 if (!$user->hasRight('holiday', 'readall') && !in_array($useridtocheck, $childids) && !in_array($object->fk_validator, $childids)) {
1267 return false;
1268 }
1269 }
1270 if ($feature == 'expensereport') {
1271 $useridtocheck = $object->fk_user_author;
1272 if (!$user->hasRight('expensereport', 'readall')) {
1273 if (!in_array($useridtocheck, $childids)) {
1274 return false;
1275 }
1276 }
1277 }
1278 if ($feature == 'hrm' && in_array('evaluation', $feature2)) {
1279 $useridtocheck = $object->fk_user;
1280
1281 if ($user->hasRight('hrm', 'evaluation', 'readall')) {
1282 // the user can view evaluations for anyone
1283 return true;
1284 }
1285 if (!$user->hasRight('hrm', 'evaluation', 'read')) {
1286 // the user can't view any evaluations
1287 return false;
1288 }
1289 // the user can only see their own evaluations or their subordinates'
1290 return in_array($useridtocheck, $childids);
1291 }
1292 }
1293
1294 // For some object, we also have to check it is public or owned by user
1295 // Param $object must be the full object and not a simple id to have this test possible.
1296 if (in_array($feature, $checkuser) && is_object($object) && $objectid > 0) {
1297 $useridtocheck = $object->fk_user;
1298 if (!empty($useridtocheck) && $useridtocheck > 0 && $useridtocheck != $user->id && empty($user->admin)) {
1299 return false;
1300 }
1301 }
1302
1303 // A private contact (field priv) can only be accessed by the user that created it
1304 if ($feature == 'contact' && in_array($dbtablename, array('socpeople', 'contact')) && !empty($objectid)) {
1305 $sqlpriv = "SELECT COUNT(dbt.rowid) as nb";
1306 $sqlpriv .= " FROM ".MAIN_DB_PREFIX."socpeople as dbt";
1307 $sqlpriv .= " WHERE dbt.rowid IN (".$db->sanitize($objectid, 1).")";
1308 $sqlpriv .= " AND dbt.priv = 1 AND (dbt.fk_user_creat IS NULL OR dbt.fk_user_creat <> ".((int) $user->id).")";
1309 $resqlpriv = $db->query($sqlpriv);
1310 if (!$resqlpriv) {
1311 return false;
1312 }
1313 $objpriv = $db->fetch_object($resqlpriv);
1314 if ($objpriv && $objpriv->nb > 0) {
1315 return false;
1316 }
1317 }
1318
1319 if ($sql) {
1320 $resql = $db->query($sql);
1321 if ($resql) {
1322 $obj = $db->fetch_object($resql);
1323 if (!$obj || $obj->nb < count(explode(',', $objectid))) { // error if we found 0 or less record than nb of id provided
1324 return false;
1325 }
1326 } else {
1327 dol_syslog("Bad forged sql in security.lib.php::checkUserAccessToObject", LOG_WARNING);
1328 return false;
1329 }
1330 }
1331 }
1332
1333 dol_syslog("security.lib.php::checkUserAccessToObject::return True", LOG_DEBUG);
1334 return true;
1335}
1336
1337
1349function httponly_accessforbidden($message = '1', $http_response_code = 403, $stringalreadysanitized = 0)
1350{
1351 top_httphead();
1352 http_response_code($http_response_code);
1353
1354 if ($stringalreadysanitized) {
1355 print $message;
1356 } else {
1357 print htmlentities($message);
1358 }
1359
1360 exit(1);
1361}
1362
1376function accessforbidden($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
1377{
1378 global $conf, $db, $user, $langs, $hookmanager;
1379 global $action, $object;
1380
1381 if (!is_object($langs)) {
1382 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
1383 $langs = new Translate('', $conf);
1384 $langs->setDefaultLang();
1385 }
1386
1387 $langs->loadLangs(array("main", "errors"));
1388
1389 if ($printheader && !defined('NOHEADERNOFOOTER')) {
1390 if (function_exists("llxHeader")) {
1391 llxHeader('');
1392 } elseif (function_exists("llxHeaderVierge")) {
1393 llxHeaderVierge('');
1394 }
1395 print '<div style="padding: 20px">';
1396 }
1397 print '<div class="error">';
1398 if (empty($message)) {
1399 print $langs->trans("ErrorForbidden");
1400 } else {
1401 print $langs->trans($message);
1402 }
1403 print '</div>';
1404 print '<br>';
1405 if (empty($showonlymessage)) {
1406 if (empty($hookmanager)) {
1407 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1408 $hookmanager = new HookManager($db);
1409 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
1410 $hookmanager->initHooks(array('main'));
1411 }
1412
1413 $parameters = array('message' => $message, 'params' => $params);
1414 $reshook = $hookmanager->executeHooks('getAccessForbiddenMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1415 print $hookmanager->resPrint;
1416 if (empty($reshook)) {
1417 $langs->loadLangs(array("errors"));
1418 if ($user->login) {
1419 print $langs->trans("CurrentLogin").': <span class="error">'.$user->login.'</span><br>';
1420 print $langs->trans("ErrorForbidden2", $langs->transnoentitiesnoconv("Home"), $langs->transnoentitiesnoconv("Users"));
1421 print $langs->trans("ErrorForbidden4");
1422 } else {
1423 print $langs->trans("ErrorForbidden3");
1424 }
1425 }
1426 }
1427 if ($printfooter && !defined('NOHEADERNOFOOTER') && function_exists("llxFooter")) {
1428 print '</div>';
1429 llxFooter();
1430 }
1431
1432 exit(0);
1433}
1434
1435
1443{
1444 $max = getDolGlobalString('MAIN_UPLOAD_DOC'); // In Kb
1445
1446 $maxphp = @ini_get('upload_max_filesize'); // In unknown
1447 if (preg_match('/k$/i', $maxphp)) {
1448 $maxphp = preg_replace('/k$/i', '', $maxphp);
1449 $maxphp = (int) ((float) $maxphp * 1);
1450 }
1451 if (preg_match('/m$/i', $maxphp)) {
1452 $maxphp = preg_replace('/m$/i', '', $maxphp);
1453 $maxphp = (int) ((float) $maxphp * 1024);
1454 }
1455 if (preg_match('/g$/i', $maxphp)) {
1456 $maxphp = preg_replace('/g$/i', '', $maxphp);
1457 $maxphp = (int) ((float) $maxphp * 1024 * 1024);
1458 }
1459 if (preg_match('/t$/i', $maxphp)) {
1460 $maxphp = preg_replace('/t$/i', '', $maxphp);
1461 $maxphp = (int) ((float) $maxphp * 1024 * 1024 * 1024);
1462 }
1463 $maxphp2 = @ini_get('post_max_size'); // In unknown
1464 if (preg_match('/k$/i', $maxphp2)) {
1465 $maxphp2 = preg_replace('/k$/i', '', $maxphp2);
1466 $maxphp2 = (int) ((float) $maxphp2) * 1;
1467 }
1468 if (preg_match('/m$/i', $maxphp2)) {
1469 $maxphp2 = preg_replace('/m$/i', '', $maxphp2);
1470 $maxphp2 = (int) ((float) $maxphp2 * 1024);
1471 }
1472 if (preg_match('/g$/i', $maxphp2)) {
1473 $maxphp2 = preg_replace('/g$/i', '', $maxphp2);
1474 $maxphp2 = (int) ((float) $maxphp2 * 1024 * 1024);
1475 }
1476 if (preg_match('/t$/i', $maxphp2)) {
1477 $maxphp2 = preg_replace('/t$/i', '', $maxphp2);
1478 $maxphp2 = (int) ((float) $maxphp2 * 1024 * 1024 * 1024);
1479 }
1480 // Now $max and $maxphp and $maxphp2 are in Kb
1481 $maxmin = $max;
1482 $maxphptoshow = $maxphptoshowparam = '';
1483 if ($maxphp > 0) {
1484 $maxmin = min($maxmin, $maxphp);
1485 $maxphptoshow = $maxphp;
1486 $maxphptoshowparam = 'upload_max_filesize';
1487 }
1488 if ($maxphp2 > 0) {
1489 $maxmin = min($maxmin, $maxphp2);
1490 if ($maxphp2 < $maxphp) {
1491 $maxphptoshow = $maxphp2;
1492 $maxphptoshowparam = 'post_max_size';
1493 }
1494 }
1495 //var_dump($maxphp.'-'.$maxphp2);
1496 //var_dump($maxmin);
1497
1498 return array('max' => $max, 'maxmin' => $maxmin, 'maxphptoshow' => $maxphptoshow, 'maxphptoshowparam' => $maxphptoshowparam);
1499}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
if(!defined( 'NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined( 'NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) if(!defined( 'NOLOGIN')) if(!defined('NOCSRFCHECK')) if(!defined( 'NOIPCHECK')) llxHeaderVierge($title, $head="", $disablejs=0, $disablehead=0, $arrayofjs=[], $arrayofcss=[])
Header function.
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage agenda events (actions)
Class to manage hooks.
Class to manage projects.
Class to manage tasks.
Class to manage translations.
Class to manage Dolibarr users.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
ascii_check($str)
Check if a string is in ASCII.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.
httponly_accessforbidden($message='1', $http_response_code=403, $stringalreadysanitized=0)
Show a message to say access is forbidden and stop program.
dol_encode($chain, $key='1')
Encode a string with base 64 algorithm + specific delta change.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
checkUserAccessToObject($user, array $featuresarray, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='', $dbt_select='rowid', $parenttableforentity='')
Check that access by a given user to an object is ok.
dol_verifyHash($chain, $hash, $type='0')
Compute a hash and compare it to the given one For backward compatibility reasons,...
dolEncrypt($chain, $key='', $ciphering='', $forceseed='')
Encode a string with a symmetric encryption.
getMaxFileSizeArray()
Return the max allowed for file upload.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
dol_decode($chain, $key='1')
Decode a base 64 encoded + specific delta change.
dolGetLdapPasswordHash($password, $type='md5')
Returns a specific ldap hash of a password.
dolDecrypt($chain, $key='')
Decode a string with a symmetric encryption.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.