Passed
Push — master ( abc2ba...40955b )
by Anton
02:23
created

GuardInterceptor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Domain;
13
14
use Doctrine\Common\Annotations\AnnotationReader;
15
use Spiral\Core\CoreInterceptorInterface;
16
use Spiral\Core\CoreInterface;
17
use Spiral\Core\Exception\ControllerException;
18
use Spiral\Core\Exception\InterceptorException;
19
use Spiral\Domain\Annotation\Guarded;
20
use Spiral\Domain\Annotation\GuardNamespace;
21
use Spiral\Security\GuardInterface;
22
23
class GuardInterceptor implements CoreInterceptorInterface
24
{
25
    /** @var GuardInterface */
26
    private $guard;
27
28
    /** @var array */
29
    private $permissionCache = [];
30
31
    /**
32
     * @param GuardInterface $guard
33
     */
34
    public function __construct(GuardInterface $guard)
35
    {
36
        $this->guard = $guard;
37
    }
38
39
    /**
40
     * @inheritDoc
41
     */
42
    public function process(string $controller, string $action, array $parameters, CoreInterface $core)
43
    {
44
        $permission = $this->getPermissions($controller, $action);
45
46
        if ($permission !== null && !$this->guard->allows($permission[0], $parameters)) {
47
            throw new ControllerException(
48
                sprintf(
49
                    'Unauthorized permission `%s` for action `%s`->`%s`',
50
                    $permission[0],
51
                    $controller,
52
                    $action
53
                ),
54
                $permission[1]
55
            );
56
        }
57
58
        return $core->callAction($controller, $action, $parameters);
59
    }
60
61
    /**
62
     * Get method RBAC permission if any. Automatically merges with controller namespace.
63
     *
64
     * @param string $controller
65
     * @param string $action
66
     * @return array|null
67
     *
68
     * @throws \Doctrine\Common\Annotations\AnnotationException
69
     */
70
    private function getPermissions(string $controller, string $action): ?array
71
    {
72
        $key = sprintf('%s:%s', $controller, $action);
73
        if (array_key_exists($key, $this->permissionCache)) {
74
            return $this->permissionCache[$key];
75
        }
76
77
        $this->permissionCache[$key] = null;
78
        try {
79
            $method = new \ReflectionMethod($controller, $action);
80
        } catch (\ReflectionException $e) {
81
            return [];
82
        }
83
84
        $reader = new AnnotationReader();
85
86
        /** @var GuardNamespace $guardNamespace */
87
        $guardNamespace = $reader->getClassAnnotation($method->getDeclaringClass(), GuardNamespace::class);
88
89
        /** @var Guarded $guarded */
90
        $guarded = $reader->getMethodAnnotation($method, Guarded::class);
91
92
        if ($guarded === null) {
93
            return null;
94
        }
95
96
        if ($guarded->permission === null && $guardNamespace === null) {
97
            throw new InterceptorException(
98
                'Unable to apply @Guarded annotation without specified permission name or @GuardNamespace'
99
            );
100
        }
101
102
        $permission = [
103
            $guarded->permission ?? $action,
104
            ControllerException::FORBIDDEN
105
        ];
106
107
        if ($guardNamespace !== null) {
108
            $permission[0] = sprintf('%s.%s', $guardNamespace->namespace, $permission[0]);
109
        }
110
111
        switch ($guarded->else) {
112
            case 'badAction':
113
                $permission[1] = ControllerException::BAD_ACTION;
114
                break;
115
            case 'notFound':
116
                $permission[1] = ControllerException::NOT_FOUND;
117
                break;
118
            case 'error':
119
                $permission[1] = ControllerException::ERROR;
120
                break;
121
        }
122
123
        $this->permissionCache[$key] = $permission;
124
125
        return $permission;
126
    }
127
}
128