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 AlSecurity
|
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) && Globals::$conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_hash')) {
|
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(Globals::$conf->global->MAIN_SECURITY_HASH_ALGO) && Globals::$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(',', Globals::$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(Globals::$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(Globals::$conf->multicompany->enabled)) {
|
500
|
|
|
if (!empty(Globals::$conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
|
501
|
|
|
if (Globals::$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(Globals::$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(Globals::$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(Globals::$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(Globals::$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(Globals::$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(Globals::$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(Globals::$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(Globals::$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
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.