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')) {
|
|
|
|
|
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
|
|
|
|