Test Failed
Push — develop ( 9e1540...9681bd )
by Florian
01:47
created

AuthorizationService::can()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 23
ccs 10
cts 10
cp 1
rs 9.5222
cc 5
nc 7
nop 3
crap 5
1
<?php
2
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
17
declare(strict_types=1);
18
19
namespace Phauthentic\Authorization;
20
21
use Phauthentic\Authorization\Policy\BeforePolicyInterface;
22
use Phauthentic\Authorization\Policy\Exception\MissingMethodException;
23
use Phauthentic\Authorization\Policy\ResolverInterface;
24
use Phauthentic\Authorization\Policy\Result;
25
use Phauthentic\Authorization\Policy\ResultInterface;
26
use RuntimeException;
27
28
/**
29
 * Authorization Service
30
 */
31
class AuthorizationService implements AuthorizationServiceInterface
32
{
33
    /**
34
     * Authorization policy resolver.
35
     *
36
     * @var \Phauthentic\Authorization\Policy\ResolverInterface
37
     */
38
    protected $resolver;
39
40
    /**
41
     * Track whether or not authorization was checked.
42
     *
43
     * @var bool
44
     */
45
    protected $authorizationChecked = false;
46
47
    /**
48 15
     * Constructor
49
     *
50 15
     * @param \Phauthentic\Authorization\Policy\ResolverInterface $resolver Authorization policy resolver.
51 15
     */
52
    public function __construct(ResolverInterface $resolver)
53
    {
54
        $this->resolver = $resolver;
55
    }
56 11
57
    /**
58 11
     * {@inheritDoc}
59 11
     */
60
    public function can(?IdentityInterface $user, string $action, $resource): ResultInterface
61 11
    {
62 5
        $this->authorizationChecked = true;
63
        $policy = $this->resolver->getPolicy($resource);
64 5
        if ($policy instanceof BeforePolicyInterface) {
65 4
            $result = $policy->before($user, $resource, $action);
66
67
            if ($result) {
68
                return $result;
69 7
            }
70
        }
71 6
72
        $handler = $this->getCanHandler($policy, $action);
73
        $result = $handler($user, $resource);
74
        if (is_bool($result)) {
75
            return new Result($result);
76
        }
77 3
78
        if ($result instanceof ResultInterface) {
79 3
            return $result;
80 3
        }
81 3
82
        throw new RuntimeException(sprintf('Policy action handler must return instance of `%s` or `bool`.', ResultInterface::class));
83 2
    }
84
85
    /**
86
     * {@inheritDoc}
87
     */
88
    public function applyScope(?IdentityInterface $user, string $action, $resource)
89
    {
90
        $this->authorizationChecked = true;
91
        $policy = $this->resolver->getPolicy($resource);
92
        $handler = $this->getScopeHandler($policy, $action);
93
        return $handler($user, $resource);
94 7
    }
95
96 7
    /**
97
     * Returns a policy action handler.
98 7
     *
99 1
     * @param mixed $policy Policy object.
100
     * @param string $action Action name.
101
     * @return callable
102 6
     * @throws \Phauthentic\Authorization\Policy\Exception\MissingMethodException
103
     */
104
    protected function getCanHandler($policy, $action): callable
105
    {
106
        $method = 'can' . ucfirst($action);
107
        if (!method_exists($policy, $method) && !method_exists($policy, '__call')) {
108
            throw (new MissingMethodException())->setMessageVars([$method, $action, get_class($policy)]);
109
        }
110
111
        return [$policy, $method];
112
    }
113 3
114
    /**
115 3
     * Returns a policy scope action handler.
116
     *
117 3
     * @param mixed $policy Policy object.
118 1
     * @param string $action Action name.
119
     * @return callable
120
     * @throws \Phauthentic\Authorization\Policy\Exception\MissingMethodException
121 2
     */
122
    protected function getScopeHandler($policy, $action): callable
123
    {
124
        $method = 'scope' . ucfirst($action);
125
        if (!method_exists($policy, $method)) {
126
            throw (new MissingMethodException())->setMessageVars([$method, $action, get_class($policy)]);
127 3
        }
128
129 3
        return [$policy, $method];
130
    }
131
132
    /**
133
     * {@inheritDoc}
134
     */
135 1
    public function authorizationChecked(): bool
136
    {
137 1
        return $this->authorizationChecked;
138
    }
139 1
140
    /**
141
     * {@inheritDoc}
142
     */
143
    public function skipAuthorization(): AuthorizationServiceInterface
144
    {
145
        $this->authorizationChecked = true;
146
        return $this;
147
    }
148
}
149