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