Ajde_Acl::getUserId()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
Unused Code introduced by
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...
Unused Code introduced by
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
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
Unused Code introduced by
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...
Unused Code introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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