1 | <?php |
||
2 | /* Copyright (C) 2008-2011 Laurent Destailleur <[email protected]> |
||
3 | * Copyright (C) 2008-2017 Regis Houssin <[email protected]> |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 3 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License |
||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
17 | * or see http://www.gnu.org/ |
||
18 | */ |
||
19 | namespace Alixar\Helpers; |
||
20 | |||
21 | /** |
||
22 | * \file htdocs/core/lib/security.lib.php |
||
23 | * \ingroup core |
||
24 | * \brief Set of function used for dolibarr security (common function included into filefunc.inc.php) |
||
25 | * Warning, this file must not depends on other library files, except function.lib.php |
||
26 | * because it is used at low code level. |
||
27 | */ |
||
28 | class Security |
||
29 | { |
||
30 | |||
31 | /** |
||
32 | * Encode a string with base 64 algorithm + specific delta change. |
||
33 | * |
||
34 | * @param string $chain string to encode |
||
35 | * @param string $key rule to use for delta ('0', '1' or 'myownkey') |
||
36 | * @return string encoded string |
||
37 | * @see dol_decode |
||
38 | */ |
||
39 | function dol_encode($chain, $key = '1') |
||
40 | { |
||
41 | if (is_numeric($key) && $key == '1') { // rule 1 is offset of 17 for char |
||
42 | $output_tab = array(); |
||
43 | $strlength = dol_strlen($chain); |
||
44 | for ($i = 0; $i < $strlength; $i++) { |
||
45 | $output_tab[$i] = chr(ord(substr($chain, $i, 1)) + 17); |
||
46 | } |
||
47 | $chain = implode("", $output_tab); |
||
48 | } elseif ($key) { |
||
49 | $result = ''; |
||
50 | $strlength = dol_strlen($chain); |
||
51 | for ($i = 0; $i < $strlength; $i++) { |
||
52 | $keychar = substr($key, ($i % strlen($key)) - 1, 1); |
||
53 | $result .= chr(ord(substr($chain, $i, 1)) + (ord($keychar) - 65)); |
||
54 | } |
||
55 | $chain = $result; |
||
56 | } |
||
57 | |||
58 | return base64_encode($chain); |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * Decode a base 64 encoded + specific delta change. |
||
63 | * This function is called by filefunc.inc.php at each page call. |
||
64 | * |
||
65 | * @param string $chain string to decode |
||
66 | * @param string $key rule to use for delta ('0', '1' or 'myownkey') |
||
67 | * @return string decoded string |
||
68 | * @see dol_encode |
||
69 | */ |
||
70 | function dol_decode($chain, $key = '1') |
||
71 | { |
||
72 | $chain = base64_decode($chain); |
||
73 | |||
74 | if (is_numeric($key) && $key == '1') { // rule 1 is offset of 17 for char |
||
75 | $output_tab = array(); |
||
76 | $strlength = dol_strlen($chain); |
||
77 | for ($i = 0; $i < $strlength; $i++) { |
||
78 | $output_tab[$i] = chr(ord(substr($chain, $i, 1)) - 17); |
||
79 | } |
||
80 | |||
81 | $chain = implode("", $output_tab); |
||
82 | } elseif ($key) { |
||
83 | $result = ''; |
||
84 | $strlength = dol_strlen($chain); |
||
85 | for ($i = 0; $i < $strlength; $i++) { |
||
86 | $keychar = substr($key, ($i % strlen($key)) - 1, 1); |
||
87 | $result .= chr(ord(substr($chain, $i, 1)) - (ord($keychar) - 65)); |
||
88 | } |
||
89 | $chain = $result; |
||
90 | } |
||
91 | |||
92 | return $chain; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Returns a hash of a string. |
||
97 | * If constant MAIN_SECURITY_HASH_ALGO is defined, we use this function as hashing function (recommanded value is 'password_hash') |
||
98 | * If constant MAIN_SECURITY_SALT is defined, we use it as a salt (used only if hashing algorightm is something else than 'password_hash'). |
||
99 | * |
||
100 | * @param string $chain String to hash |
||
101 | * @param string $type Type of hash ('0':auto will use MAIN_SECURITY_HASH_ALGO else md5, '1':sha1, '2':sha1+md5, '3':md5, '4':md5 for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. |
||
102 | * @return string Hash of string |
||
103 | * @getRandomPassword |
||
104 | */ |
||
105 | static function dol_hash($chain, $type = '0') |
||
106 | { |
||
107 | // No need to add salt for password_hash |
||
108 | if (($type == '0' || $type == 'auto') && !empty(Globals::$conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_hash')) { |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
109 | return password_hash($chain, PASSWORD_DEFAULT); |
||
110 | } |
||
111 | // Salt value |
||
112 | if (!empty(Globals::$conf->global->MAIN_SECURITY_SALT)) { |
||
113 | $chain = Globals::$conf->global->MAIN_SECURITY_SALT . $chain; |
||
114 | } |
||
115 | if ($type == '1' || $type == 'sha1') { |
||
116 | return sha1($chain); |
||
117 | } |
||
118 | if ($type == '2' || $type == 'sha1md5') { |
||
119 | return sha1(md5($chain)); |
||
120 | } |
||
121 | if ($type == '3' || $type == 'md5') { |
||
122 | return md5($chain); |
||
123 | } |
||
124 | if ($type == '4' || $type == 'md5openldap') { |
||
125 | return '{md5}' . base64_encode(mhash(MHASH_MD5, $chain)); // For OpenLdap with md5 (based on an unencrypted password in base) |
||
126 | } |
||
127 | if ($type == '5') { |
||
128 | return hash('sha256', $chain); |
||
129 | } |
||
130 | if (!empty(Globals::$conf->global->MAIN_SECURITY_HASH_ALGO) && Globals::$conf->global->MAIN_SECURITY_HASH_ALGO == 'sha1') { |
||
131 | return sha1($chain); |
||
132 | } |
||
133 | if (!empty(Globals::$conf->global->MAIN_SECURITY_HASH_ALGO) && Globals::$conf->global->MAIN_SECURITY_HASH_ALGO == 'sha1md5') { |
||
134 | return sha1(md5($chain)); |
||
135 | } |
||
136 | // No particular encoding defined, use default |
||
137 | return md5($chain); |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Compute a hash and compare it to the given one |
||
142 | * For backward compatibility reasons, if the hash is not in the password_hash format, we will try to match against md5 and sha1md5 |
||
143 | * If constant MAIN_SECURITY_HASH_ALGO is defined, we use this function as hashing function. |
||
144 | * If constant MAIN_SECURITY_SALT is defined, we use it as a salt. |
||
145 | * |
||
146 | * @param string $chain String to hash (not hashed string) |
||
147 | * @param string $hash hash to compare |
||
148 | * @param string $type Type of hash ('0':auto, '1':sha1, '2':sha1+md5, '3':md5, '4':md5 for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. |
||
149 | * @return bool True if the computed hash is the same as the given one |
||
150 | */ |
||
151 | static function dol_verifyHash($chain, $hash, $type = '0') |
||
152 | { |
||
153 | return \password_verify($chain, $hash); |
||
154 | |||
155 | if ($type == '0' && !empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_verify')) { |
||
156 | if ($hash[0] == '$') { |
||
157 | return \password_verify($chain, $hash); |
||
158 | } |
||
159 | if (strlen($hash) == 32) { |
||
160 | return self::dol_verifyHash($chain, $hash, '3'); // md5 |
||
161 | } |
||
162 | if (strlen($hash) == 40) { |
||
163 | return self::dol_verifyHash($chain, $hash, '2'); // sha1md5 |
||
164 | } |
||
165 | |||
166 | return false; |
||
167 | } |
||
168 | |||
169 | return self::dol_hash($chain, $type) == $hash; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Check permissions of a user to show a page and an object. Check read permission. |
||
174 | * If GETPOST('action','aZ09') defined, we also check write and delete permission. |
||
175 | * |
||
176 | * @param User $user User to check |
||
177 | * @param string $features Features to check (it must be module name. Examples: 'societe', 'contact', 'produit&service', 'produit|service', ...) |
||
178 | * @param int $objectid Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional). |
||
179 | * @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). |
||
180 | * @param string $feature2 Feature to check, second level of permission (optional). Can be a 'or' check with 'level1|level2'. |
||
181 | * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional) |
||
182 | * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional) |
||
183 | * @param int $isdraft 1=The object with id=$objectid is a draft |
||
184 | * @return int Always 1, die process if not allowed |
||
185 | * @see dol_check_secure_access_document |
||
186 | */ |
||
187 | function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = 'fk_soc', $dbt_select = 'rowid', $isdraft = 0) |
||
188 | { |
||
189 | global $db, $conf; |
||
190 | global $hookmanager; |
||
191 | |||
192 | //dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename,$feature2,$dbt_socfield,$dbt_select"); |
||
193 | //print "user_id=".$user->id.", features=".$features.", feature2=".$feature2.", objectid=".$objectid; |
||
194 | //print ", dbtablename=".$dbtablename.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select; |
||
195 | //print ", perm: ".$features."->".$feature2."=".($user->rights->$features->$feature2->lire)."<br>"; |
||
196 | // Get more permissions checks from hooks |
||
197 | $parameters = array('features' => $features, 'objectid' => $objectid, 'idtype' => $dbt_select); |
||
198 | $reshook = $hookmanager->executeHooks('restrictedArea', $parameters); |
||
199 | if (!empty($hookmanager->resArray['result'])) |
||
200 | return true; |
||
201 | if ($reshook > 0) |
||
202 | return false; |
||
203 | |||
204 | if ($dbt_select != 'rowid' && $dbt_select != 'id') |
||
205 | $objectid = "'" . $objectid . "'"; |
||
206 | |||
207 | // Features/modules to check |
||
208 | $featuresarray = array($features); |
||
209 | if (preg_match('/&/', $features)) |
||
210 | $featuresarray = explode("&", $features); |
||
211 | else if (preg_match('/\|/', $features)) |
||
212 | $featuresarray = explode("|", $features); |
||
213 | |||
214 | // More subfeatures to check |
||
215 | if (!empty($feature2)) |
||
216 | $feature2 = explode("|", $feature2); |
||
217 | |||
218 | // More parameters |
||
219 | $params = explode('&', $tableandshare); |
||
220 | $dbtablename = (!empty($params[0]) ? $params[0] : ''); |
||
221 | $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename); |
||
222 | |||
223 | $listofmodules = explode(',', $conf->global->MAIN_MODULES_FOR_EXTERNAL); |
||
224 | |||
225 | // Check read permission from module |
||
226 | $readok = 1; |
||
227 | $nbko = 0; |
||
228 | foreach ($featuresarray as $feature) { // first we check nb of test ko |
||
229 | $featureforlistofmodule = $feature; |
||
230 | if ($featureforlistofmodule == 'produit') |
||
231 | $featureforlistofmodule = 'product'; |
||
232 | if (!empty($user->societe_id) && !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 |
||
233 | $readok = 0; |
||
234 | $nbko++; |
||
235 | continue; |
||
236 | } |
||
237 | |||
238 | if ($feature == 'societe') { |
||
239 | if (!$user->rights->societe->lire && !$user->rights->fournisseur->lire) { |
||
240 | $readok = 0; |
||
241 | $nbko++; |
||
242 | } |
||
243 | } else if ($feature == 'contact') { |
||
244 | if (!$user->rights->societe->contact->lire) { |
||
245 | $readok = 0; |
||
246 | $nbko++; |
||
247 | } |
||
248 | } else if ($feature == 'produit|service') { |
||
249 | if (!$user->rights->produit->lire && !$user->rights->service->lire) { |
||
250 | $readok = 0; |
||
251 | $nbko++; |
||
252 | } |
||
253 | } else if ($feature == 'prelevement') { |
||
254 | if (!$user->rights->prelevement->bons->lire) { |
||
255 | $readok = 0; |
||
256 | $nbko++; |
||
257 | } |
||
258 | } else if ($feature == 'cheque') { |
||
259 | if (!$user->rights->banque->cheque) { |
||
260 | $readok = 0; |
||
261 | $nbko++; |
||
262 | } |
||
263 | } else if ($feature == 'projet') { |
||
264 | if (!$user->rights->projet->lire && !$user->rights->projet->all->lire) { |
||
265 | $readok = 0; |
||
266 | $nbko++; |
||
267 | } |
||
268 | } else if (!empty($feature2)) { // This should be used for future changes |
||
269 | $tmpreadok = 1; |
||
270 | foreach ($feature2 as $subfeature) { |
||
271 | if (!empty($subfeature) && empty($user->rights->$feature->$subfeature->lire) && empty($user->rights->$feature->$subfeature->read)) { |
||
272 | $tmpreadok = 0; |
||
273 | } else if (empty($subfeature) && empty($user->rights->$feature->lire) && empty($user->rights->$feature->read)) { |
||
274 | $tmpreadok = 0; |
||
275 | } else { |
||
276 | $tmpreadok = 1; |
||
277 | break; |
||
278 | } // Break is to bypass second test if the first is ok |
||
279 | } |
||
280 | if (!$tmpreadok) { // We found a test on feature that is ko |
||
281 | $readok = 0; // All tests are ko (we manage here the and, the or will be managed later using $nbko). |
||
282 | $nbko++; |
||
283 | } |
||
284 | } else if (!empty($feature) && ($feature != 'user' && $feature != 'usergroup')) { // This is for old permissions |
||
285 | if (empty($user->rights->$feature->lire) && empty($user->rights->$feature->read) && empty($user->rights->$feature->run)) { |
||
286 | $readok = 0; |
||
287 | $nbko++; |
||
288 | } |
||
289 | } |
||
290 | } |
||
291 | |||
292 | // If a or and at least one ok |
||
293 | if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) |
||
294 | $readok = 1; |
||
295 | |||
296 | if (!$readok) |
||
297 | accessforbidden(); |
||
298 | //print "Read access is ok"; |
||
299 | // Check write permission from module (we need to know write permission to create but also to delete drafts record) |
||
300 | $createok = 1; |
||
301 | $nbko = 0; |
||
302 | if (GETPOST('action', 'aZ09') == 'create' || ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete')) { |
||
303 | foreach ($featuresarray as $feature) { |
||
304 | if ($feature == 'contact') { |
||
305 | if (!$user->rights->societe->contact->creer) { |
||
306 | $createok = 0; |
||
307 | $nbko++; |
||
308 | } |
||
309 | } else if ($feature == 'produit|service') { |
||
310 | if (!$user->rights->produit->creer && !$user->rights->service->creer) { |
||
311 | $createok = 0; |
||
312 | $nbko++; |
||
313 | } |
||
314 | } else if ($feature == 'prelevement') { |
||
315 | if (!$user->rights->prelevement->bons->creer) { |
||
316 | $createok = 0; |
||
317 | $nbko++; |
||
318 | } |
||
319 | } else if ($feature == 'commande_fournisseur') { |
||
320 | if (!$user->rights->fournisseur->commande->creer) { |
||
321 | $createok = 0; |
||
322 | $nbko++; |
||
323 | } |
||
324 | } else if ($feature == 'banque') { |
||
325 | if (!$user->rights->banque->modifier) { |
||
326 | $createok = 0; |
||
327 | $nbko++; |
||
328 | } |
||
329 | } else if ($feature == 'cheque') { |
||
330 | if (!$user->rights->banque->cheque) { |
||
331 | $createok = 0; |
||
332 | $nbko++; |
||
333 | } |
||
334 | } else if (!empty($feature2)) { // This should be used |
||
335 | foreach ($feature2 as $subfeature) { |
||
336 | if (empty($user->rights->$feature->$subfeature->creer) && empty($user->rights->$feature->$subfeature->write) && empty($user->rights->$feature->$subfeature->create)) { |
||
337 | $createok = 0; |
||
338 | $nbko++; |
||
339 | } else { |
||
340 | $createok = 1; |
||
341 | break; |
||
342 | } // Break to bypass second test if the first is ok |
||
343 | } |
||
344 | } else if (!empty($feature)) { // This is for old permissions ('creer' or 'write') |
||
345 | //print '<br>feature='.$feature.' creer='.$user->rights->$feature->creer.' write='.$user->rights->$feature->write; |
||
346 | if (empty($user->rights->$feature->creer) && empty($user->rights->$feature->write) && empty($user->rights->$feature->create)) { |
||
347 | $createok = 0; |
||
348 | $nbko++; |
||
349 | } |
||
350 | } |
||
351 | } |
||
352 | |||
353 | // If a or and at least one ok |
||
354 | if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) |
||
355 | $createok = 1; |
||
356 | |||
357 | if (GETPOST('action', 'aZ09') == 'create' && !$createok) |
||
358 | accessforbidden(); |
||
359 | //print "Write access is ok"; |
||
360 | } |
||
361 | |||
362 | // Check create user permission |
||
363 | $createuserok = 1; |
||
364 | if (GETPOST('action', 'aZ09') == 'confirm_create_user' && GETPOST("confirm", 'aZ09') == 'yes') { |
||
365 | if (!$user->rights->user->user->creer) |
||
366 | $createuserok = 0; |
||
367 | |||
368 | if (!$createuserok) |
||
369 | accessforbidden(); |
||
370 | //print "Create user access is ok"; |
||
371 | } |
||
372 | |||
373 | // Check delete permission from module |
||
374 | $deleteok = 1; |
||
375 | $nbko = 0; |
||
376 | if ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete') { |
||
377 | foreach ($featuresarray as $feature) { |
||
378 | if ($feature == 'contact') { |
||
379 | if (!$user->rights->societe->contact->supprimer) |
||
380 | $deleteok = 0; |
||
381 | } |
||
382 | else if ($feature == 'produit|service') { |
||
383 | if (!$user->rights->produit->supprimer && !$user->rights->service->supprimer) |
||
384 | $deleteok = 0; |
||
385 | } |
||
386 | else if ($feature == 'commande_fournisseur') { |
||
387 | if (!$user->rights->fournisseur->commande->supprimer) |
||
388 | $deleteok = 0; |
||
389 | } |
||
390 | else if ($feature == 'banque') { |
||
391 | if (!$user->rights->banque->modifier) |
||
392 | $deleteok = 0; |
||
393 | } |
||
394 | else if ($feature == 'cheque') { |
||
395 | if (!$user->rights->banque->cheque) |
||
396 | $deleteok = 0; |
||
397 | } |
||
398 | else if ($feature == 'ecm') { |
||
399 | if (!$user->rights->ecm->upload) |
||
400 | $deleteok = 0; |
||
401 | } |
||
402 | else if ($feature == 'ftp') { |
||
403 | if (!$user->rights->ftp->write) |
||
404 | $deleteok = 0; |
||
405 | }else if ($feature == 'salaries') { |
||
406 | if (!$user->rights->salaries->delete) |
||
407 | $deleteok = 0; |
||
408 | } |
||
409 | else if ($feature == 'salaries') { |
||
410 | if (!$user->rights->salaries->delete) |
||
411 | $deleteok = 0; |
||
412 | } |
||
413 | else if (!empty($feature2)) { // This should be used for permissions on 2 levels |
||
414 | foreach ($feature2 as $subfeature) { |
||
415 | if (empty($user->rights->$feature->$subfeature->supprimer) && empty($user->rights->$feature->$subfeature->delete)) |
||
416 | $deleteok = 0; |
||
417 | else { |
||
418 | $deleteok = 1; |
||
419 | break; |
||
420 | } // For bypass the second test if the first is ok |
||
421 | } |
||
422 | } else if (!empty($feature)) { // This is used for permissions on 1 level |
||
423 | //print '<br>feature='.$feature.' creer='.$user->rights->$feature->supprimer.' write='.$user->rights->$feature->delete; |
||
424 | if (empty($user->rights->$feature->supprimer) && empty($user->rights->$feature->delete) && empty($user->rights->$feature->run)) |
||
425 | $deleteok = 0; |
||
426 | } |
||
427 | } |
||
428 | |||
429 | // If a or and at least one ok |
||
430 | if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) |
||
431 | $deleteok = 1; |
||
432 | |||
433 | if (!$deleteok && !($isdraft && $createok)) |
||
434 | accessforbidden(); |
||
435 | //print "Delete access is ok"; |
||
436 | } |
||
437 | |||
438 | // If we have a particular object to check permissions on, we check this object |
||
439 | // is linked to a company allowed to $user. |
||
440 | if (!empty($objectid) && $objectid > 0) { |
||
441 | $ok = checkUserAccessToObject($user, $featuresarray, $objectid, $tableandshare, $feature2, $dbt_keyfield, $dbt_select); |
||
442 | return $ok ? 1 : accessforbidden(); |
||
443 | } |
||
444 | |||
445 | return 1; |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Check access by user to object. |
||
450 | * This function is also called by restrictedArea |
||
451 | * |
||
452 | * @param User $user User to check |
||
453 | * @param array $featuresarray Features/modules to check. Example: ('user','service','member','project','task',...) |
||
454 | * @param int|string $objectid Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional). |
||
455 | * @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). |
||
456 | * @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'. |
||
457 | * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional) |
||
458 | * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional) |
||
459 | * @return bool True if user has access, False otherwise |
||
460 | * @see restrictedArea |
||
461 | */ |
||
462 | function checkUserAccessToObject($user, $featuresarray, $objectid = 0, $tableandshare = '', $feature2 = '', $dbt_keyfield = '', $dbt_select = 'rowid') |
||
463 | { |
||
464 | global $db, $conf; |
||
465 | |||
466 | // More parameters |
||
467 | $params = explode('&', $tableandshare); |
||
468 | $dbtablename = (!empty($params[0]) ? $params[0] : ''); |
||
469 | $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename); |
||
470 | |||
471 | foreach ($featuresarray as $feature) { |
||
472 | $sql = ''; |
||
473 | |||
474 | // For backward compatibility |
||
475 | if ($feature == 'member') |
||
476 | $feature = 'adherent'; |
||
477 | if ($feature == 'project') |
||
478 | $feature = 'projet'; |
||
479 | if ($feature == 'task') |
||
480 | $feature = 'projet_task'; |
||
481 | |||
482 | $check = array('adherent', 'banque', 'don', 'user', 'usergroup', 'product', 'produit', 'service', 'produit|service', 'categorie', 'resource'); // Test on entity only (Objects with no link to company) |
||
483 | $checksoc = array('societe'); // Test for societe object |
||
484 | $checkother = array('contact', 'agenda'); // Test on entity and link to third party. Allowed if link is empty (Ex: contacts...). |
||
485 | $checkproject = array('projet', 'project'); // Test for project object |
||
486 | $checktask = array('projet_task'); |
||
487 | $nocheck = array('barcode', 'stock'); // No test |
||
488 | $checkdefault = 'all other not already defined'; // Test on entity and link to third party. Not allowed if link is empty (Ex: invoice, orders...). |
||
489 | // If dbtablename not defined, we use same name for table than module name |
||
490 | if (empty($dbtablename)) { |
||
491 | $dbtablename = $feature; |
||
492 | $sharedelement = (!empty($params[1]) ? $params[1] : $dbtablename); // We change dbtablename, so we set sharedelement too. |
||
493 | } |
||
494 | |||
495 | // Check permission for object with entity |
||
496 | if (in_array($feature, $check)) { |
||
497 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
498 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
499 | if (($feature == 'user' || $feature == 'usergroup') && !empty($conf->multicompany->enabled)) { |
||
500 | if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) { |
||
501 | if ($conf->entity == 1 && $user->admin && !$user->entity) { |
||
502 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
503 | $sql .= " AND dbt.entity IS NOT NULL"; |
||
504 | } else { |
||
505 | $sql .= "," . MAIN_DB_PREFIX . "usergroup_user as ug"; |
||
506 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
507 | $sql .= " AND (ug.fk_user = dbt.rowid"; |
||
508 | $sql .= " AND ug.entity IN (" . getEntity('user') . "))"; |
||
509 | $sql .= " OR dbt.entity = 0"; // Show always superadmin |
||
510 | } |
||
511 | } else { |
||
512 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
513 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
514 | } |
||
515 | } else { |
||
516 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
517 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
518 | } |
||
519 | } else if (in_array($feature, $checksoc)) { // We check feature = checksoc |
||
520 | // If external user: Check permission for external users |
||
521 | if ($user->socid > 0) { |
||
522 | if ($user->socid <> $objectid) |
||
523 | return false; |
||
524 | } |
||
525 | // If internal user: Check permission for internal users that are restricted on their objects |
||
526 | else if (!empty($conf->societe->enabled) && ($user->rights->societe->lire && !$user->rights->societe->client->voir)) { |
||
527 | $sql = "SELECT COUNT(sc.fk_soc) as nb"; |
||
528 | $sql .= " FROM (" . MAIN_DB_PREFIX . "societe_commerciaux as sc"; |
||
529 | $sql .= ", " . MAIN_DB_PREFIX . "societe as s)"; |
||
530 | $sql .= " WHERE sc.fk_soc IN (" . $objectid . ")"; |
||
531 | $sql .= " AND sc.fk_user = " . $user->id; |
||
532 | $sql .= " AND sc.fk_soc = s.rowid"; |
||
533 | $sql .= " AND s.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
534 | } |
||
535 | // If multicompany and internal users with all permissions, check user is in correct entity |
||
536 | else if (!empty($conf->multicompany->enabled)) { |
||
537 | $sql = "SELECT COUNT(s.rowid) as nb"; |
||
538 | $sql .= " FROM " . MAIN_DB_PREFIX . "societe as s"; |
||
539 | $sql .= " WHERE s.rowid IN (" . $objectid . ")"; |
||
540 | $sql .= " AND s.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
541 | } |
||
542 | } else if (in_array($feature, $checkother)) { // Test on entity and link to societe. Allowed if link is empty (Ex: contacts...). |
||
543 | // If external user: Check permission for external users |
||
544 | if ($user->socid > 0) { |
||
545 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
546 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
547 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
548 | $sql .= " AND dbt.fk_soc = " . $user->socid; |
||
549 | } |
||
550 | // If internal user: Check permission for internal users that are restricted on their objects |
||
551 | else if (!empty($conf->societe->enabled) && ($user->rights->societe->lire && !$user->rights->societe->client->voir)) { |
||
552 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
553 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
554 | $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON dbt.fk_soc = sc.fk_soc AND sc.fk_user = '" . $user->id . "'"; |
||
555 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
556 | $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 |
||
557 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
558 | } |
||
559 | // If multicompany and internal users with all permissions, check user is in correct entity |
||
560 | else if (!empty($conf->multicompany->enabled)) { |
||
561 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
562 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
563 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
564 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
565 | } |
||
566 | } else if (in_array($feature, $checkproject)) { |
||
567 | if (!empty($conf->projet->enabled) && empty($user->rights->projet->all->lire)) { |
||
568 | include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; |
||
569 | $projectstatic = new Project($db); |
||
570 | $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0); |
||
571 | $tmparray = explode(',', $tmps); |
||
572 | if (!in_array($objectid, $tmparray)) |
||
573 | return false; |
||
574 | } |
||
575 | else { |
||
576 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
577 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
578 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
579 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
580 | } |
||
581 | } else if (in_array($feature, $checktask)) { |
||
582 | if (!empty($conf->projet->enabled) && empty($user->rights->projet->all->lire)) { |
||
583 | $task = new Task($db); |
||
584 | $task->fetch($objectid); |
||
585 | |||
586 | include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; |
||
587 | $projectstatic = new Project($db); |
||
588 | $tmps = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, 0); |
||
589 | $tmparray = explode(',', $tmps); |
||
590 | if (!in_array($task->fk_project, $tmparray)) |
||
591 | return false; |
||
592 | } |
||
593 | else { |
||
594 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
595 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
596 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
597 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
598 | } |
||
599 | } else if (!in_array($feature, $nocheck)) { // By default (case of $checkdefault), we check on object entity + link to third party on field $dbt_keyfield |
||
600 | // If external user: Check permission for external users |
||
601 | if ($user->socid > 0) { |
||
602 | if (empty($dbt_keyfield)) |
||
603 | dol_print_error('', 'Param dbt_keyfield is required but not defined'); |
||
604 | $sql = "SELECT COUNT(dbt." . $dbt_keyfield . ") as nb"; |
||
605 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
606 | $sql .= " WHERE dbt.rowid IN (" . $objectid . ")"; |
||
607 | $sql .= " AND dbt." . $dbt_keyfield . " = " . $user->socid; |
||
608 | } |
||
609 | // If internal user: Check permission for internal users that are restricted on their objects |
||
610 | else if (!empty($conf->societe->enabled) && ($user->rights->societe->lire && !$user->rights->societe->client->voir)) { |
||
611 | if (empty($dbt_keyfield)) |
||
612 | dol_print_error('', 'Param dbt_keyfield is required but not defined'); |
||
613 | $sql = "SELECT COUNT(sc.fk_soc) as nb"; |
||
614 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
615 | $sql .= ", " . MAIN_DB_PREFIX . "societe as s"; |
||
616 | $sql .= ", " . MAIN_DB_PREFIX . "societe_commerciaux as sc"; |
||
617 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
618 | $sql .= " AND sc.fk_soc = dbt." . $dbt_keyfield; |
||
619 | $sql .= " AND dbt." . $dbt_keyfield . " = s.rowid"; |
||
620 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
621 | $sql .= " AND sc.fk_user = " . $user->id; |
||
622 | } |
||
623 | // If multicompany and internal users with all permissions, check user is in correct entity |
||
624 | else if (!empty($conf->multicompany->enabled)) { |
||
625 | $sql = "SELECT COUNT(dbt." . $dbt_select . ") as nb"; |
||
626 | $sql .= " FROM " . MAIN_DB_PREFIX . $dbtablename . " as dbt"; |
||
627 | $sql .= " WHERE dbt." . $dbt_select . " IN (" . $objectid . ")"; |
||
628 | $sql .= " AND dbt.entity IN (" . getEntity($sharedelement, 1) . ")"; |
||
629 | } |
||
630 | } |
||
631 | |||
632 | if ($sql) { |
||
633 | $resql = $db->query($sql); |
||
634 | if ($resql) { |
||
635 | $obj = $db->fetch_object($resql); |
||
636 | if (!$obj || $obj->nb < count(explode(',', $objectid))) |
||
637 | return false; |
||
638 | } |
||
639 | else { |
||
640 | return false; |
||
641 | } |
||
642 | } |
||
643 | } |
||
644 | return true; |
||
645 | } |
||
646 | |||
647 | /** |
||
648 | * Show a message to say access is forbidden and stop program |
||
649 | * Calling this function terminate execution of PHP. |
||
650 | * |
||
651 | * @param string $message Force error message |
||
652 | * @param int $printheader Show header before |
||
653 | * @param int $printfooter Show footer after |
||
654 | * @param int $showonlymessage Show only message parameter. Otherwise add more information. |
||
655 | * @return void |
||
656 | */ |
||
657 | function accessforbidden($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0) |
||
658 | { |
||
659 | global $conf, $db, $user, $langs; |
||
660 | if (!is_object($langs)) { |
||
661 | include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php'; |
||
662 | $langs = new Translate('', $conf); |
||
663 | $langs->setDefaultLang(); |
||
664 | } |
||
665 | |||
666 | $langs->load("errors"); |
||
667 | |||
668 | if ($printheader) { |
||
669 | if (function_exists("llxHeader")) |
||
670 | llxHeader(''); |
||
671 | else if (function_exists("llxHeaderVierge")) |
||
672 | llxHeaderVierge(''); |
||
673 | } |
||
674 | print '<div class="error">'; |
||
675 | if (!$message) |
||
676 | print $langs->trans("ErrorForbidden"); |
||
677 | else |
||
678 | print $message; |
||
679 | print '</div>'; |
||
680 | print '<br>'; |
||
681 | if (empty($showonlymessage)) { |
||
682 | if ($user->login) { |
||
683 | print $langs->trans("CurrentLogin") . ': <font class="error">' . $user->login . '</font><br>'; |
||
684 | print $langs->trans("ErrorForbidden2", $langs->trans("Home"), $langs->trans("Users")); |
||
685 | } else { |
||
686 | print $langs->trans("ErrorForbidden3"); |
||
687 | } |
||
688 | } |
||
689 | if ($printfooter && function_exists("llxFooter")) |
||
690 | llxFooter(); |
||
691 | exit(0); |
||
692 | } |
||
693 | } |
||
694 |