Failed Conditions
Pull Request — master (#125)
by Florent
21:10
created

AnnotationDriver::onKernelController()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
nop 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\SecurityBundle\Annotation;
15
16
use Doctrine\Common\Annotations\Reader;
17
use OAuth2Framework\SecurityBundle\Annotation\Checker\Checker;
18
use OAuth2Framework\SecurityBundle\Security\Authentication\Token\OAuth2Token;
19
use OAuth2Framework\Component\Core\Message\OAuth2Message;
20
use OAuth2Framework\Component\Core\Message\OAuth2MessageFactoryManager;
21
use Psr\Http\Message\ResponseInterface;
22
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
23
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
24
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
25
26
class AnnotationDriver
27
{
28
    /**
29
     * @var Reader
30
     */
31
    private $reader;
32
33
    /**
34
     * @var TokenStorageInterface
35
     */
36
    private $tokenStorage;
37
38
    /**
39
     * @var Checker[]
40
     */
41
    private $checkers = [];
42
43
    /**
44
     * @var OAuth2MessageFactoryManager
45
     */
46
    private $oauth2ResponseFactoryManager;
47
48
    /**
49
     * AnnotationDriver constructor.
50
     *
51
     * @param Reader                      $reader
52
     * @param TokenStorageInterface       $tokenStorage
53
     * @param OAuth2MessageFactoryManager $oauth2ResponseFactoryManager
54
     */
55
    public function __construct(Reader $reader, TokenStorageInterface $tokenStorage, OAuth2MessageFactoryManager $oauth2ResponseFactoryManager)
56
    {
57
        $this->reader = $reader;
58
        $this->tokenStorage = $tokenStorage;
59
        $this->oauth2ResponseFactoryManager = $oauth2ResponseFactoryManager;
60
    }
61
62
    /**
63
     * @param Checker $checker
64
     *
65
     * @return AnnotationDriver
66
     */
67
    public function add(Checker $checker): self
68
    {
69
        $this->checkers[] = $checker;
70
71
        return $this;
72
    }
73
74
    /**
75
     * @return Checker[]
76
     */
77
    public function all(): array
78
    {
79
        return $this->checkers;
80
    }
81
82
    public function onKernelController(FilterControllerEvent $event)
83
    {
84
        if (!is_array($controller = $event->getController())) {
85
            return;
86
        }
87
88
        $object = new \ReflectionObject($controller[0]);
89
        $method = $object->getMethod($controller[1]);
90
        $classConfigurations = $this->reader->getClassAnnotations($object);
91
        $methodConfigurations = $this->reader->getMethodAnnotations($method);
92
93
        foreach (array_merge($classConfigurations, $methodConfigurations) as $configuration) {
94
            if ($configuration instanceof OAuth2) {
95
                $this->processOAuth2Annotation($event, $configuration);
96
            }
97
        }
98
    }
99
100
    /**
101
     * @param FilterControllerEvent $event
102
     * @param OAuth2                $configuration
103
     */
104
    private function processOAuth2Annotation(FilterControllerEvent $event, OAuth2 $configuration): void
105
    {
106
        $token = $this->tokenStorage->getToken();
107
108
        if (!$token instanceof OAuth2Token) {
109
            $this->createAuthenticationException($event, 'OAuth2 authentication required', $configuration);
110
111
            return;
112
        }
113
114
        foreach ($this->all() as $checker) {
115
            try {
116
                $checker->check($token, $configuration);
117
            } catch (\Exception $e) {
118
                $this->createAccessDeniedException($event, $e->getMessage(), $configuration, $e);
119
            }
120
        }
121
    }
122
123
    /**
124
     * @param FilterControllerEvent $event
125
     * @param string                $message
126
     * @param OAuth2                $configuration
127
     */
128
    private function createAuthenticationException(FilterControllerEvent $event, string $message, OAuth2 $configuration)
129
    {
130
        $additionalData = $configuration->getScope() ? ['scope' => $configuration->getScope()] : [];
131
        $response = $this->oauth2ResponseFactoryManager->getResponse(
132
            new OAuth2Message(
133
                401,
134
                OAuth2Message::ERROR_ACCESS_DENIED,
135
                $message
136
            ),
137
            $additionalData
138
        );
139
140
        $this->updateFilterControllerEvent($event, $response);
141
    }
142
143
    /**
144
     * @param FilterControllerEvent $event
145
     * @param string                $message
146
     * @param OAuth2                $configuration
147
     * @param \Exception            $previous
148
     */
149
    private function createAccessDeniedException(FilterControllerEvent $event, string $message, OAuth2 $configuration, \Exception $previous)
150
    {
151
        $additionalData = $configuration->getScope() ? ['scope' => $configuration->getScope()] : [];
152
        $response = $this->oauth2ResponseFactoryManager->getResponse(
153
            new OAuth2Message(
154
            403,
155
            OAuth2Message::ERROR_ACCESS_DENIED,
156
                $message,
157
                $previous
158
            ),
159
            $additionalData
160
        );
161
        $this->updateFilterControllerEvent($event, $response);
162
    }
163
164
    /**
165
     * @param FilterControllerEvent $event
166
     * @param ResponseInterface     $psr7Response
167
     */
168
    private function updateFilterControllerEvent(FilterControllerEvent $event, ResponseInterface $psr7Response)
169
    {
170
        $event->setController(function () use ($psr7Response) {
171
            $factory = new HttpFoundationFactory();
172
            $symfonyResponse = $factory->createResponse($psr7Response);
173
174
            return $symfonyResponse;
175
        });
176
    }
177
}
178