Completed
Pull Request — master (#288)
by Guilherme
06:17
created

AuthorizeController::authorizeAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\OpenIDBundle\Controller;
12
13
use FOS\OAuthServerBundle\Event\OAuthEvent;
14
use LoginCidadao\CoreBundle\Model\PersonInterface;
15
use LoginCidadao\OpenIDBundle\Event\AuthorizationEvent;
16
use LoginCidadao\OpenIDBundle\LoginCidadaoOpenIDEvents;
17
use LoginCidadao\OpenIDBundle\Manager\ClientManager;
18
use LoginCidadao\OAuthBundle\Service\OrganizationService;
19
use LoginCidadao\OAuthBundle\Model\OrganizationInterface;
20
use LoginCidadao\OAuthBundle\Model\ClientInterface;
21
use OAuth2\Server;
22
use OAuth2\ServerBundle\Controller\AuthorizeController as BaseController;
23
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
24
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
25
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
26
use Symfony\Component\EventDispatcher\EventDispatcher;
27
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\HttpFoundation\RequestStack;
30
31
/**
32
 * Class AuthorizeController
33
 * @package LoginCidadao\OpenIDBundle\Controller
34
 * @codeCoverageIgnore
35
 */
36
class AuthorizeController extends BaseController
37
{
38
39
    /**
40
     * @Route("/openid/connect/authorize", name="_authorize_handle")
41
     * @Method({"POST"})
42
     */
43
    public function handleAuthorizeAction()
44
    {
45
        $request = $this->getRequest();
46
        $implodedScope = implode(' ', $request->request->get('scope'));
47
        $request->request->set('scope', $implodedScope);
48
49
        $isAuthorized = $request->request->has('accepted')
50
            || !$request->request->has('rejected');
51
52
        $response = $this->handleAuthorize($this->getOAuth2Server(), $isAuthorized);
53
54
        $event = new OAuthEvent(
55
            $this->getUser(),
56
            $this->getClient($request), $isAuthorized
57
        );
58
59
        /** @var EventDispatcherInterface $dispatcher */
60
        $dispatcher = $this->get('event_dispatcher');
61
        $dispatcher->dispatch(OAuthEvent::POST_AUTHORIZATION_PROCESS, $event);
62
63
        return $response;
64
    }
65
66
    /**
67
     * Render the Authorization fragment
68
     *
69
     * @Template()
70
     *
71
     * @deprecated
72
     */
73
    public function authorizeAction()
74
    {
75
        throw new \RuntimeException('This class should not be used!');
76
    }
77
78
    /**
79
     * @Route("/openid/connect/authorize", name="_authorize_validate")
80
     * @Method({"GET"})
81
     * @Template("LoginCidadaoOpenIDBundle:Authorize:authorize.html.twig")
82
     */
83
    public function validateAuthorizeAction()
84
    {
85
        $request = $this->getRequest();
86
        $client = $this->getClient($request);
87
88
        if (!$client instanceof \FOS\OAuthServerBundle\Model\ClientInterface) {
89
            return parent::validateAuthorizeAction();
90
        }
91
92
        /** @var PersonInterface $person */
93
        $person = $this->getUser();
94
95
        /** @var EventDispatcher $dispatcher */
96
        $dispatcher = $this->get('event_dispatcher');
97
98
        $event = new OAuthEvent($person, $client);
99
        $dispatcher->dispatch(OAuthEvent::PRE_AUTHORIZATION_PROCESS, $event);
100
101
        $isAuthorized = $event->isAuthorizedClient();
102
        $askConsent = $request->get('prompt', null) == 'consent';
103
104
        if ($isAuthorized && !$askConsent) {
105
            return $this->handleAuthorize($this->getOAuth2Server(), $isAuthorized);
106
        }
107
108
        $authEvent = new AuthorizationEvent($person, $client, $request->get('scope'));
109
        $dispatcher->dispatch(LoginCidadaoOpenIDEvents::NEW_AUTHORIZATION_REQUEST, $authEvent);
110
        $remoteClaims = $authEvent->getRemoteClaims();
111
112
        /** @var OrganizationService $organizationService */
113
        $organizationService = $this->get('organization');
114
        $warnUntrusted = $this->shouldWarnUntrusted($client);
115
        $metadata = $this->getMetadata($client);
116
        $organization = $organizationService->getOrganization($metadata);
117
118
        // Call the lib's original Controller
119
        $parentResponse = parent::validateAuthorizeAction();
120
        if (!is_array($parentResponse)) {
121
            return $parentResponse;
122
        }
123
        $parentResponse['scopes'] = $this->removeRemoteScope($parentResponse['scopes']);
124
125
        $response = array_merge([
126
            'qs' => [
127
                'client_id' => $client->getPublicId(),
128
                'scope' => $parentResponse['scopes'],
129
                'response_type' => $request->get('response_type'),
130
                'redirect_uri' => $request->get('redirect_uri'),
131
                'state' => $request->get('state'),
132
                'nonce' => $request->get('nonce'),
133
            ],
134
            'remoteClaims' => $remoteClaims,
135
            'client' => $client,
136
            'metadata' => $metadata,
137
            'organization' => $organization,
138
            'warnUntrusted' => $warnUntrusted,
139
        ], $parentResponse);
140
141
        return $response;
142
    }
143
144
    private function handleAuthorize(Server $server, $isAuthorized)
145
    {
146
        /** @var \OAuth2\Request $request */
147
        $request = $this->get('oauth2.request');
148
149
        /** @var \OAuth2\Response $response */
150
        $response = $this->get('oauth2.response');
151
152
        return $server->handleAuthorizeRequest(
153
            $request,
154
            $response,
155
            $isAuthorized,
156
            $this->getUser()->getId()
157
        );
158
    }
159
160
    private function getClient($fullId)
161
    {
162
        if ($fullId instanceof Request) {
163
            $fullId = $fullId->get('client_id');
164
        }
165
166
        /** @var ClientManager $clientManager */
167
        $clientManager = $this->get('lc.client_manager');
168
169
        return $clientManager->getClientById($fullId);
170
    }
171
172
    private function shouldWarnUntrusted(ClientInterface $client = null)
173
    {
174
        $warnUntrusted = $this->getParameter('warn_untrusted');
175
176
        if ($client) {
177
            $metadata = $this->getMetadata($client);
178
179
            if ($metadata && $metadata->getOrganization() instanceof OrganizationInterface) {
180
                $isTrusted = $metadata->getOrganization()->isTrusted();
181
            } else {
182
                $isTrusted = false;
183
            }
184
        } else {
185
            $isTrusted = false;
186
        }
187
188
        if ($isTrusted || !$warnUntrusted) {
189
            return false; // do not warn
190
        }
191
192
        return true; // warn
193
    }
194
195
    private function getMetadata(ClientInterface $client = null)
196
    {
197
        if (!$client) {
198
            return null;
199
        }
200
201
        $repo = $this->getDoctrine()->getRepository('LoginCidadaoOpenIDBundle:ClientMetadata');
202
203
        return $repo->findOneBy(['client' => $client]);
204
    }
205
206
    /**
207
     * @return object|Server
208
     */
209
    private function getOAuth2Server()
210
    {
211
        return $this->get('oauth2.server');
212
    }
213
214
    /**
215
     * @param array $scopes
216
     * @return array
217
     */
218
    private function removeRemoteScope(array $scopes)
219
    {
220
        return array_filter($scopes, function ($scope) {
221
            if (preg_match('/^tag:/', $scope) === 1) {
222
                return false;
223
            }
224
225
            return true;
226
        });
227
    }
228
229
    private function getRequest(): Request
230
    {
231
        /** @var RequestStack $requestStack */
232
        $requestStack = $this->get('request_stack');
233
        $request = $requestStack->getCurrentRequest();
234
235
        if ($request instanceof Request) {
236
            return $request;
237
        }
238
239
        throw new \RuntimeException("Request could not be found");
240
    }
241
}
242