Issues (1919)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

lib/Ajde/Acl.php (17 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
class Ajde_Acl extends Ajde_Model
4
{
5
    public static $log = [];
6
    public static $access = null;
7
8
    protected $_autoloadParents = false;
9
10
    private static $_aclCollectionCache = [];
11
    private static $_aclRulesCache = [];
12
13
    private static $_user;
14
15
    public static function getLog()
16
    {
17
        return self::$log;
18
    }
19
20
    private static function getUser()
21
    {
22
        if (!isset(self::$_user)) {
23
            self::$_user = Ajde_User::getLoggedIn();
24
        }
25
26
        return self::$_user;
27
    }
28
29
    private static function getUserId()
30
    {
31
        $user = self::getUser();
32
        if ($user !== false) {
33
            return $user->getPK();
34
        }
35
36
        return -1;
37
    }
38
39
    private static function getUsergroupId()
40
    {
41
        $user = self::getUser();
42
        if ($user !== false) {
43
            return (string) $user->getUsergroup();
44
        }
45
46
        return -1;
47
    }
48
49
    /**
50
     * @return AclCollection
51
     */
52
    private static function getAclCollection()
53
    {
54
        return new AclCollection();
55
    }
56
57
    /**
58
     * @return AclCollection
59
     */
60
    private static function getAclModel()
61
    {
62
        return new AclModel();
63
    }
64
65
    /**
66
     * @return Ajde_Acl|bool
67
     */
68
    public static function lookup($usergroup, $entity, $module, $action = '*', $extra = '*', $permission = false)
69
    {
70
        $acl = self::getAclModel();
71
        $type = ($usergroup === 'public' ? 'public' : 'usergroup');
72
        $fields = [
73
            'entity' => $entity,
74
            'type'   => $type,
75
            'module' => $module,
76
            'action' => $action,
77
            'extra'  => $extra,
78
        ];
79
        if (is_numeric($usergroup)) {
80
            $fields['usergroup'] = $usergroup;
81
        }
82
        if ($permission !== false) {
83
            $fields['permission'] = $permission;
84
        }
85
        if ($acl->loadByFields($fields)) {
86
            return $acl;
87
        }
88
89
        return false;
90
    }
91
92 View Code Duplication
    public static function removePermission(
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
        $usergroup,
94
        $entity,
95
        $module,
96
        $action = '*',
97
        $extra = '*',
98
        $permission = false
99
    ) {
100
        $acl = self::lookup($usergroup, $entity, $module, $action, $extra, $permission);
101
        if ($acl) {
102
            return $acl->delete();
103
        }
104
105
        return false;
106
    }
107
108 View Code Duplication
    public static function removeModelPermissions($usergroup, $model, $extra = '*')
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
109
    {
110
        $collection = self::getModelActions($usergroup, $model, $extra);
111
        $success = true;
112
        foreach ($collection as $acl) {
113
            $success = $success * $acl->delete();
114
        }
115
116
        return $success == true;
117
    }
118
119
    public static function addPermission($permission, $entity, $usergroup, $module, $action = '*', $extra = '*')
120
    {
121
        $acl = self::getAclModel();
122
        $type = ($usergroup === 'public' ? 'public' : 'usergroup');
123
        $values = [
124
            'entity'     => $entity,
125
            'type'       => $type,
126
            'module'     => $module,
127
            'action'     => $action,
128
            'extra'      => $extra,
129
            'permission' => $permission,
130
        ];
131
        if (is_numeric($usergroup)) {
132
            $values['usergroup'] = $usergroup;
133
        }
134
        $acl->populate($values);
135
136
        return $acl->insert();
137
    }
138
139
    /**
140
     * @return AclCollection
141
     */
142
    public static function getModelActions($usergroup, $model, $extra = '*', $permission = false)
143
    {
144
        $collection = self::getAclCollection();
145
        $collection->addFilter(new Ajde_Filter_Where('entity', Ajde_Filter::FILTER_EQUALS, 'model'));
146
        $type = ($usergroup === 'public' ? 'public' : 'usergroup');
147
        if (is_numeric($usergroup)) {
148
            $collection->addFilter(new Ajde_Filter_Where('usergroup', Ajde_Filter::FILTER_EQUALS, $usergroup));
149
        }
150
        $collection->addFilter(new Ajde_Filter_Where('type', Ajde_Filter::FILTER_EQUALS, $type));
151
        $collection->addFilter(new Ajde_Filter_Where('module', Ajde_Filter::FILTER_EQUALS, $model));
152
        if ($permission !== false) {
153
            $collection->addFilter(new Ajde_Filter_Where('permission', Ajde_Filter::FILTER_EQUALS, $permission));
154
        }
155
        $collection->addFilter(new Ajde_Filter_Where('extra', Ajde_Filter::FILTER_EQUALS, $extra));
156
157
        return $collection;
158
    }
159
160 View Code Duplication
    public static function getModelActionsAsArray($usergroup, $model, $extra = '*', $permission = false)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
161
    {
162
        $collection = self::getModelActions($usergroup, $model, $extra, $permission);
163
        $actions = [];
164
        foreach ($collection as $acl) {
165
            $actions[] = $acl->getAction();
166
        }
167
168
        return $actions;
169
    }
170
171 View Code Duplication
    public static function getPagePermission($usergroup, $module, $action = '*', $extra = '*')
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
    {
173
        $acl = self::lookup($usergroup, 'page', $module, $action, $extra);
174
        if ($acl) {
175
            return $acl->getPermission();
0 ignored issues
show
Documentation Bug introduced by
The method getPermission does not exist on object<AclModel>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
176
        }
177
178
        return false;
179
    }
180
181
    public static function validateController($module, $action, $extra)
182
    {
183
        $access = self::validatePage($module, $action, $extra);
184
        self::$access = $access;
185
186
        return $access;
187
    }
188
189
    public static function validatePage($module, $action, $extra)
190
    {
191
        return self::doValidation('page', $module, $action, $extra);
192
    }
193
194
    private static function validateOwner($uid, $gid)
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
The parameter $uid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $gid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
195
    {
196
        return false;
197
    }
198
199
    private static function validateParent($uid, $gid)
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
The parameter $uid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $gid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
200
    {
201
        return false;
202
    }
203
204
    /**
205
     * @param string $entity
206
     * @param string $module
207
     * @param string $action
208
     * @param string $extra
209
     * @param bool   $ownerCallback
210
     * @param bool   $parentCallback
211
     * @param bool   $determineWildcard
212
     *
213
     * @return bool
214
     */
215
    public static function doValidation(
216
        $entity,
217
        $module,
218
        $action,
219
        $extra,
220
        $ownerCallback = false,
221
        $parentCallback = false,
222
        $determineWildcard = false
223
    ) {
224
        $uid = self::getUserId();
225
        $usergroup = self::getUsergroupId();
226
227
        $isWildcard = false;
228
229
        $callbackHash = '';
230
        if ($ownerCallback !== false && $parentCallback !== false) {
231
            $callbackHash = md5(get_class($ownerCallback[0]).get_class($parentCallback[0]).$ownerCallback[1].$parentCallback[1]);
232
        }
233
        $validationHash = md5($entity.'/'.$module.'/'.$action.'/'.$extra.'/'.$uid.'/'.$usergroup.'/'.$callbackHash);
234
235
        if (isset(self::$_aclRulesCache[$validationHash])) {
236
            $orderedRules = self::$_aclRulesCache[$validationHash];
237
        } else {
238
239
            /*
240
             * Allright, this is how things go down here:
241
             * We want to check for at least one allowed or owner record in this direction:
242
             *
243
             * 1. Wildcard usergroup AND module/action
244
             * 2. Wildcard user AND module/action
245
             * 3. Specific usergroup AND module/action
246
             * 4. Specific user AND module/action
247
             * 5. Public AND module/action
248
             *
249
             * Module/action goes down in this order:
250
             *
251
             * A1. Wildcard module AND wildcard action
252
             * A2. Wildcard module AND wildcard action (with extra)
253
             * B1. Wildcard module AND specific action
254
             * B2. Wildcard module AND specific action (with extra)
255
             * C1. Specific module AND wildcard action
256
             * C2. Specific module AND wildcard action (with extra)
257
             * D1. Specific module AND specific action
258
             * D2. Specific module AND specific action (with extra)
259
             *
260
             * This makes for 20 checks.
261
             *
262
             * If a denied record is found and no allowed or owner record is present
263
             * further down, deny access.
264
             */
265
266
            $access = null;
267
268
            $moduleAction = [
269
                'A1' => [
270
                    'module' => '*',
271
                    'action' => '*',
272
                    'extra'  => '*',
273
                ],
274
                'A2' => [
275
                    'module' => '*',
276
                    'action' => '*',
277
                    'extra'  => $extra,
278
                ],
279
                'B1' => [
280
                    'module' => '*',
281
                    'action' => $action,
282
                    'extra'  => '*',
283
                ],
284
                'B2' => [
285
                    'module' => '*',
286
                    'action' => $action,
287
                    'extra'  => $extra,
288
                ],
289
                'C1' => [
290
                    'module' => $module,
291
                    'action' => '*',
292
                    'extra'  => '*',
293
                ],
294
                'C2' => [
295
                    'module' => $module,
296
                    'action' => '*',
297
                    'extra'  => $extra,
298
                ],
299
                'D1' => [
300
                    'module' => $module,
301
                    'action' => $action,
302
                    'extra'  => '*',
303
                ],
304
                'D2' => [
305
                    'module' => $module,
306
                    'action' => $action,
307
                    'extra'  => $extra,
308
                ],
309
            ];
310
311
            $userGroup = [
312
                1 => ['usergroup', null],
313
                2 => ['user', null],
314
                3 => ['usergroup', $usergroup],
315
                4 => ['user', $uid],
316
                5 => ['public', null],
317
            ];
318
319
            /*
320
             * Allright, let's prepare the SQL!
321
             */
322
323
            // From cache
324
            if (isset(self::$_aclCollectionCache[$entity])) {
325
                $rules = self::$_aclCollectionCache[$entity];
326
                // Load collection
327
            } else {
328
                $rules = self::getAclCollection();
329
                $rules->reset();
330
331
                //		$moduleActionWhereGroup = new Ajde_Filter_WhereGroup(Ajde_Query::OP_AND);
332
                //		foreach($moduleAction as $moduleActionPart) {
333
                //			$group = new Ajde_Filter_WhereGroup(Ajde_Query::OP_OR);
334
                //			foreach($moduleActionPart as $key => $value) {
335
                //				$group->addFilter(new Ajde_Filter_Where($key, Ajde_Filter::FILTER_EQUALS, $value, Ajde_Query::OP_AND));
336
                //			}
337
                //			$moduleActionWhereGroup->addFilter($group);
338
                //		}
339
                //
340
                //		foreach($userGroup as $userGroupPart) {
341
                //			$group = new Ajde_Filter_WhereGroup(Ajde_Query::OP_OR);
342
                //			$comparison = is_null($userGroupPart[1]) ? Ajde_Filter::FILTER_IS : Ajde_Filter::FILTER_EQUALS;
343
                //			$group->addFilter(new Ajde_Filter_Where('type', Ajde_Filter::FILTER_EQUALS, $userGroupPart[0], Ajde_Query::OP_AND));
344
                //			if ($userGroupPart[0] !== 'public') {
345
                //				$group->addFilter(new Ajde_Filter_Where($userGroupPart[0], $comparison, $userGroupPart[1], Ajde_Query::OP_AND));
346
                //			}
347
                //			$group->addFilter($moduleActionWhereGroup, Ajde_Query::OP_AND);
348
                //			$rules->addFilter($group, Ajde_Query::OP_OR);
349
                //		}
350
351
                // add the entity filter
352
                $rules->filterByEntity($entity);
353
354
                // do the load
355
                $rules->load();
356
357
                self::$_aclCollectionCache[$entity] = $rules;
358
            }
359
360
            /*
361
             * Oempfff... now let's traverse and set the order
362
             *
363
             * Update: It seems that we can just load the entire ACL table in the collection
364
             * and use this traversal to find matching rules instead of executing this
365
             * overly complicated SQL query constructed above...
366
             */
367
368
            $orderedRules = [];
369
            foreach ($userGroup as $ugpKey => $userGroupPart) {
370
                $type = $userGroupPart[0];
371
                $ugId = $userGroupPart[1];
372
                foreach ($moduleAction as $maKey => $moduleActionPart) {
373
                    $module = $moduleActionPart['module'];
374
                    $action = $moduleActionPart['action'];
375
                    $extra = $moduleActionPart['extra'];
376
                    $rule = $rules->findRule($type, $ugId, $module, $action, $extra);
377
                    if ($rule !== false) {
378
                        $orderedRules[$ugpKey.$maKey] = $rule;
379
                    }
380
                }
381
            }
382
383
            self::$_aclRulesCache[$validationHash] = $orderedRules;
384
        }
385
386
        /*
387
         * Finally, determine access
388
         */
389
        $extra = ($extra !== '*' && $extra !== '') ? ' ('.$extra.')' : '';
390
        foreach ($orderedRules as $key => $rule) {
391
            if ($rule->type === 'public' && self::getUser() === false) {
392
                switch ($rule->permission) {
393 View Code Duplication
                    case 'allow':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
394
                        self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' allows access for '.$module.'/'.$action.$extra.' (public)';
395
                        $access = true;
396
                        $isWildcard = $rule->extra == '*';
397
                        break 2;
398
                    case 'deny':
399 View Code Duplication
                    default:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
                        self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' denies access for '.$module.'/'.$action.$extra.' (public)';
401
                        $access = false;
402
                        break;
403
                }
404
            } else {
405
                if ($rule->type !== 'public') {
406
                    if (self::getUser()) {
407
                        switch ($rule->permission) {
408 View Code Duplication
                            case 'deny':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
409
                                self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' denies access for '.$module.'/'.$action.$extra;
410
                                $access = false;
411
                                break;
412 View Code Duplication
                            case 'own':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
413
                                if (call_user_func_array($ownerCallback, [$uid, $usergroup])) {
414
                                    self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' allows access for '.$module.'/'.$action.$extra.' (owner)';
415
                                    $access = true;
416
                                    $isWildcard = $rule->extra == '*';
417
                                    break 2;
418
                                } else {
419
                                    self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' denies access for '.$module.'/'.$action.$extra.' (owner)';
420
                                    // TODO: or inherit?
421
                                    $access = false;
422
                                }
423
                                break;
424 View Code Duplication
                            case 'parent':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
425
                                if (call_user_func_array($parentCallback, [$uid, $usergroup])) {
426
                                    self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' allows access for '.$module.'/'.$action.$extra.' (parent)';
427
                                    $access = true;
428
                                    $isWildcard = $rule->extra == '*';
429
                                    break 2;
430
                                } else {
431
                                    self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' denies access for '.$module.'/'.$action.$extra.' (parent)';
432
                                    // TODO: or inherit?
433
                                    $access = false;
434
                                }
435
                                break;
436 View Code Duplication
                            case 'allow':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
437
                                self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' allows access for '.$module.'/'.$action.$extra;
438
                                $access = true;
439
                                $isWildcard = $rule->extra == '*';
440
                                break 2;
441
                        }
442
                    } else {
443
                        self::$log[] = $key.' match with ACL rule id '.$rule->getPK().' denies access for '.$module.'/'.$action.$extra.' (not logged in)';
444
                        $access = false;
445
                    }
446
                }
447
            }
448
        }
449
450
        if (!isset($access)) {
451
            self::$log[] = 'No match in ACL rules denies access for '.$module.'/'.$action.$extra;
452
            $access = false;
453
        }
454
455
        if ($determineWildcard) {
456
            return $isWildcard;
457
        }
458
459
        return $access;
460
    }
461
}
462