Failed Conditions
Branch master (116909)
by Guilherme
08:28
created

ClientRegistrationController::getHost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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 LoginCidadao\OAuthBundle\Entity\Client;
14
use JMS\Serializer\SerializationContext;
15
use Symfony\Component\HttpFoundation\Request;
16
use FOS\RestBundle\Controller\FOSRestController;
17
use FOS\RestBundle\Controller\Annotations as REST;
18
use LoginCidadao\OpenIDBundle\Entity\ClientMetadata;
19
use LoginCidadao\OpenIDBundle\Form\ClientMetadataForm;
20
use LoginCidadao\OpenIDBundle\Exception\DynamicRegistrationException;
21
22
class ClientRegistrationController extends FOSRestController
23
{
24
25
    /**
26
     * @REST\Post("/openid/connect/register", name="oidc_dynamic_registration", defaults={"_format"="json"})
27
     * @REST\View(templateVar="client")
28
     */
29 3
    public function registerAction(Request $request)
30
    {
31 3
        $this->parseJsonRequest($request);
32
33 3
        $data = new ClientMetadata();
34 3
        $form = $this->createForm(new ClientMetadataForm(), $data, ['cascade_validation' => true]);
35
36 3
        $form->handleRequest($request);
37 3
        if ($form->isValid()) {
38 1
            $metadata = $form->getData();
39 1
            $client = $this->registerClient($metadata);
40
41 1
            return $this->view($metadata->fromClient($client), 201);
42
        } else {
43 2
            $error = $this->handleFormErrors($form->getErrors(true));
0 ignored issues
show
Bug introduced by
$form->getErrors(true) of type Symfony\Component\Form\FormErrorIterator is incompatible with the type Symfony\Component\Form\FormError[] expected by parameter $errors of LoginCidadao\OpenIDBundl...ler::handleFormErrors(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

43
            $error = $this->handleFormErrors(/** @scrutinizer ignore-type */ $form->getErrors(true));
Loading history...
44
45 2
            return $this->view($error->getData(), 400);
46
        }
47
    }
48
49
    /**
50
     * @REST\Get("/openid/connect/register/{clientId}", name="oidc_get_client_details", defaults={"_format"="json"})
51
     * @REST\View(templateVar="client")
52
     */
53
    public function getDetailsAction(Request $request, $clientId)
54
    {
55
        try {
56
            $client = $this->getClientOr404($clientId);
57
        } catch (DynamicRegistrationException $e) {
58
            return $this->view($e->getData(), 400);
59
        }
60
        $this->checkRegistrationAccessToken($request, $client);
61
62
        $context = SerializationContext::create()->setGroups("client_metadata");
63
64
        $view = $this->view($client->getMetadata())->setSerializationContext($context);
65
66
        return $this->handleView($view);
67
    }
68
69
    /**
70
     * @param \Symfony\Component\Form\FormError[] $errors
71
     * @return DynamicRegistrationException
72
     */
73 2
    private function handleFormErrors($errors)
74
    {
75 2
        foreach ($errors as $error) {
76 2
            $cause = $error->getCause();
77 2
            $value = $cause->getInvalidValue();
78 2
            $propertyRegex = '/^data\\.([a-zA-Z0-9_]+).*$/';
79 2
            $property = preg_replace(
80 2
                $propertyRegex,
81 2
                '$1',
82 2
                $cause->getPropertyPath()
83
            );
84
            //$property      = str_replace('data.', '', $cause->getPropertyPath());
85
86
            switch ($property) {
87 2
                case 'redirect_uris':
88 1
                    return new DynamicRegistrationException(
89 1
                        'Invalid redirect URIs: '.$cause->getMessage(),
90 1
                        DynamicRegistrationException::ERROR_INVALID_REDIRECT_URI
91
                    );
92 1
                case 'sector_identifier_uri':
93
                    return new DynamicRegistrationException(
94
                        "Invalid value for '{$property}': {$cause->getMessage()}",
95
                        DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA
96
                    );
97
                default:
98 1
                    return new DynamicRegistrationException(
99 1
                        "Invalid value for '{$property}'='{$value}': {$cause->getMessage()}",
100 1
                        DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA
101
                    );
102
            }
103
        }
104
    }
105
106
    /**
107
     * @param ClientMetadata $data
108
     * @return Client
109
     */
110 1
    private function registerClient(ClientMetadata $data)
111
    {
112 1
        $em = $this->getDoctrine()->getManager();
113 1
        if ($data->getClient() === null) {
114 1
            $client = $data->toClient();
115
        } else {
116
            $client = $data->getClient();
117
        }
118
119 1
        if ($client->getName() === null) {
120 1
            $firstUrl = $this->getHost($client->getRedirectUris()[0]);
121 1
            $client->setName($firstUrl);
122
        }
123 1
        if ($client->getDescription() === null) {
0 ignored issues
show
introduced by
The condition $client->getDescription() === null can never be true.
Loading history...
124 1
            $client->setDescription('');
125
        }
126 1
        if ($client->getTermsOfUseUrl() === null) {
127 1
            $client->setTermsOfUseUrl('');
128
        }
129 1
        if ($client->getSiteUrl() === null) {
130 1
            $client->setSiteUrl('');
131
        }
132
133 1
        if (count($data->getContacts()) > 0) {
134
            $owners = $em->getRepository($this->getParameter('user.class'))
135
                ->findByEmail($data->getContacts());
136
137
            foreach ($owners as $person) {
138
                if ($person->getConfirmationToken() !== null) {
139
                    continue;
140
                }
141
                $client->getOwners()->add($person);
142
            }
143
        }
144
145 1
        $publicScopes = explode(' ', $this->getParameter('lc_public_scopes'));
146 1
        $client->setAllowedScopes($publicScopes);
147
148 1
        $em->persist($client);
149
150 1
        $data->setClient($client);
151 1
        $em->persist($data);
152
153 1
        $em->flush();
154
155 1
        return $client;
156
    }
157
158 1
    private function getHost($uri)
159
    {
160 1
        return parse_url($uri, PHP_URL_HOST);
161
    }
162
163 3
    private function parseJsonRequest(Request $request)
164
    {
165 3
        $request->setFormat('json', 'application/json');
166 3
        if (0 === strpos(
167 3
                $request->headers->get('Content-Type'),
168 3
                'application/json'
169
            )
170
        ) {
171 3
            $data = json_decode($request->getContent(), true);
172 3
            $request->request->replace(is_array($data) ? $data : array());
173
        }
174 3
    }
175
176
    /**
177
     * @param string $clientId
178
     * @return Client
179
     */
180
    private function getClientOr404($clientId)
181
    {
182
        $parts = explode('_', $clientId, 2);
183
        if (count($parts) !== 2) {
184
            throw new DynamicRegistrationException(
185
                "Invalid client_id",
186
                DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA
187
            );
188
        }
189
        $entityId = $parts[0];
190
        $publicId = $parts[1];
191
192
        $client = $this->getDoctrine()->getRepository('LoginCidadaoOAuthBundle:Client')
193
            ->findOneBy(array('id' => $entityId, 'randomId' => $publicId));
194
195
        if (!$client) {
196
            throw $this->createNotFoundException('Client not found.');
197
        }
198
199
        return $client;
200
    }
201
202
    private function checkRegistrationAccessToken(
203
        Request $request,
204
        Client $client
205
    ) {
206
        $raw = $request->get(
207
            'access_token',
208
            $request->headers->get('authorization')
209
        );
210
211
        $token = str_replace('Bearer ', '', $raw);
212
        $metadata = $client->getMetadata();
213
        if (!$token || $metadata->getRegistrationAccessToken() !== $token) {
214
            throw $this->createAccessDeniedException();
215
        }
216
    }
217
}
218