Completed
Push — issue#666 ( 928a8e...2654e9 )
by Guilherme
04:24
created

AuthorizeController::getOAuth2Server()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 1
b 0
f 0
ccs 0
cts 0
cp 0
crap 2
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\OpenIDBundle\Manager\ScopeManager;
19
use LoginCidadao\OAuthBundle\Service\OrganizationService;
20
use LoginCidadao\OAuthBundle\Model\OrganizationInterface;
21
use LoginCidadao\OAuthBundle\Model\ClientInterface;
22
use LoginCidadao\OpenIDBundle\Validator\SectorIdentifierUriChecker;
23
use LoginCidadao\RemoteClaimsBundle\Model\RemoteClaimInterface;
24
use OAuth2\Server;
25
use OAuth2\ServerBundle\Entity\Scope;
26
use OAuth2\ServerBundle\Controller\AuthorizeController as BaseController;
27
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
28
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
29
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
30
use Symfony\Component\EventDispatcher\EventDispatcher;
31
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
32
use Symfony\Component\HttpFoundation\JsonResponse;
33
use Symfony\Component\HttpFoundation\Request;
34
35
class AuthorizeController extends BaseController
36
{
37
38
    /**
39
     * @Route("/openid/connect/authorize", name="_authorize_handle")
40
     * @Method({"POST"})
41
     */
42
    public function handleAuthorizeAction()
43
    {
44
        $request = $this->getRequest();
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Bundle\Framework...ontroller::getRequest() has been deprecated with message: since version 2.4, to be removed in 3.0. Ask Symfony to inject the Request object into your controller method instead by type hinting it in the method's signature.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
45
        $implodedScope = implode(' ', $request->request->get('scope'));
46
        $request->request->set('scope', $implodedScope);
47
48
        $isAuthorized = $request->request->has('accepted')
49
            || !$request->request->has('rejected');
50
51
        $response = $this->handleAuthorize($this->getOAuth2Server(), $isAuthorized);
52
53
        $event = new OAuthEvent(
54
            $this->getUser(),
55
            $this->getClient($request), $isAuthorized
56
        );
57
58
        /** @var EventDispatcherInterface $dispatcher */
59
        $dispatcher = $this->get('event_dispatcher');
60
        $dispatcher->dispatch(OAuthEvent::POST_AUTHORIZATION_PROCESS, $event);
61
62
        return $response;
63
    }
64
65
    /**
66
     * Render the Authorization fragment
67
     *
68
     * @Template()
69
     * @param Request $request
70
     * @return array
71
     *
72
     * @deprecated
73
     */
74
    public function authorizeAction(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
75
    {
76
        throw new \RuntimeException('This class should not be used!');
77
        $client = $this->getClient($request);
0 ignored issues
show
Unused Code introduced by
$client = $this->getClient($request); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
78
79
        $scope = explode(' ', $request->get('scope'));
80
        if (array_search('public_profile', $scope) === false) {
81
            $scope[] = 'public_profile';
82
        }
83
84
        /** @var ScopeManager $scopeManager */
85
        $scopeManager = $this->get('oauth2.scope_manager');
86
        $scopes = array_map(
87
            function (Scope $value) {
88
                return $value->getScope();
89
            },
90
            $scopeManager->findScopesByScopes($scope)
91
        );
92
93
        /** @var OrganizationService $organizationService */
94
        $organizationService = $this->get('organization');
95
        $warnUntrusted = $this->shouldWarnUntrusted($client);
96
        $metadata = $this->getMetadata($client);
97
        $organization = $organizationService->getOrganization($metadata);
98
99
        $qs = [
100
            'client_id' => $client->getPublicId(),
101
            'scope' => $scope,
102
            'response_type' => $request->get('response_type'),
103
            'redirect_uri' => $request->get('redirect_uri'),
104
            'state' => $request->get('state'),
105
            'nonce' => $request->get('nonce'),
106
        ];
107
108
        return [
109
            'qs' => $qs,
110
            'scopes' => $scopes,
111
            'client' => $client,
112
            'warnUntrusted' => $warnUntrusted,
113
            'metadata' => $metadata,
114
            'organization' => $organization,
115
        ];
116
    }
117
118
    /**
119
     * @Route("/openid/connect/authorize", name="_authorize_validate")
120
     * @Method({"GET"})
121
     * @Template("LoginCidadaoOpenIDBundle:Authorize:authorize.html.twig")
122
     */
123
    public function validateAuthorizeAction()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
124
    {
125
        $request = $this->getRequest();
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Bundle\Framework...ontroller::getRequest() has been deprecated with message: since version 2.4, to be removed in 3.0. Ask Symfony to inject the Request object into your controller method instead by type hinting it in the method's signature.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
126
        $client = $this->getClient($request);
127
128
        if (!$client instanceof \FOS\OAuthServerBundle\Model\ClientInterface) {
129
            return parent::validateAuthorizeAction();
130
        }
131
132
        /** @var PersonInterface $person */
133
        $person = $this->getUser();
134
135
        /** @var EventDispatcher $dispatcher */
136
        $dispatcher = $this->get('event_dispatcher');
137
138
        $event = new OAuthEvent($person, $client);
139
        $dispatcher->dispatch(OAuthEvent::PRE_AUTHORIZATION_PROCESS, $event);
140
141
        $isAuthorized = $event->isAuthorizedClient();
142
        $askConsent = $request->get('prompt', null) == 'consent';
143
144
        if ($isAuthorized && !$askConsent) {
145
            return $this->handleAuthorize($this->getOAuth2Server(), $isAuthorized);
146
        }
147
148
        $authEvent = new AuthorizationEvent($person, $client, $request->get('scope'));
149
        $dispatcher->dispatch(LoginCidadaoOpenIDEvents::NEW_AUTHORIZATION_REQUEST, $authEvent);
150
        $remoteClaims = $authEvent->getRemoteClaims();
151
152
        /** @var OrganizationService $organizationService */
153
        $organizationService = $this->get('organization');
154
        $warnUntrusted = $this->shouldWarnUntrusted($client);
155
        $metadata = $this->getMetadata($client);
156
        $organization = $organizationService->getOrganization($metadata);
157
158
        // Call the lib's original Controller
159
        $parentResponse = parent::validateAuthorizeAction();
160
        $parentResponse['scopes'] = $this->removeRemoteScope($parentResponse['scopes']);
161
162
        $response = array_merge([
163
            'qs' => [
164
                'client_id' => $client->getPublicId(),
165
                'scope' => $parentResponse['scopes'],
166
                'response_type' => $request->get('response_type'),
167
                'redirect_uri' => $request->get('redirect_uri'),
168
                'state' => $request->get('state'),
169
                'nonce' => $request->get('nonce'),
170
            ],
171
            'remoteClaims' => $remoteClaims,
172
            'client' => $client,
173
            'metadata' => $metadata,
174
            'organization' => $organization,
175
            'warnUntrusted' => $warnUntrusted,
176
        ], $parentResponse);
177
178
        return $response;
179
    }
180
181
    private function handleAuthorize(Server $server, $isAuthorized)
182
    {
183
        /** @var \OAuth2\Request $request */
184
        $request = $this->get('oauth2.request');
185
186
        /** @var \OAuth2\Response $response */
187
        $response = $this->get('oauth2.response');
188
189
        return $server->handleAuthorizeRequest(
190
            $request,
191
            $response,
192
            $isAuthorized,
193
            $this->getUser()->getId()
194
        );
195
    }
196
197
    private function getClient($fullId)
198
    {
199
        if ($fullId instanceof Request) {
200
            $fullId = $fullId->get('client_id');
201
        }
202
203
        /** @var ClientManager $clientManager */
204
        $clientManager = $this->get('lc.client_manager');
205
206
        return $clientManager->getClientById($fullId);
207
    }
208
209
    private function shouldWarnUntrusted(ClientInterface $client = null)
210
    {
211
        $warnUntrusted = $this->getParameter('warn_untrusted');
212
213
        if ($client) {
214
            $metadata = $this->getMetadata($client);
215
216
            if ($metadata && $metadata->getOrganization() instanceof OrganizationInterface) {
217
                $isTrusted = $metadata->getOrganization()->isTrusted();
218
            } else {
219
                $isTrusted = false;
220
            }
221
        } else {
222
            $isTrusted = false;
223
        }
224
225
        if ($isTrusted || !$warnUntrusted) {
226
            return false; // do not warn
227
        }
228
229
        return true; // warn
230
    }
231
232
    private function getMetadata(ClientInterface $client = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
233
    {
234
        if (!$client) {
235
            return null;
236
        }
237
238
        $repo = $this->getDoctrine()->getRepository('LoginCidadaoOpenIDBundle:ClientMetadata');
239
240
        return $repo->findOneBy(['client' => $client]);
241
    }
242
243
    /**
244
     * @return SectorIdentifierUriChecker
245
     */
246
    private function getSectorIdentifierUriChecker()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
247
    {
248
        return $this->get('checker.sector_identifier_uri');
249
    }
250
251
    /**
252
     * @return object|Server
253
     */
254
    private function getOAuth2Server()
255
    {
256
        return $this->get('oauth2.server');
257
    }
258
259
    /**
260
     * @param array $scopes
261
     * @return array
262
     */
263
    private function removeRemoteScope(array $scopes)
264
    {
265
        return array_filter($scopes, function ($scope) {
266
            if (preg_match('/^tag:/', $scope) === 1) {
267
                return false;
268
            }
269
270
            return true;
271
        });
272
    }
273
}
274