Test Failed
Push — develop ( 60bbcf...b9e10c )
by Florian
01:41
created

AuthorizationService::can()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 38
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 21
dl 0
loc 38
ccs 14
cts 14
cp 1
rs 8.6506
c 1
b 0
f 0
cc 7
nc 9
nop 3
crap 7
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
5
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
12
 * @link          https://cakephp.org CakePHP(tm) Project
13
 * @since         1.0.0
14
 * @license       https://opensource.org/licenses/mit-license.php MIT License
15
 */
16
namespace Phauthentic\Authorization;
17
18
use Phauthentic\Authorization\Policy\BeforePolicyInterface;
19
use Phauthentic\Authorization\Policy\Exception\MissingMethodException;
20
use Phauthentic\Authorization\Policy\ResolverInterface;
21
use Phauthentic\Authorization\Policy\Result;
22
use Phauthentic\Authorization\Policy\ResultInterface;
23
use RuntimeException;
24
25
/**
26
 * Authorization Service
27
 */
28
class AuthorizationService implements AuthorizationServiceInterface
29
{
30
    /**
31
     * Authorization policy resolver.
32
     *
33
     * @var \Phauthentic\Authorization\Policy\ResolverInterface
34
     */
35
    protected $resolver;
36
37
    /**
38
     * Track whether or not authorization was checked.
39
     *
40
     * @var bool
41
     */
42
    protected $authorizationChecked = false;
43
44
    /**
45
     * Constructor
46
     *
47
     * @param \Phauthentic\Authorization\Policy\ResolverInterface $resolver Authorization policy resolver.
48 15
     */
49
    public function __construct(ResolverInterface $resolver)
50 15
    {
51 15
        $this->resolver = $resolver;
52
    }
53
54
    /**
55
     * {@inheritDoc}
56 11
     */
57
    public function can(?IdentityInterface $user, string $action, $resource): ResultInterface
58 11
    {
59 11
        $this->authorizationChecked = true;
60
        $policy = $this->resolver->getPolicy($resource);
61 11
62 5
        if ($policy instanceof BeforePolicyInterface) {
63
            $result = $policy->before($user, $resource, $action);
64 5
65 4
            if (is_bool($result)) {
0 ignored issues
show
introduced by
The condition is_bool($result) is always false.
Loading history...
66
                return new Result($result);
67
            }
68
69 7
            if ($result instanceof ResultInterface) {
70
                return $result;
71 6
            }
72
73
            if ($result !== null) {
0 ignored issues
show
introduced by
The condition $result !== null is always false.
Loading history...
74
                throw new RuntimeException(sprintf(
75
                    'Pre-authorization check must return instance of `%s`, `bool` or `null`.',
76
                    ResultInterface::class
77 3
                ));
78
            }
79 3
        }
80 3
81 3
        $handler = $this->getCanHandler($policy, $action);
82
        $result = $handler($user, $resource);
83 2
84
        if (is_bool($result)) {
85
            return new Result($result);
86
        }
87
88
        if ($result instanceof ResultInterface) {
89
            return $result;
90
        }
91
92
        throw new RuntimeException(sprintf(
93
            'Policy action handler must return instance of `%s` or `bool`.',
94 7
            ResultInterface::class
95
        ));
96 7
    }
97
98 7
    /**
99 1
     * {@inheritDoc}
100
     */
101
    public function applyScope(?IdentityInterface $user, string $action, $resource)
102 6
    {
103
        $this->authorizationChecked = true;
104
        $policy = $this->resolver->getPolicy($resource);
105
        $handler = $this->getScopeHandler($policy, $action);
106
107
        return $handler($user, $resource);
108
    }
109
110
    /**
111
     * Returns a policy action handler.
112
     *
113 3
     * @param mixed $policy Policy object.
114
     * @param string $action Action name.
115 3
     * @return callable
116
     * @throws \Phauthentic\Authorization\Policy\Exception\MissingMethodException
117 3
     */
118 1
    protected function getCanHandler($policy, $action): callable
119
    {
120
        $method = 'can' . ucfirst($action);
121 2
122
        if (!method_exists($policy, $method) && !method_exists($policy, '__call')) {
123
            throw (new MissingMethodException())->setMessageVars([$method, $action, get_class($policy)]);
124
        }
125
126
        return [$policy, $method];
127 3
    }
128
129 3
    /**
130
     * Returns a policy scope action handler.
131
     *
132
     * @param mixed $policy Policy object.
133
     * @param string $action Action name.
134
     * @return callable
135 1
     * @throws \Phauthentic\Authorization\Policy\Exception\MissingMethodException
136
     */
137 1
    protected function getScopeHandler($policy, $action): callable
138
    {
139 1
        $method = 'scope' . ucfirst($action);
140
141
        if (!method_exists($policy, $method)) {
142
            throw (new MissingMethodException())->setMessageVars([$method, $action, get_class($policy)]);
143
        }
144
145
        return [$policy, $method];
146
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151
    public function authorizationChecked(): bool
152
    {
153
        return $this->authorizationChecked;
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159
    public function skipAuthorization(): AuthorizationServiceInterface
160
    {
161
        $this->authorizationChecked = true;
162
163
        return $this;
164
    }
165
}
166