Failed Conditions
Push — issue#702_rs ( ed72a1...cdafcf )
by Guilherme
07:33
created

ClientRegistrationController   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 129
rs 10
c 0
b 0
f 0
wmc 18

7 Methods

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