security.lib.php 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. <?php
  2. /* Copyright (C) 2008-2021 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2008-2021 Regis Houssin <regis.houssin@inodbox.com>
  4. * Copyright (C) 2020 Ferran Marcet <fmarcet@2byte.es>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. * or see https://www.gnu.org/
  19. */
  20. /**
  21. * \file htdocs/core/lib/security.lib.php
  22. * \ingroup core
  23. * \brief Set of function used for dolibarr security (common function included into filefunc.inc.php)
  24. * Warning, this file must not depends on other library files, except function.lib.php
  25. * because it is used at low code level.
  26. */
  27. /**
  28. * Encode a string with base 64 algorithm + specific delta change.
  29. *
  30. * @param string $chain string to encode
  31. * @param string $key rule to use for delta ('0', '1' or 'myownkey')
  32. * @return string encoded string
  33. * @see dol_decode()
  34. */
  35. function dol_encode($chain, $key = '1')
  36. {
  37. if (is_numeric($key) && $key == '1') { // rule 1 is offset of 17 for char
  38. $output_tab = array();
  39. $strlength = dol_strlen($chain);
  40. for ($i = 0; $i < $strlength; $i++) {
  41. $output_tab[$i] = chr(ord(substr($chain, $i, 1)) + 17);
  42. }
  43. $chain = implode("", $output_tab);
  44. } elseif ($key) {
  45. $result = '';
  46. $strlength = dol_strlen($chain);
  47. for ($i = 0; $i < $strlength; $i++) {
  48. $keychar = substr($key, ($i % strlen($key)) - 1, 1);
  49. $result .= chr(ord(substr($chain, $i, 1)) + (ord($keychar) - 65));
  50. }
  51. $chain = $result;
  52. }
  53. return base64_encode($chain);
  54. }
  55. /**
  56. * Decode a base 64 encoded + specific delta change.
  57. * This function is called by filefunc.inc.php at each page call.
  58. *
  59. * @param string $chain string to decode
  60. * @param string $key rule to use for delta ('0', '1' or 'myownkey')
  61. * @return string decoded string
  62. * @see dol_encode()
  63. */
  64. function dol_decode($chain, $key = '1')
  65. {
  66. $chain = base64_decode($chain);
  67. if (is_numeric($key) && $key == '1') { // rule 1 is offset of 17 for char
  68. $output_tab = array();
  69. $strlength = dol_strlen($chain);
  70. for ($i = 0; $i < $strlength; $i++) {
  71. $output_tab[$i] = chr(ord(substr($chain, $i, 1)) - 17);
  72. }
  73. $chain = implode("", $output_tab);
  74. } elseif ($key) {
  75. $result = '';
  76. $strlength = dol_strlen($chain);
  77. for ($i = 0; $i < $strlength; $i++) {
  78. $keychar = substr($key, ($i % strlen($key)) - 1, 1);
  79. $result .= chr(ord(substr($chain, $i, 1)) - (ord($keychar) - 65));
  80. }
  81. $chain = $result;
  82. }
  83. return $chain;
  84. }
  85. /**
  86. * Return a string of random bytes (hexa string) with length = $length fro cryptographic purposes.
  87. *
  88. * @param int $length Length of random string
  89. * @return string Random string
  90. */
  91. function dolGetRandomBytes($length)
  92. {
  93. if (function_exists('random_bytes')) { // Available with PHP 7 only.
  94. return bin2hex(random_bytes((int) floor($length / 2))); // the bin2hex will double the number of bytes so we take length / 2
  95. }
  96. 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.
  97. }
  98. /**
  99. * Encode a string with a symetric encryption. Used to encrypt sensitive data into database.
  100. * Note: If a backup is restored onto another instance with a different $conf->file->instance_unique_id, then decoded value will differ.
  101. * This function is called for example by dol_set_const() when saving a sensible data into database configuration table llx_const.
  102. *
  103. * @param string $chain string to encode
  104. * @param string $key If '', we use $conf->file->instance_unique_id
  105. * @param string $ciphering Default ciphering algorithm
  106. * @param string $forceseed To force the seed
  107. * @return string encoded string
  108. * @see dolDecrypt(), dol_hash()
  109. */
  110. function dolEncrypt($chain, $key = '', $ciphering = 'AES-256-CTR', $forceseed = '')
  111. {
  112. global $conf;
  113. global $dolibarr_disable_dolcrypt_for_debug;
  114. if ($chain === '' || is_null($chain)) {
  115. return '';
  116. }
  117. $reg = array();
  118. if (preg_match('/^dolcrypt:([^:]+):(.+)$/', $chain, $reg)) {
  119. // The $chain is already a crypted string
  120. return $chain;
  121. }
  122. if (empty($key)) {
  123. $key = $conf->file->instance_unique_id;
  124. }
  125. if (empty($ciphering)) {
  126. $ciphering = 'AES-256-CTR';
  127. }
  128. $newchain = $chain;
  129. if (function_exists('openssl_encrypt') && empty($dolibarr_disable_dolcrypt_for_debug)) {
  130. $ivlen = 16;
  131. if (function_exists('openssl_cipher_iv_length')) {
  132. $ivlen = openssl_cipher_iv_length($ciphering);
  133. }
  134. if ($ivlen === false || $ivlen < 1 || $ivlen > 32) {
  135. $ivlen = 16;
  136. }
  137. if (empty($forceseed)) {
  138. $ivseed = dolGetRandomBytes($ivlen);
  139. } else {
  140. $ivseed = dol_trunc(md5($forceseed), $ivlen, 'right', 'UTF-8', 1);
  141. }
  142. $newchain = openssl_encrypt($chain, $ciphering, $key, 0, $ivseed);
  143. return 'dolcrypt:'.$ciphering.':'.$ivseed.':'.$newchain;
  144. } else {
  145. return $chain;
  146. }
  147. }
  148. /**
  149. * Decode a string with a symetric encryption. Used to decrypt sensitive data saved into database.
  150. * Note: If a backup is restored onto another instance with a different $conf->file->instance_unique_id, then decoded value will differ.
  151. *
  152. * @param string $chain string to encode
  153. * @param string $key If '', we use $conf->file->instance_unique_id
  154. * @return string encoded string
  155. * @see dolEncrypt(), dol_hash()
  156. */
  157. function dolDecrypt($chain, $key = '')
  158. {
  159. global $conf;
  160. if ($chain === '' || is_null($chain)) {
  161. return '';
  162. }
  163. if (empty($key)) {
  164. $key = $conf->file->instance_unique_id;
  165. }
  166. $reg = array();
  167. if (preg_match('/^dolcrypt:([^:]+):(.+)$/', $chain, $reg)) {
  168. $ciphering = $reg[1];
  169. if (function_exists('openssl_decrypt')) {
  170. if (empty($key)) {
  171. return 'Error dolDecrypt decrypt key is empty';
  172. }
  173. $tmpexplode = explode(':', $reg[2]);
  174. if (!empty($tmpexplode[1]) && is_string($tmpexplode[0])) {
  175. $newchain = openssl_decrypt($tmpexplode[1], $ciphering, $key, 0, $tmpexplode[0]);
  176. } else {
  177. $newchain = openssl_decrypt($tmpexplode[0], $ciphering, $key, 0, null);
  178. }
  179. } else {
  180. $newchain = 'Error dolDecrypt function openssl_decrypt() not available';
  181. }
  182. return $newchain;
  183. } else {
  184. return $chain;
  185. }
  186. }
  187. /**
  188. * Returns a hash (non reversible encryption) of a string.
  189. * If constant MAIN_SECURITY_HASH_ALGO is defined, we use this function as hashing function (recommanded value is 'password_hash')
  190. * If constant MAIN_SECURITY_SALT is defined, we use it as a salt (used only if hashing algorightm is something else than 'password_hash').
  191. *
  192. * @param string $chain String to hash
  193. * @param string $type Type of hash ('0':auto will use MAIN_SECURITY_HASH_ALGO else md5, '1':sha1, '2':sha1+md5, '3':md5, '4': for OpenLdap, '5':sha256, '6':password_hash). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'.
  194. * @return string Hash of string
  195. * @see getRandomPassword(), dol_verifyHash()
  196. */
  197. function dol_hash($chain, $type = '0')
  198. {
  199. global $conf;
  200. // No need to add salt for password_hash
  201. if (($type == '0' || $type == 'auto') && !empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_hash')) {
  202. return password_hash($chain, PASSWORD_DEFAULT);
  203. }
  204. // Salt value
  205. if (!empty($conf->global->MAIN_SECURITY_SALT) && $type != '4' && $type !== 'openldap') {
  206. $chain = $conf->global->MAIN_SECURITY_SALT.$chain;
  207. }
  208. if ($type == '1' || $type == 'sha1') {
  209. return sha1($chain);
  210. } elseif ($type == '2' || $type == 'sha1md5') {
  211. return sha1(md5($chain));
  212. } elseif ($type == '3' || $type == 'md5') {
  213. return md5($chain);
  214. } elseif ($type == '4' || $type == 'openldap') {
  215. return dolGetLdapPasswordHash($chain, getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5'));
  216. } elseif ($type == '5' || $type == 'sha256') {
  217. return hash('sha256', $chain);
  218. } elseif ($type == '6' || $type == 'password_hash') {
  219. return password_hash($chain, PASSWORD_DEFAULT);
  220. } elseif (!empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'sha1') {
  221. return sha1($chain);
  222. } elseif (!empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'sha1md5') {
  223. return sha1(md5($chain));
  224. }
  225. // No particular encoding defined, use default
  226. return md5($chain);
  227. }
  228. /**
  229. * Compute a hash and compare it to the given one
  230. * For backward compatibility reasons, if the hash is not in the password_hash format, we will try to match against md5 and sha1md5
  231. * If constant MAIN_SECURITY_HASH_ALGO is defined, we use this function as hashing function.
  232. * If constant MAIN_SECURITY_SALT is defined, we use it as a salt.
  233. *
  234. * @param string $chain String to hash (not hashed string)
  235. * @param string $hash hash to compare
  236. * @param string $type Type of hash ('0':auto, '1':sha1, '2':sha1+md5, '3':md5, '4': for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'.
  237. * @return bool True if the computed hash is the same as the given one
  238. * @see dol_hash()
  239. */
  240. function dol_verifyHash($chain, $hash, $type = '0')
  241. {
  242. global $conf;
  243. if ($type == '0' && !empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_verify')) {
  244. if (! empty($hash[0]) && $hash[0] == '$') {
  245. return password_verify($chain, $hash);
  246. } elseif (dol_strlen($hash) == 32) {
  247. return dol_verifyHash($chain, $hash, '3'); // md5
  248. } elseif (dol_strlen($hash) == 40) {
  249. return dol_verifyHash($chain, $hash, '2'); // sha1md5
  250. }
  251. return false;
  252. }
  253. return dol_hash($chain, $type) == $hash;
  254. }
  255. /**
  256. * Returns a specific ldap hash of a password.
  257. *
  258. * @param string $password Password to hash
  259. * @param string $type Type of hash
  260. * @return string Hash of password
  261. */
  262. function dolGetLdapPasswordHash($password, $type = 'md5')
  263. {
  264. if (empty($type)) {
  265. $type = 'md5';
  266. }
  267. $salt = substr(sha1(time()), 0, 8);
  268. if ($type === 'md5') {
  269. return '{MD5}' . base64_encode(hash("md5", $password, true)); //For OpenLdap with md5 (based on an unencrypted password in base)
  270. } elseif ($type === 'md5frommd5') {
  271. return '{MD5}' . base64_encode(hex2bin($password)); // Create OpenLDAP MD5 password from Dolibarr MD5 password
  272. } elseif ($type === 'smd5') {
  273. return "{SMD5}" . base64_encode(hash("md5", $password . $salt, true) . $salt);
  274. } elseif ($type === 'sha') {
  275. return '{SHA}' . base64_encode(hash("sha1", $password, true));
  276. } elseif ($type === 'ssha') {
  277. return "{SSHA}" . base64_encode(hash("sha1", $password . $salt, true) . $salt);
  278. } elseif ($type === 'sha256') {
  279. return "{SHA256}" . base64_encode(hash("sha256", $password, true));
  280. } elseif ($type === 'ssha256') {
  281. return "{SSHA256}" . base64_encode(hash("sha256", $password . $salt, true) . $salt);
  282. } elseif ($type === 'sha384') {
  283. return "{SHA384}" . base64_encode(hash("sha384", $password, true));
  284. } elseif ($type === 'ssha384') {
  285. return "{SSHA384}" . base64_encode(hash("sha384", $password . $salt, true) . $salt);
  286. } elseif ($type === 'sha512') {
  287. return "{SHA512}" . base64_encode(hash("sha512", $password, true));
  288. } elseif ($type === 'ssha512') {
  289. return "{SSHA512}" . base64_encode(hash("sha512", $password . $salt, true) . $salt);
  290. } elseif ($type === 'crypt') {
  291. return '{CRYPT}' . crypt($password, $salt);
  292. } elseif ($type === 'clear') {
  293. return '{CLEAR}' . $password; // Just for test, plain text password is not secured !
  294. }
  295. }
  296. /**
  297. * Check permissions of a user to show a page and an object. Check read permission.
  298. * If GETPOST('action','aZ09') defined, we also check write and delete permission.
  299. * This method check permission on module then call checkUserAccessToObject() for permission on object (according to entity and socid of user).
  300. *
  301. * @param User $user User to check
  302. * @param string $features Features to check (it must be module name or $object->element. Can be a 'or' check with 'levela|levelb'.
  303. * Examples: 'societe', 'contact', 'produit&service', 'produit|service', ...)
  304. * This is used to check permission $user->rights->features->...
  305. * @param int|string|Object $object Object or Object ID or list of Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional).
  306. * @param string $tableandshare 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity for multicompany module. Param not used if objectid is null (optional).
  307. * @param string $feature2 Feature to check, second level of permission (optional). Can be a 'or' check with 'sublevela|sublevelb'.
  308. * This is used to check permission $user->rights->features->feature2...
  309. * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional). Can use '' if NA.
  310. * @param string $dbt_select Field rowid name, for select into tableandshare if not "rowid". Not used if objectid is null (optional)
  311. * @param int $isdraft 1=The object with id=$objectid is a draft
  312. * @param int $mode Mode (0=default, 1=return without dieing)
  313. * @return int If mode = 0 (default): Always 1, die process if not allowed. If mode = 1: Return 0 if access not allowed.
  314. * @see dol_check_secure_access_document(), checkUserAccessToObject()
  315. */
  316. function restrictedArea(User $user, $features, $object = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = 'fk_soc', $dbt_select = 'rowid', $isdraft = 0, $mode = 0)
  317. {
  318. global $db, $conf;
  319. global $hookmanager;
  320. // Define $objectid
  321. if (is_object($object)) {
  322. $objectid = $object->id;
  323. } else {
  324. $objectid = $object; // $objectid can be X or 'X,Y,Z'
  325. }
  326. if ($objectid) {
  327. $objectid = preg_replace('/[^0-9\.\,]/', '', $objectid); // For the case value is coming from a non sanitized user input
  328. }
  329. //dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename, $feature2, $dbt_socfield, $dbt_select, $isdraft");
  330. /*print "user_id=".$user->id.", features=".$features.", feature2=".$feature2.", objectid=".$objectid;
  331. print ", dbtablename=".$tableandshare.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select;
  332. print ", perm: user->right->".$features.($feature2 ? "->".$feature2 : "")."=".($user->hasRight($features, $feature2, 'lire'))."<br>";
  333. */
  334. $parentfortableentity = '';
  335. // Fix syntax of $features param
  336. $originalfeatures = $features;
  337. if ($features == 'agenda') {
  338. $tableandshare = 'actioncomm&societe';
  339. $feature2 = 'myactions|allactions';
  340. $dbt_select = 'id';
  341. }
  342. if ($features == 'facturerec') {
  343. $features = 'facture';
  344. }
  345. if ($features == 'mo') {
  346. $features = 'mrp';
  347. }
  348. if ($features == 'member') {
  349. $features = 'adherent';
  350. }
  351. if ($features == 'subscription') {
  352. $features = 'adherent';
  353. $feature2 = 'cotisation';
  354. }
  355. if ($features == 'website' && is_object($object) && $object->element == 'websitepage') {
  356. $parentfortableentity = 'fk_website@website';
  357. }
  358. if ($features == 'project') {
  359. $features = 'projet';
  360. }
  361. if ($features == 'product') {
  362. $features = 'produit';
  363. }
  364. if ($features == 'productbatch') {
  365. $features = 'produit';
  366. }
  367. if ($features == 'tax') {
  368. $feature2 = 'charges';
  369. }
  370. if ($features == 'fournisseur') { // When vendor invoice and purchase order are into module 'fournisseur'
  371. $features = 'fournisseur';
  372. if (is_object($object) && $object->element == 'invoice_supplier') {
  373. $feature2 = 'facture';
  374. } elseif (is_object($object) && $object->element == 'order_supplier') {
  375. $feature2 = 'commande';
  376. }
  377. }
  378. //print $features.' - '.$tableandshare.' - '.$feature2.' - '.$dbt_select."\n";
  379. // Get more permissions checks from hooks
  380. $parameters = array('features'=>$features, 'originalfeatures'=>$originalfeatures, 'objectid'=>$objectid, 'dbt_select'=>$dbt_select, 'idtype'=>$dbt_select, 'isdraft'=>$isdraft);
  381. $reshook = $hookmanager->executeHooks('restrictedArea', $parameters);
  382. if (isset($hookmanager->resArray['result'])) {
  383. if ($hookmanager->resArray['result'] == 0) {
  384. if ($mode) {
  385. return 0;
  386. } else {
  387. accessforbidden(); // Module returns 0, so access forbidden
  388. }
  389. }
  390. }
  391. if ($reshook > 0) { // No other test done.
  392. return 1;
  393. }
  394. // Features/modules to check
  395. $featuresarray = array($features);
  396. if (preg_match('/&/', $features)) {
  397. $featuresarray = explode("&", $features);
  398. } elseif (preg_match('/\|/', $features)) {
  399. $featuresarray = explode("|", $features);
  400. }
  401. // More subfeatures to check
  402. if (!empty($feature2)) {
  403. $feature2 = explode("|", $feature2);
  404. }
  405. $listofmodules = explode(',', $conf->global->MAIN_MODULES_FOR_EXTERNAL);
  406. // Check read permission from module
  407. $readok = 1;
  408. $nbko = 0;
  409. foreach ($featuresarray as $feature) { // first we check nb of test ko
  410. $featureforlistofmodule = $feature;
  411. if ($featureforlistofmodule == 'produit') {
  412. $featureforlistofmodule = 'product';
  413. }
  414. if ($featureforlistofmodule == 'supplier_proposal') {
  415. $featureforlistofmodule = 'supplierproposal';
  416. }
  417. if (!empty($user->socid) && !empty($conf->global->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
  418. $readok = 0;
  419. $nbko++;
  420. continue;
  421. }
  422. if ($feature == 'societe') {
  423. if (!$user->hasRight('societe', 'lire') && !$user->hasRight('fournisseur', 'lire')) {
  424. $readok = 0;
  425. $nbko++;
  426. }
  427. } elseif ($feature == 'contact') {
  428. if (empty($user->rights->societe->contact->lire)) {
  429. $readok = 0;
  430. $nbko++;
  431. }
  432. } elseif ($feature == 'produit|service') {
  433. if (empty($user->rights->produit->lire) && empty($user->rights->service->lire)) {
  434. $readok = 0;
  435. $nbko++;
  436. }
  437. } elseif ($feature == 'prelevement') {
  438. if (empty($user->rights->prelevement->bons->lire)) {
  439. $readok = 0;
  440. $nbko++;
  441. }
  442. } elseif ($feature == 'cheque') {
  443. if (empty($user->rights->banque->cheque)) {
  444. $readok = 0;
  445. $nbko++;
  446. }
  447. } elseif ($feature == 'projet') {
  448. if (empty($user->rights->projet->lire) && empty($user->rights->projet->all->lire)) {
  449. $readok = 0;
  450. $nbko++;
  451. }
  452. } elseif ($feature == 'payment') {
  453. if (empty($user->rights->facture->lire)) {
  454. $readok = 0;
  455. $nbko++;
  456. }
  457. } elseif ($feature == 'payment_supplier') {
  458. if (empty($user->rights->fournisseur->facture->lire)) {
  459. $readok = 0;
  460. $nbko++;
  461. }
  462. } elseif ($feature == 'payment_sc') {
  463. if (empty($user->rights->tax->charges->lire)) {
  464. $readok = 0;
  465. $nbko++;
  466. }
  467. } elseif (!empty($feature2)) { // This is for permissions on 2 levels (module->object->read)
  468. $tmpreadok = 1;
  469. foreach ($feature2 as $subfeature) {
  470. if ($subfeature == 'user' && $user->id == $objectid) {
  471. continue; // A user can always read its own card
  472. }
  473. if ($subfeature == 'fiscalyear' && $user->hasRight('accounting', 'fiscalyear', 'write')) {
  474. // only one right for fiscalyear
  475. $tmpreadok = 1;
  476. continue;
  477. }
  478. if (!empty($subfeature) && empty($user->rights->$feature->$subfeature->lire) && empty($user->rights->$feature->$subfeature->read)) {
  479. $tmpreadok = 0;
  480. } elseif (empty($subfeature) && empty($user->rights->$feature->lire) && empty($user->rights->$feature->read)) {
  481. $tmpreadok = 0;
  482. } else {
  483. $tmpreadok = 1;
  484. break;
  485. } // Break is to bypass second test if the first is ok
  486. }
  487. if (!$tmpreadok) { // We found a test on feature that is ko
  488. $readok = 0; // All tests are ko (we manage here the and, the or will be managed later using $nbko).
  489. $nbko++;
  490. }
  491. } elseif (!empty($feature) && ($feature != 'user' && $feature != 'usergroup')) { // This is permissions on 1 level (module->read)
  492. if (empty($user->rights->$feature->lire)
  493. && empty($user->rights->$feature->read)
  494. && empty($user->rights->$feature->run)) {
  495. $readok = 0;
  496. $nbko++;
  497. }
  498. }
  499. }
  500. // If a or and at least one ok
  501. if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) {
  502. $readok = 1;
  503. }
  504. if (!$readok) {
  505. if ($mode) {
  506. return 0;
  507. } else {
  508. accessforbidden();
  509. }
  510. }
  511. //print "Read access is ok";
  512. // Check write permission from module (we need to know write permission to create but also to delete drafts record or to upload files)
  513. $createok = 1;
  514. $nbko = 0;
  515. $wemustcheckpermissionforcreate = (GETPOST('sendit', 'alpha') || GETPOST('linkit', 'alpha') || in_array(GETPOST('action', 'aZ09'), array('create', 'update', 'set', 'upload', 'add_element_resource', 'confirm_delete_linked_resource')) || GETPOST('roworder', 'alpha', 2));
  516. $wemustcheckpermissionfordeletedraft = ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete');
  517. if ($wemustcheckpermissionforcreate || $wemustcheckpermissionfordeletedraft) {
  518. foreach ($featuresarray as $feature) {
  519. if ($feature == 'contact') {
  520. if (empty($user->rights->societe->contact->creer)) {
  521. $createok = 0;
  522. $nbko++;
  523. }
  524. } elseif ($feature == 'produit|service') {
  525. if (empty($user->rights->produit->creer) && empty($user->rights->service->creer)) {
  526. $createok = 0;
  527. $nbko++;
  528. }
  529. } elseif ($feature == 'prelevement') {
  530. if (!$user->rights->prelevement->bons->creer) {
  531. $createok = 0;
  532. $nbko++;
  533. }
  534. } elseif ($feature == 'commande_fournisseur') {
  535. if (empty($user->rights->fournisseur->commande->creer) || empty($user->rights->supplier_order->creer)) {
  536. $createok = 0;
  537. $nbko++;
  538. }
  539. } elseif ($feature == 'banque') {
  540. if (empty($user->rights->banque->modifier)) {
  541. $createok = 0;
  542. $nbko++;
  543. }
  544. } elseif ($feature == 'cheque') {
  545. if (empty($user->rights->banque->cheque)) {
  546. $createok = 0;
  547. $nbko++;
  548. }
  549. } elseif ($feature == 'import') {
  550. if (empty($user->rights->import->run)) {
  551. $createok = 0;
  552. $nbko++;
  553. }
  554. } elseif ($feature == 'ecm') {
  555. if (!$user->rights->ecm->upload) {
  556. $createok = 0;
  557. $nbko++;
  558. }
  559. } elseif (!empty($feature2)) { // This is for permissions on 2 levels (module->object->write)
  560. foreach ($feature2 as $subfeature) {
  561. if ($subfeature == 'user' && $user->id == $objectid && $user->rights->user->self->creer) {
  562. continue; // User can edit its own card
  563. }
  564. if ($subfeature == 'user' && $user->id == $objectid && $user->rights->user->self->password) {
  565. continue; // User can edit its own password
  566. }
  567. if ($subfeature == 'user' && $user->id != $objectid && $user->rights->user->user->password) {
  568. continue; // User can edit another user's password
  569. }
  570. if (empty($user->rights->$feature->$subfeature->creer)
  571. && empty($user->rights->$feature->$subfeature->write)
  572. && empty($user->rights->$feature->$subfeature->create)) {
  573. $createok = 0;
  574. $nbko++;
  575. } else {
  576. $createok = 1;
  577. // Break to bypass second test if the first is ok
  578. break;
  579. }
  580. }
  581. } elseif (!empty($feature)) { // This is for permissions on 1 levels (module->write)
  582. //print '<br>feature='.$feature.' creer='.$user->rights->$feature->creer.' write='.$user->rights->$feature->write; exit;
  583. if (empty($user->rights->$feature->creer)
  584. && empty($user->rights->$feature->write)
  585. && empty($user->rights->$feature->create)) {
  586. $createok = 0;
  587. $nbko++;
  588. }
  589. }
  590. }
  591. // If a or and at least one ok
  592. if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) {
  593. $createok = 1;
  594. }
  595. if ($wemustcheckpermissionforcreate && !$createok) {
  596. if ($mode) {
  597. return 0;
  598. } else {
  599. accessforbidden();
  600. }
  601. }
  602. //print "Write access is ok";
  603. }
  604. // Check create user permission
  605. $createuserok = 1;
  606. if (GETPOST('action', 'aZ09') == 'confirm_create_user' && GETPOST("confirm", 'aZ09') == 'yes') {
  607. if (!$user->rights->user->user->creer) {
  608. $createuserok = 0;
  609. }
  610. if (!$createuserok) {
  611. if ($mode) {
  612. return 0;
  613. } else {
  614. accessforbidden();
  615. }
  616. }
  617. //print "Create user access is ok";
  618. }
  619. // Check delete permission from module
  620. $deleteok = 1;
  621. $nbko = 0;
  622. if ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete') {
  623. foreach ($featuresarray as $feature) {
  624. if ($feature == 'bookmark') {
  625. if (!$user->rights->bookmark->supprimer) {
  626. if ($user->id != $object->fk_user || empty($user->rights->bookmark->creer)) {
  627. $deleteok = 0;
  628. }
  629. }
  630. } elseif ($feature == 'contact') {
  631. if (!$user->rights->societe->contact->supprimer) {
  632. $deleteok = 0;
  633. }
  634. } elseif ($feature == 'produit|service') {
  635. if (!$user->rights->produit->supprimer && !$user->rights->service->supprimer) {
  636. $deleteok = 0;
  637. }
  638. } elseif ($feature == 'commande_fournisseur') {
  639. if (!$user->rights->fournisseur->commande->supprimer) {
  640. $deleteok = 0;
  641. }
  642. } elseif ($feature == 'payment_supplier') { // Permission to delete a payment of an invoice is permission to edit an invoice.
  643. if (!$user->rights->fournisseur->facture->creer) {
  644. $deleteok = 0;
  645. }
  646. } elseif ($feature == 'payment') {
  647. if (!$user->rights->facture->paiement) {
  648. $deleteok = 0;
  649. }
  650. } elseif ($feature == 'payment_sc') {
  651. if (!$user->rights->tax->charges->creer) {
  652. $deleteok = 0;
  653. }
  654. } elseif ($feature == 'banque') {
  655. if (empty($user->rights->banque->modifier)) {
  656. $deleteok = 0;
  657. }
  658. } elseif ($feature == 'cheque') {
  659. if (empty($user->rights->banque->cheque)) {
  660. $deleteok = 0;
  661. }
  662. } elseif ($feature == 'ecm') {
  663. if (!$user->rights->ecm->upload) {
  664. $deleteok = 0;
  665. }
  666. } elseif ($feature == 'ftp') {
  667. if (!$user->rights->ftp->write) {
  668. $deleteok = 0;
  669. }
  670. } elseif ($feature == 'salaries') {
  671. if (!$user->rights->salaries->delete) {
  672. $deleteok = 0;
  673. }
  674. } elseif ($feature == 'adherent') {
  675. if (empty($user->rights->adherent->supprimer)) {
  676. $deleteok = 0;
  677. }
  678. } elseif ($feature == 'paymentbybanktransfer') {
  679. if (empty($user->rights->paymentbybanktransfer->create)) { // There is no delete permission
  680. $deleteok = 0;
  681. }
  682. } elseif ($feature == 'prelevement') {
  683. if (empty($user->rights->prelevement->bons->creer)) { // There is no delete permission
  684. $deleteok = 0;
  685. }
  686. } elseif (!empty($feature2)) { // This is for permissions on 2 levels
  687. foreach ($feature2 as $subfeature) {
  688. if (empty($user->rights->$feature->$subfeature->supprimer) && empty($user->rights->$feature->$subfeature->delete)) {
  689. $deleteok = 0;
  690. } else {
  691. $deleteok = 1;
  692. break;
  693. } // For bypass the second test if the first is ok
  694. }
  695. } elseif (!empty($feature)) { // This is used for permissions on 1 level
  696. //print '<br>feature='.$feature.' creer='.$user->rights->$feature->supprimer.' write='.$user->rights->$feature->delete;
  697. if (empty($user->rights->$feature->supprimer)
  698. && empty($user->rights->$feature->delete)
  699. && empty($user->rights->$feature->run)) {
  700. $deleteok = 0;
  701. }
  702. }
  703. }
  704. // If a or and at least one ok
  705. if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) {
  706. $deleteok = 1;
  707. }
  708. if (!$deleteok && !($isdraft && $createok)) {
  709. if ($mode) {
  710. return 0;
  711. } else {
  712. accessforbidden();
  713. }
  714. }
  715. //print "Delete access is ok";
  716. }
  717. // If we have a particular object to check permissions on, we check if $user has permission
  718. // for this given object (link to company, is contact for project, ...)
  719. if (!empty($objectid) && $objectid > 0) {
  720. $ok = checkUserAccessToObject($user, $featuresarray, $object, $tableandshare, $feature2, $dbt_keyfield, $dbt_select, $parentfortableentity);
  721. $params = array('objectid' => $objectid, 'features' => join(',', $featuresarray), 'features2' => $feature2);
  722. //print 'checkUserAccessToObject ok='.$ok;
  723. if ($mode) {
  724. return $ok ? 1 : 0;
  725. } else {
  726. if ($ok) {
  727. return 1;
  728. } else {
  729. accessforbidden('', 1, 1, 0, $params);
  730. }
  731. }
  732. }
  733. return 1;
  734. }
  735. /**
  736. * Check that access by a given user to an object is ok.
  737. * This function is also called by restrictedArea() that check before if module is enabled and if permission of user for $action is ok.
  738. *
  739. * @param User $user User to check
  740. * @param array $featuresarray Features/modules to check. Example: ('user','service','member','project','task',...)
  741. * @param int|string|Object $object Full object or object ID or list of object id. For example if we want to check a particular record (optional) is linked to a owned thirdparty (optional).
  742. * @param string $tableandshare 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity for multicompany modume. Param not used if objectid is null (optional).
  743. * @param array|string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'.
  744. * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional). Can use '' if NA.
  745. * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional).
  746. * @param string $parenttableforentity Parent table for entity. Example 'fk_website@website'
  747. * @return bool True if user has access, False otherwise
  748. * @see restrictedArea()
  749. */
  750. function checkUserAccessToObject($user, array $featuresarray, $object = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = '', $dbt_select = 'rowid', $parenttableforentity = '')
  751. {
  752. global $db, $conf;
  753. if (is_object($object)) {
  754. $objectid = $object->id;
  755. } else {
  756. $objectid = $object; // $objectid can be X or 'X,Y,Z'
  757. }
  758. $objectid = preg_replace('/[^0-9\.\,]/', '', $objectid); // For the case value is coming from a non sanitized user input
  759. //dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename, $feature2, $dbt_socfield, $dbt_select, $isdraft");
  760. //print "user_id=".$user->id.", features=".join(',', $featuresarray).", objectid=".$objectid;
  761. //print ", tableandshare=".$tableandshare.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select."<br>";
  762. // More parameters
  763. $params = explode('&', $tableandshare);
  764. $dbtablename = (!empty($params[0]) ? $params[0] : '');
  765. $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename);
  766. foreach ($featuresarray as $feature) {
  767. $sql = '';
  768. //var_dump($feature);exit;
  769. // For backward compatibility
  770. if ($feature == 'member') {
  771. $feature = 'adherent';
  772. }
  773. if ($feature == 'project') {
  774. $feature = 'projet';
  775. }
  776. if ($feature == 'task') {
  777. $feature = 'projet_task';
  778. }
  779. if ($feature == 'eventorganization') {
  780. $feature = 'agenda';
  781. $dbtablename = 'actioncomm';
  782. }
  783. if ($feature == 'payment_sc') {
  784. $feature = "chargesociales";
  785. $objectid = $object->fk_charge;
  786. }
  787. $checkonentitydone = 0;
  788. // Array to define rules of checks to do
  789. $check = array('adherent', 'banque', 'bom', 'don', 'mrp', 'user', 'usergroup', 'payment', 'payment_supplier', 'product', 'produit', 'service', 'produit|service', 'categorie', 'resource', 'expensereport', 'holiday', 'salaries', 'website', 'recruitment', 'chargesociales'); // Test on entity only (Objects with no link to company)
  790. $checksoc = array('societe'); // Test for object Societe
  791. $checkother = array('agenda', 'contact', 'contrat'); // Test on entity + link to third party on field $dbt_keyfield. Allowed if link is empty (Ex: contacts...).
  792. $checkproject = array('projet', 'project'); // Test for project object
  793. $checktask = array('projet_task'); // Test for task object
  794. $checkhierarchy = array('expensereport', 'holiday'); // check permission among the hierarchy of user
  795. $checkuser = array('bookmark'); // check permission among the fk_user (must be myself or null)
  796. $nocheck = array('barcode', 'stock'); // No test
  797. //$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...).
  798. // If dbtablename not defined, we use same name for table than module name
  799. if (empty($dbtablename)) {
  800. $dbtablename = $feature;
  801. $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename); // We change dbtablename, so we set sharedelement too.
  802. }
  803. // To avoid an access forbidden with a numeric ref
  804. if ($dbt_select != 'rowid' && $dbt_select != 'id') {
  805. $objectid = "'".$objectid."'"; // Note: $objectid was already cast into int at begin of this method.
  806. }
  807. // Check permission for objectid on entity only
  808. if (in_array($feature, $check) && $objectid > 0) { // For $objectid = 0, no check
  809. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  810. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  811. if (($feature == 'user' || $feature == 'usergroup') && isModEnabled('multicompany')) { // Special for multicompany
  812. if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
  813. if ($conf->entity == 1 && $user->admin && !$user->entity) {
  814. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  815. $sql .= " AND dbt.entity IS NOT NULL";
  816. } else {
  817. $sql .= ",".MAIN_DB_PREFIX."usergroup_user as ug";
  818. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  819. $sql .= " AND ((ug.fk_user = dbt.rowid";
  820. $sql .= " AND ug.entity IN (".getEntity('usergroup')."))";
  821. $sql .= " OR dbt.entity = 0)"; // Show always superadmin
  822. }
  823. } else {
  824. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  825. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  826. }
  827. } else {
  828. $reg = array();
  829. if ($parenttableforentity && preg_match('/(.*)@(.*)/', $parenttableforentity, $reg)) {
  830. $sql .= ", ".MAIN_DB_PREFIX.$reg[2]." as dbtp";
  831. $sql .= " WHERE dbt.".$reg[1]." = dbtp.rowid AND dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  832. $sql .= " AND dbtp.entity IN (".getEntity($sharedelement, 1).")";
  833. } else {
  834. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  835. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  836. }
  837. }
  838. $checkonentitydone = 1;
  839. }
  840. if (in_array($feature, $checksoc) && $objectid > 0) { // We check feature = checksoc. For $objectid = 0, no check
  841. // If external user: Check permission for external users
  842. if ($user->socid > 0) {
  843. if ($user->socid != $objectid) {
  844. return false;
  845. }
  846. } elseif (isModEnabled("societe") && ($user->hasRight('societe', 'lire') && empty($user->rights->societe->client->voir))) {
  847. // If internal user: Check permission for internal users that are restricted on their objects
  848. $sql = "SELECT COUNT(sc.fk_soc) as nb";
  849. $sql .= " FROM (".MAIN_DB_PREFIX."societe_commerciaux as sc";
  850. $sql .= ", ".MAIN_DB_PREFIX."societe as s)";
  851. $sql .= " WHERE sc.fk_soc IN (".$db->sanitize($objectid, 1).")";
  852. $sql .= " AND sc.fk_user = ".((int) $user->id);
  853. $sql .= " AND sc.fk_soc = s.rowid";
  854. $sql .= " AND s.entity IN (".getEntity($sharedelement, 1).")";
  855. } elseif (isModEnabled('multicompany')) {
  856. // If multicompany and internal users with all permissions, check user is in correct entity
  857. $sql = "SELECT COUNT(s.rowid) as nb";
  858. $sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
  859. $sql .= " WHERE s.rowid IN (".$db->sanitize($objectid, 1).")";
  860. $sql .= " AND s.entity IN (".getEntity($sharedelement, 1).")";
  861. }
  862. $checkonentitydone = 1;
  863. }
  864. if (in_array($feature, $checkother) && $objectid > 0) { // Test on entity + link to thirdparty. Allowed if link is empty (Ex: contacts...).
  865. // If external user: Check permission for external users
  866. if ($user->socid > 0) {
  867. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  868. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  869. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  870. $sql .= " AND dbt.fk_soc = ".((int) $user->socid);
  871. } elseif (isModEnabled("societe") && ($user->hasRight('societe', 'lire') && empty($user->rights->societe->client->voir))) {
  872. // If internal user: Check permission for internal users that are restricted on their objects
  873. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  874. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  875. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON dbt.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
  876. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  877. $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
  878. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  879. } elseif (isModEnabled('multicompany')) {
  880. // If multicompany and internal users with all permissions, check user is in correct entity
  881. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  882. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  883. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  884. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  885. }
  886. $checkonentitydone = 1;
  887. }
  888. if (in_array($feature, $checkproject) && $objectid > 0) {
  889. if (isModEnabled('project') && empty($user->rights->projet->all->lire)) {
  890. $projectid = $objectid;
  891. include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
  892. $projectstatic = new Project($db);
  893. $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0);
  894. $tmparray = explode(',', $tmps);
  895. if (!in_array($projectid, $tmparray)) {
  896. return false;
  897. }
  898. } else {
  899. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  900. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  901. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  902. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  903. }
  904. $checkonentitydone = 1;
  905. }
  906. if (in_array($feature, $checktask) && $objectid > 0) {
  907. if (isModEnabled('project') && empty($user->rights->projet->all->lire)) {
  908. $task = new Task($db);
  909. $task->fetch($objectid);
  910. $projectid = $task->fk_project;
  911. include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
  912. $projectstatic = new Project($db);
  913. $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0);
  914. $tmparray = explode(',', $tmps);
  915. if (!in_array($projectid, $tmparray)) {
  916. return false;
  917. }
  918. } else {
  919. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  920. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  921. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  922. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  923. }
  924. $checkonentitydone = 1;
  925. }
  926. //var_dump($sql);
  927. 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
  928. // If external user: Check permission for external users
  929. if ($user->socid > 0) {
  930. if (empty($dbt_keyfield)) {
  931. dol_print_error('', 'Param dbt_keyfield is required but not defined');
  932. }
  933. $sql = "SELECT COUNT(dbt.".$dbt_keyfield.") as nb";
  934. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  935. $sql .= " WHERE dbt.rowid IN (".$db->sanitize($objectid, 1).")";
  936. $sql .= " AND dbt.".$dbt_keyfield." = ".((int) $user->socid);
  937. } elseif (isModEnabled("societe") && empty($user->rights->societe->client->voir)) {
  938. // If internal user without permission to see all thirdparties: Check permission for internal users that are restricted on their objects
  939. if ($feature != 'ticket') {
  940. if (empty($dbt_keyfield)) {
  941. dol_print_error('', 'Param dbt_keyfield is required but not defined');
  942. }
  943. $sql = "SELECT COUNT(sc.fk_soc) as nb";
  944. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  945. $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
  946. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  947. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  948. $sql .= " AND sc.fk_soc = dbt.".$dbt_keyfield;
  949. $sql .= " AND sc.fk_user = ".((int) $user->id);
  950. } else {
  951. // On ticket, the thirdparty is not mandatory, so we need a special test to accept record with no thirdparties.
  952. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  953. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  954. $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = dbt.".$dbt_keyfield." AND sc.fk_user = ".((int) $user->id);
  955. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  956. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  957. $sql .= " AND (sc.fk_user = ".((int) $user->id)." OR sc.fk_user IS NULL)";
  958. }
  959. } elseif (isModEnabled('multicompany')) {
  960. // If multicompany, and user is an internal user with all permissions, check that object is in correct entity
  961. $sql = "SELECT COUNT(dbt.".$dbt_select.") as nb";
  962. $sql .= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
  963. $sql .= " WHERE dbt.".$dbt_select." IN (".$db->sanitize($objectid, 1).")";
  964. $sql .= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
  965. }
  966. }
  967. //print $sql;
  968. // For events, check on users assigned to event
  969. if ($feature === 'agenda' && $objectid > 0) {
  970. // Also check owner or attendee for users without allactions->read
  971. if ($objectid > 0 && empty($user->rights->agenda->allactions->read)) {
  972. require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
  973. $action = new ActionComm($db);
  974. $action->fetch($objectid);
  975. if ($action->authorid != $user->id && $action->userownerid != $user->id && !(array_key_exists($user->id, $action->userassigned))) {
  976. return false;
  977. }
  978. }
  979. }
  980. // For some object, we also have to check it is in the user hierarchy
  981. // Param $object must be the full object and not a simple id to have this test possible.
  982. if (in_array($feature, $checkhierarchy) && is_object($object) && $objectid > 0) {
  983. $childids = $user->getAllChildIds(1);
  984. $useridtocheck = 0;
  985. if ($feature == 'holiday') {
  986. $useridtocheck = $object->fk_user;
  987. if (!in_array($useridtocheck, $childids)) {
  988. return false;
  989. }
  990. $useridtocheck = $object->fk_validator;
  991. if (!in_array($useridtocheck, $childids)) {
  992. return false;
  993. }
  994. }
  995. if ($feature == 'expensereport') {
  996. $useridtocheck = $object->fk_user_author;
  997. if (!$user->rights->expensereport->readall) {
  998. if (!in_array($useridtocheck, $childids)) {
  999. return false;
  1000. }
  1001. }
  1002. }
  1003. }
  1004. // For some object, we also have to check it is public or owned by user
  1005. // Param $object must be the full object and not a simple id to have this test possible.
  1006. if (in_array($feature, $checkuser) && is_object($object) && $objectid > 0) {
  1007. $useridtocheck = $object->fk_user;
  1008. if (!empty($useridtocheck) && $useridtocheck > 0 && $useridtocheck != $user->id && empty($user->admin)) {
  1009. return false;
  1010. }
  1011. }
  1012. if ($sql) {
  1013. $resql = $db->query($sql);
  1014. if ($resql) {
  1015. $obj = $db->fetch_object($resql);
  1016. if (!$obj || $obj->nb < count(explode(',', $objectid))) { // error if we found 0 or less record than nb of id provided
  1017. return false;
  1018. }
  1019. } else {
  1020. dol_syslog("Bad forged sql in checkUserAccessToObject", LOG_WARNING);
  1021. return false;
  1022. }
  1023. }
  1024. }
  1025. return true;
  1026. }
  1027. /**
  1028. * Show a message to say access is forbidden and stop program.
  1029. * This includes only HTTP header.
  1030. * Calling this function terminate execution of PHP.
  1031. *
  1032. * @param string $message Force error message
  1033. * @param int $http_response_code HTTP response code
  1034. * @param int $stringalreadysanitized 1 if string is already sanitized with HTML entities
  1035. * @return void
  1036. * @see accessforbidden()
  1037. */
  1038. function httponly_accessforbidden($message = 1, $http_response_code = 403, $stringalreadysanitized = 0)
  1039. {
  1040. top_httphead();
  1041. http_response_code($http_response_code);
  1042. if ($stringalreadysanitized) {
  1043. print $message;
  1044. } else {
  1045. print htmlentities($message);
  1046. }
  1047. exit(1);
  1048. }
  1049. /**
  1050. * Show a message to say access is forbidden and stop program.
  1051. * This includes HTTP and HTML header and footer (except if $printheader and $printfooter is 0, use this case inside an already started page).
  1052. * Calling this function terminate execution of PHP.
  1053. *
  1054. * @param string $message Force error message
  1055. * @param int $printheader Show header before
  1056. * @param int $printfooter Show footer after
  1057. * @param int $showonlymessage Show only message parameter. Otherwise add more information.
  1058. * @param array|null $params More parameters provided to hook
  1059. * @return void
  1060. * @see httponly_accessforbidden()
  1061. */
  1062. function accessforbidden($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
  1063. {
  1064. global $conf, $db, $user, $langs, $hookmanager;
  1065. global $action, $object;
  1066. if (!is_object($langs)) {
  1067. include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
  1068. $langs = new Translate('', $conf);
  1069. $langs->setDefaultLang();
  1070. }
  1071. $langs->load("errors");
  1072. if ($printheader) {
  1073. if (function_exists("llxHeader")) {
  1074. llxHeader('');
  1075. } elseif (function_exists("llxHeaderVierge")) {
  1076. llxHeaderVierge('');
  1077. }
  1078. }
  1079. print '<div class="error">';
  1080. if (empty($message)) {
  1081. print $langs->trans("ErrorForbidden");
  1082. } else {
  1083. print $langs->trans($message);
  1084. }
  1085. print '</div>';
  1086. print '<br>';
  1087. if (empty($showonlymessage)) {
  1088. if (empty($hookmanager)) {
  1089. include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
  1090. $hookmanager = new HookManager($db);
  1091. // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
  1092. $hookmanager->initHooks(array('main'));
  1093. }
  1094. $parameters = array('message'=>$message, 'params'=>$params);
  1095. $reshook = $hookmanager->executeHooks('getAccessForbiddenMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
  1096. print $hookmanager->resPrint;
  1097. if (empty($reshook)) {
  1098. $langs->loadLangs(array("errors"));
  1099. if ($user->login) {
  1100. print $langs->trans("CurrentLogin").': <span class="error">'.$user->login.'</span><br>';
  1101. print $langs->trans("ErrorForbidden2", $langs->transnoentitiesnoconv("Home"), $langs->transnoentitiesnoconv("Users"));
  1102. print $langs->trans("ErrorForbidden4");
  1103. } else {
  1104. print $langs->trans("ErrorForbidden3");
  1105. }
  1106. }
  1107. }
  1108. if ($printfooter && function_exists("llxFooter")) {
  1109. llxFooter();
  1110. }
  1111. exit(0);
  1112. }
  1113. /**
  1114. * Return the max allowed for file upload.
  1115. * Analyze among: upload_max_filesize, post_max_size, MAIN_UPLOAD_DOC
  1116. *
  1117. * @return array Array with all max size for file upload
  1118. */
  1119. function getMaxFileSizeArray()
  1120. {
  1121. global $conf;
  1122. $max = $conf->global->MAIN_UPLOAD_DOC; // In Kb
  1123. $maxphp = @ini_get('upload_max_filesize'); // In unknown
  1124. if (preg_match('/k$/i', $maxphp)) {
  1125. $maxphp = preg_replace('/k$/i', '', $maxphp);
  1126. $maxphp = $maxphp * 1;
  1127. }
  1128. if (preg_match('/m$/i', $maxphp)) {
  1129. $maxphp = preg_replace('/m$/i', '', $maxphp);
  1130. $maxphp = $maxphp * 1024;
  1131. }
  1132. if (preg_match('/g$/i', $maxphp)) {
  1133. $maxphp = preg_replace('/g$/i', '', $maxphp);
  1134. $maxphp = $maxphp * 1024 * 1024;
  1135. }
  1136. if (preg_match('/t$/i', $maxphp)) {
  1137. $maxphp = preg_replace('/t$/i', '', $maxphp);
  1138. $maxphp = $maxphp * 1024 * 1024 * 1024;
  1139. }
  1140. $maxphp2 = @ini_get('post_max_size'); // In unknown
  1141. if (preg_match('/k$/i', $maxphp2)) {
  1142. $maxphp2 = preg_replace('/k$/i', '', $maxphp2);
  1143. $maxphp2 = $maxphp2 * 1;
  1144. }
  1145. if (preg_match('/m$/i', $maxphp2)) {
  1146. $maxphp2 = preg_replace('/m$/i', '', $maxphp2);
  1147. $maxphp2 = $maxphp2 * 1024;
  1148. }
  1149. if (preg_match('/g$/i', $maxphp2)) {
  1150. $maxphp2 = preg_replace('/g$/i', '', $maxphp2);
  1151. $maxphp2 = $maxphp2 * 1024 * 1024;
  1152. }
  1153. if (preg_match('/t$/i', $maxphp2)) {
  1154. $maxphp2 = preg_replace('/t$/i', '', $maxphp2);
  1155. $maxphp2 = $maxphp2 * 1024 * 1024 * 1024;
  1156. }
  1157. // Now $max and $maxphp and $maxphp2 are in Kb
  1158. $maxmin = $max;
  1159. $maxphptoshow = $maxphptoshowparam = '';
  1160. if ($maxphp > 0) {
  1161. $maxmin = min($maxmin, $maxphp);
  1162. $maxphptoshow = $maxphp;
  1163. $maxphptoshowparam = 'upload_max_filesize';
  1164. }
  1165. if ($maxphp2 > 0) {
  1166. $maxmin = min($maxmin, $maxphp2);
  1167. if ($maxphp2 < $maxphp) {
  1168. $maxphptoshow = $maxphp2;
  1169. $maxphptoshowparam = 'post_max_size';
  1170. }
  1171. }
  1172. //var_dump($maxphp.'-'.$maxphp2);
  1173. //var_dump($maxmin);
  1174. return array('max'=>$max, 'maxmin'=>$maxmin, 'maxphptoshow'=>$maxphptoshow, 'maxphptoshowparam'=>$maxphptoshowparam);
  1175. }