Failed Conditions
Push — ng ( c3e4f5...b44631 )
by Florent
15:52
created

AnnotationDriver   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 145
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 8
dl 0
loc 145
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A addChecker() 0 6 1
A getCheckers() 0 4 1
C onKernelController() 0 32 7
A createAuthenticationException() 0 14 1
A createAccessDeniedException() 0 11 1
A updateFilterControllerEvent() 0 9 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Bundle\Annotation;
15
16
use Doctrine\Common\Annotations\Reader;
17
use OAuth2Framework\Bundle\Annotation\Checker\Checker;
18
use OAuth2Framework\Bundle\Security\Authentication\Token\OAuth2Token;
19
use OAuth2Framework\Component\Core\Exception\OAuth2Exception;
20
use OAuth2Framework\Component\Core\Response\OAuth2Response;
21
use OAuth2Framework\Component\Core\Response\OAuth2ResponseFactoryManager;
22
use OAuth2Framework\Component\TokenType\TokenTypeManager;
23
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
24
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
25
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
26
27
class AnnotationDriver
28
{
29
    /**
30
     * @var Reader
31
     */
32
    private $reader;
33
34
    /**
35
     * @var TokenStorageInterface
36
     */
37
    private $tokenStorage;
38
39
    /**
40
     * @var Checker[]
41
     */
42
    private $checkers = [];
43
44
    /**
45
     * @var TokenTypeManager
46
     */
47
    private $tokenTypeManager;
48
49
    /**
50
     * @var OAuth2ResponseFactoryManager
51
     */
52
    private $oauth2ResponseFactoryManager;
53
54
    /**
55
     * AnnotationDriver constructor.
56
     *
57
     * @param Reader                       $reader
58
     * @param TokenStorageInterface        $tokenStorage
59
     * @param TokenTypeManager             $tokenTypeManager
60
     * @param OAuth2ResponseFactoryManager $oauth2ResponseFactoryManager
61
     */
62
    public function __construct(Reader $reader, TokenStorageInterface $tokenStorage, TokenTypeManager $tokenTypeManager, OAuth2ResponseFactoryManager $oauth2ResponseFactoryManager)
63
    {
64
        $this->reader = $reader;
65
        $this->tokenStorage = $tokenStorage;
66
        $this->tokenTypeManager = $tokenTypeManager;
67
        $this->oauth2ResponseFactoryManager = $oauth2ResponseFactoryManager;
68
    }
69
70
    /**
71
     * @param Checker $checker
72
     *
73
     * @return AnnotationDriver
74
     */
75
    public function addChecker(Checker $checker): self
76
    {
77
        $this->checkers[] = $checker;
78
79
        return $this;
80
    }
81
82
    /**
83
     * @return Checker[]
84
     */
85
    public function getCheckers(): array
86
    {
87
        return $this->checkers;
88
    }
89
90
    public function onKernelController(FilterControllerEvent $event)
91
    {
92
        if (!is_array($controller = $event->getController())) {
93
            return;
94
        }
95
96
        $object = new \ReflectionObject($controller[0]);
97
        $method = $object->getMethod($controller[1]);
98
        $classConfigurations = $this->reader->getClassAnnotations($object);
99
        $methodConfigurations = $this->reader->getMethodAnnotations($method);
100
101
        foreach (array_merge($classConfigurations, $methodConfigurations) as $configuration) {
102
            if ($configuration instanceof OAuth2) {
103
                $token = $this->tokenStorage->getToken();
104
105
                if (!$token instanceof OAuth2Token) {
106
                    $this->createAuthenticationException($event, 'OAuth2 authentication required');
107
108
                    return;
109
                }
110
111
                foreach ($this->getCheckers() as $checker) {
112
                    $message = $checker->check($token, $configuration);
113
                    if (null !== $message) {
114
                        $this->createAccessDeniedException($event, $message);
115
116
                        return;
117
                    }
118
                }
119
            }
120
        }
121
    }
122
123
    /**
124
     * @param FilterControllerEvent $event
125
     * @param string                $message
126
     */
127
    private function createAuthenticationException(FilterControllerEvent &$event, $message)
128
    {
129
        $schemes = $this->tokenTypeManager->getSchemes();
130
        $response = $this->oauth2ResponseFactoryManager->getResponse(
131
            401,
0 ignored issues
show
Documentation introduced by
401 is of type integer, but the function expects a object<OAuth2Framework\C...eption\OAuth2Exception>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
132
            [
0 ignored issues
show
Unused Code introduced by
The call to OAuth2ResponseFactoryManager::getResponse() has too many arguments starting with array('error' => \OAuth2... 'schemes' => $schemes).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
133
                'error' => OAuth2Exception::ERROR_ACCESS_DENIED,
134
                'error_description' => $message,
135
                'schemes' => $schemes,
136
            ]
137
        );
138
139
        $this->updateFilterControllerEvent($event, $response);
0 ignored issues
show
Compatibility introduced by
$response of type object<OAuth2Framework\C...Auth2ResponseInterface> is not a sub-type of object<OAuth2Framework\C...esponse\OAuth2Response>. It seems like you assume a concrete implementation of the interface OAuth2Framework\Componen...OAuth2ResponseInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
140
    }
141
142
    /**
143
     * @param FilterControllerEvent $event
144
     * @param string                $message
145
     */
146
    private function createAccessDeniedException(FilterControllerEvent &$event, $message)
147
    {
148
        $response = $this->oauth2ResponseFactoryManager->getResponse(
149
            403,
0 ignored issues
show
Documentation introduced by
403 is of type integer, but the function expects a object<OAuth2Framework\C...eption\OAuth2Exception>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
150
            [
0 ignored issues
show
Unused Code introduced by
The call to OAuth2ResponseFactoryManager::getResponse() has too many arguments starting with array('error' => \OAuth2...scription' => $message).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
151
                'error' => OAuth2Exception::ERROR_ACCESS_DENIED,
152
                'error_description' => $message,
153
            ]
154
        );
155
        $this->updateFilterControllerEvent($event, $response);
0 ignored issues
show
Compatibility introduced by
$response of type object<OAuth2Framework\C...Auth2ResponseInterface> is not a sub-type of object<OAuth2Framework\C...esponse\OAuth2Response>. It seems like you assume a concrete implementation of the interface OAuth2Framework\Componen...OAuth2ResponseInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
156
    }
157
158
    /**
159
     * @param FilterControllerEvent $event
160
     * @param OAuth2Response        $response
161
     */
162
    private function updateFilterControllerEvent(FilterControllerEvent &$event, OAuth2Response $response)
163
    {
164
        $event->setController(function () use ($response) {
165
            $factory = new HttpFoundationFactory();
166
            $response = $factory->createResponse($response->getResponse());
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $response, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
167
168
            return $response;
169
        });
170
    }
171
}
172