|
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 Doctrine\DBAL\Exception\UniqueConstraintViolationException; |
|
14
|
|
|
use LoginCidadao\OAuthBundle\Entity\Client; |
|
15
|
|
|
use JMS\Serializer\SerializationContext; |
|
16
|
|
|
use LoginCidadao\OAuthBundle\Model\ClientInterface; |
|
17
|
|
|
use LoginCidadao\OpenIDBundle\Manager\ClientManager; |
|
18
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
19
|
|
|
use FOS\RestBundle\Controller\FOSRestController; |
|
20
|
|
|
use FOS\RestBundle\Controller\Annotations as REST; |
|
21
|
|
|
use LoginCidadao\OpenIDBundle\Entity\ClientMetadata; |
|
22
|
|
|
use LoginCidadao\OpenIDBundle\Form\ClientMetadataForm; |
|
23
|
|
|
use LoginCidadao\OpenIDBundle\Exception\DynamicRegistrationException; |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* Class ClientRegistrationController |
|
27
|
|
|
* @package LoginCidadao\OpenIDBundle\Controller |
|
28
|
|
|
* @codeCoverageIgnore |
|
29
|
|
|
*/ |
|
30
|
|
|
class ClientRegistrationController extends FOSRestController |
|
31
|
|
|
{ |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* @REST\Post("/openid/connect/register", name="oidc_dynamic_registration", defaults={"_format"="json"}) |
|
35
|
|
|
* @REST\View(templateVar="client") |
|
36
|
|
|
*/ |
|
37
|
|
|
public function registerAction(Request $request) |
|
38
|
|
|
{ |
|
39
|
|
|
$this->parseJsonRequest($request); |
|
40
|
|
|
$clientManager = $this->getClientManager(); |
|
41
|
|
|
|
|
42
|
|
|
$data = new ClientMetadata(); |
|
43
|
|
|
$form = $this->createForm(new ClientMetadataForm($clientManager), $data, ['cascade_validation' => true]); |
|
44
|
|
|
|
|
45
|
|
|
$form->handleRequest($request); |
|
46
|
|
|
if ($form->isValid()) { |
|
47
|
|
|
$metadata = $form->getData(); |
|
48
|
|
|
try { |
|
49
|
|
|
$client = $clientManager->register($metadata); |
|
50
|
|
|
} catch (UniqueConstraintViolationException $e) { |
|
51
|
|
|
$error = new DynamicRegistrationException('Client already exists', 400); |
|
52
|
|
|
|
|
53
|
|
|
return $this->view($error->getData(), $error->getCode()); |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
return $this->view($metadata->fromClient($client), 201); |
|
57
|
|
|
} else { |
|
58
|
|
|
$error = $this->handleFormErrors($form->getErrors(true)); |
|
59
|
|
|
|
|
60
|
|
|
return $this->view($error->getData(), 400); |
|
61
|
|
|
} |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* @REST\Get("/openid/connect/register/{clientId}", name="oidc_get_client_details", defaults={"_format"="json"}) |
|
66
|
|
|
* @REST\View(templateVar="client") |
|
67
|
|
|
*/ |
|
68
|
|
|
public function getDetailsAction(Request $request, $clientId) |
|
69
|
|
|
{ |
|
70
|
|
|
try { |
|
71
|
|
|
$client = $this->getClientOr404($clientId); |
|
72
|
|
|
} catch (DynamicRegistrationException $e) { |
|
73
|
|
|
return $this->view($e->getData(), 400); |
|
74
|
|
|
} |
|
75
|
|
|
$this->checkRegistrationAccessToken($request, $client); |
|
76
|
|
|
|
|
77
|
|
|
$context = SerializationContext::create()->setGroups("client_metadata"); |
|
78
|
|
|
|
|
79
|
|
|
$view = $this->view($client->getMetadata())->setSerializationContext($context); |
|
80
|
|
|
|
|
81
|
|
|
return $this->handleView($view); |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* @param \Symfony\Component\Form\FormError[] $errors |
|
86
|
|
|
* @return DynamicRegistrationException |
|
87
|
|
|
*/ |
|
88
|
|
|
private function handleFormErrors($errors) |
|
89
|
|
|
{ |
|
90
|
|
|
foreach ($errors as $error) { |
|
91
|
|
|
$cause = $error->getCause(); |
|
92
|
|
|
$value = $cause->getInvalidValue(); |
|
93
|
|
|
$propertyRegex = '/^data\\.([a-zA-Z0-9_]+).*$/'; |
|
94
|
|
|
$property = preg_replace( |
|
95
|
|
|
$propertyRegex, |
|
96
|
|
|
'$1', |
|
97
|
|
|
$cause->getPropertyPath() |
|
98
|
|
|
); |
|
99
|
|
|
//$property = str_replace('data.', '', $cause->getPropertyPath()); |
|
|
|
|
|
|
100
|
|
|
|
|
101
|
|
|
switch ($property) { |
|
102
|
|
|
case 'redirect_uris': |
|
103
|
|
|
return new DynamicRegistrationException( |
|
104
|
|
|
'Invalid redirect URIs: '.$cause->getMessage(), |
|
105
|
|
|
DynamicRegistrationException::ERROR_INVALID_REDIRECT_URI |
|
106
|
|
|
); |
|
107
|
|
|
case 'sector_identifier_uri': |
|
108
|
|
|
return new DynamicRegistrationException( |
|
109
|
|
|
"Invalid value for '{$property}': {$cause->getMessage()}", |
|
110
|
|
|
DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA |
|
111
|
|
|
); |
|
112
|
|
|
default: |
|
113
|
|
|
return new DynamicRegistrationException( |
|
114
|
|
|
"Invalid value for '{$property}'='{$value}': {$cause->getMessage()}", |
|
115
|
|
|
DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA |
|
116
|
|
|
); |
|
117
|
|
|
} |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
private function parseJsonRequest(Request $request) |
|
122
|
|
|
{ |
|
123
|
|
|
$request->setFormat('json', 'application/json'); |
|
124
|
|
|
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) { |
|
125
|
|
|
$data = json_decode($request->getContent(), true); |
|
126
|
|
|
$request->request->replace(is_array($data) ? $data : []); |
|
127
|
|
|
} |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* @param string $clientId |
|
132
|
|
|
* @return ClientInterface |
|
133
|
|
|
*/ |
|
134
|
|
|
private function getClientOr404($clientId) |
|
135
|
|
|
{ |
|
136
|
|
|
/** @var ClientInterface|null $client */ |
|
137
|
|
|
$client = $this->getClientManager()->getClientById($clientId); |
|
138
|
|
|
|
|
139
|
|
|
if (!$client instanceof ClientInterface) { |
|
140
|
|
|
throw $this->createNotFoundException('Client not found.'); |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
return $client; |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
private function checkRegistrationAccessToken(Request $request, Client $client) |
|
147
|
|
|
{ |
|
148
|
|
|
$raw = $request->get( |
|
149
|
|
|
'access_token', |
|
150
|
|
|
$request->headers->get('authorization') |
|
151
|
|
|
); |
|
152
|
|
|
|
|
153
|
|
|
$token = str_replace('Bearer ', '', $raw); |
|
154
|
|
|
$metadata = $client->getMetadata(); |
|
155
|
|
|
if (!$token || $metadata->getRegistrationAccessToken() !== $token) { |
|
156
|
|
|
throw $this->createAccessDeniedException(); |
|
157
|
|
|
} |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
/** |
|
161
|
|
|
* @return ClientManager|object |
|
162
|
|
|
*/ |
|
163
|
|
|
private function getClientManager() |
|
164
|
|
|
{ |
|
165
|
|
|
return $this->get('lc.client_manager'); |
|
166
|
|
|
} |
|
167
|
|
|
} |
|
168
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.