Passed
Push — develop ( a21492...3c31ee )
by Florian
02:49 queued 02:15
created

AuthorizationService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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\ResultInterface;
22
use RuntimeException;
23
24
/**
25
 * Authorization Service
26
 */
27
class AuthorizationService implements AuthorizationServiceInterface
28
{
29
    /**
30
     * Authorization policy resolver.
31
     *
32
     * @var \Phauthentic\Authorization\Policy\ResolverInterface
33
     */
34
    protected $resolver;
35
36
    /**
37
     * Track whether or not authorization was checked.
38
     *
39
     * @var bool
40
     */
41
    protected $authorizationChecked = false;
42
43
    /**
44
     * Constructor
45
     *
46
     * @param \Phauthentic\Authorization\Policy\ResolverInterface $resolver Authorization policy resolver.
47
     */
48 15
    public function __construct(ResolverInterface $resolver)
49
    {
50 15
        $this->resolver = $resolver;
51 15
    }
52
53
    /**
54
     * {@inheritDoc}
55
     */
56 11
    public function can(?IdentityInterface $user, string $action, $resource)
57
    {
58 11
        $this->authorizationChecked = true;
59 11
        $policy = $this->resolver->getPolicy($resource);
60
61 11
        if ($policy instanceof BeforePolicyInterface) {
62 5
            $result = $policy->before($user, $resource, $action);
63
64 5
            if ($result instanceof ResultInterface || is_bool($result)) {
65 4
                return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type Phauthentic\Authorization\Policy\ResultInterface which is incompatible with the return type mandated by Phauthentic\Authorizatio...ServiceInterface::can() of boolean.
Loading history...
66
            }
67 1
            if ($result !== null) {
0 ignored issues
show
introduced by
The condition $result !== null is always false.
Loading history...
68
                throw new RuntimeException(
69
                    'Pre-authorization check must return `Phauthentic\Authorization\Policy\ResultInterface`, '
70
                    . '`bool` or `null`.'
71
                );
72
            }
73
        }
74
75 7
        $handler = $this->getCanHandler($policy, $action);
76 6
        $result = $handler($user, $resource);
77
78 6
        if ($result instanceof ResultInterface) {
79 1
            return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result returns the type Phauthentic\Authorization\Policy\ResultInterface which is incompatible with the return type mandated by Phauthentic\Authorizatio...ServiceInterface::can() of boolean.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
80
        }
81
82 5
        return $result === true;
83
    }
84
85
    /**
86
     * {@inheritDoc}
87
     */
88 3
    public function applyScope(?IdentityInterface $user, string $action, $resource)
89
    {
90 3
        $this->authorizationChecked = true;
91 3
        $policy = $this->resolver->getPolicy($resource);
92 3
        $handler = $this->getScopeHandler($policy, $action);
93
94 2
        return $handler($user, $resource);
95
    }
96
97
    /**
98
     * Returns a policy action handler.
99
     *
100
     * @param mixed $policy Policy object.
101
     * @param string $action Action name.
102
     * @return callable
103
     * @throws \Phauthentic\Authorization\Policy\Exception\MissingMethodException
104
     */
105 7
    protected function getCanHandler($policy, $action)
106
    {
107 7
        $method = 'can' . ucfirst($action);
108
109 7
        if (!method_exists($policy, $method) && !method_exists($policy, '__call')) {
110 1
            throw (new MissingMethodException())->setMessageVars([$method, $action, get_class($policy)]);
111
        }
112
113 6
        return [$policy, $method];
114
    }
115
116
    /**
117
     * Returns a policy scope action handler.
118
     *
119
     * @param mixed $policy Policy object.
120
     * @param string $action Action name.
121
     * @return callable
122
     * @throws \Phauthentic\Authorization\Policy\Exception\MissingMethodException
123
     */
124 3
    protected function getScopeHandler($policy, $action): callable
125
    {
126 3
        $method = 'scope' . ucfirst($action);
127
128 3
        if (!method_exists($policy, $method)) {
129 1
            throw (new MissingMethodException())->setMessageVars([$method, $action, get_class($policy)]);
130
        }
131
132 2
        return [$policy, $method];
133
    }
134
135
    /**
136
     * {@inheritDoc}
137
     */
138 3
    public function authorizationChecked(): bool
139
    {
140 3
        return $this->authorizationChecked;
141
    }
142
143
    /**
144
     * {@inheritDoc}
145
     */
146 1
    public function skipAuthorization(): AuthorizationServiceInterface
147
    {
148 1
        $this->authorizationChecked = true;
149
150 1
        return $this;
151
    }
152
}
153