Passed
Push — master ( 3a23e3...2f3817 )
by Guilherme
01:09 queued 10s
created

ClientRegistrationController::handleViolations()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 4
nop 1
dl 0
loc 26
rs 9.7
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 Doctrine\DBAL\Exception\UniqueConstraintViolationException;
14
use FOS\RestBundle\Context\Context;
15
use LoginCidadao\OAuthBundle\Entity\Client;
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\Exception\DynamicRegistrationException;
23
use Symfony\Component\Serializer\Encoder\JsonEncoder;
24
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
25
use Symfony\Component\Serializer\Serializer;
26
use Symfony\Component\Validator\ConstraintViolationInterface;
27
use Symfony\Component\Validator\ConstraintViolationListInterface;
28
use Symfony\Component\Validator\Validator\ValidatorInterface;
29
30
/**
31
 * Class ClientRegistrationController
32
 * @package LoginCidadao\OpenIDBundle\Controller
33
 * @codeCoverageIgnore
34
 */
35
class ClientRegistrationController extends FOSRestController
0 ignored issues
show
Deprecated Code introduced by
The class FOS\RestBundle\Controller\FOSRestController has been deprecated: since FOSRestBundle 2.5, use {@see AbstractFOSRestController} instead ( Ignorable by Annotation )

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

35
class ClientRegistrationController extends /** @scrutinizer ignore-deprecated */ FOSRestController
Loading history...
36
{
37
38
    /**
39
     * @REST\Post("/openid/connect/register", name="oidc_dynamic_registration", defaults={"_format"="json"})
40
     * @REST\View(templateVar="client")
41
     */
42
    public function registerAction(Request $request)
43
    {
44
        $serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
45
46
        /** @var ClientMetadata $metadata */
47
        $metadata = $serializer->deserialize($request->getContent(), ClientMetadata::class, 'json');
48
49
        /** @var ValidatorInterface $validator */
50
        $validator = $this->get('validator');
51
        $violations = $validator->validate($metadata);
52
53
        if ($violations->count() === 0) {
54
            $clientManager = $this->getClientManager();
55
            try {
56
                $client = $clientManager->register($metadata);
57
            } catch (UniqueConstraintViolationException $e) {
58
                $error = new DynamicRegistrationException('Client already exists', 400);
59
60
                return $this->view($error->getData(), $error->getCode());
61
            }
62
63
            return $this->view($metadata->fromClient($client), 201);
64
        } else {
65
            $error = $this->handleViolations($violations);
66
67
            return $this->view($error->getData(), 400);
68
        }
69
    }
70
71
    /**
72
     * @REST\Get("/openid/connect/register/{clientId}", name="oidc_get_client_details", defaults={"_format"="json"})
73
     * @REST\View(templateVar="client")
74
     */
75
    public function getDetailsAction(Request $request, $clientId)
76
    {
77
        try {
78
            $client = $this->getClientOr404($clientId);
79
        } catch (DynamicRegistrationException $e) {
80
            return $this->view($e->getData(), 400);
81
        }
82
        $this->checkRegistrationAccessToken($request, $client);
83
84
        $context = (new Context())->setGroups(["client_metadata"]);
85
        $view = $this->view($client->getMetadata())->setContext($context);
86
87
        return $this->handleView($view);
88
    }
89
90
    /**
91
     * @param string $clientId
92
     * @return ClientInterface
93
     */
94
    private function getClientOr404($clientId)
95
    {
96
        /** @var ClientInterface|null $client */
97
        $client = $this->getClientManager()->getClientById($clientId);
98
99
        if (!$client instanceof ClientInterface) {
0 ignored issues
show
introduced by
$client is always a sub-type of LoginCidadao\OAuthBundle\Model\ClientInterface.
Loading history...
100
            throw $this->createNotFoundException('Client not found.');
101
        }
102
103
        return $client;
104
    }
105
106
    private function checkRegistrationAccessToken(Request $request, Client $client)
107
    {
108
        $raw = $request->get('access_token', $request->headers->get('authorization'));
109
110
        $token = str_replace('Bearer ', '', $raw);
111
        $metadata = $client->getMetadata();
112
        if (!$token || $metadata->getRegistrationAccessToken() !== $token) {
113
            throw $this->createAccessDeniedException();
114
        }
115
    }
116
117
    /**
118
     * @return ClientManager|object
119
     */
120
    private function getClientManager()
121
    {
122
        return $this->get('lc.client_manager');
123
    }
124
125
    /**
126
     * @param ConstraintViolationInterface[]|ConstraintViolationListInterface $violations
127
     * @return DynamicRegistrationException
128
     */
129
    private function handleViolations($violations)
130
    {
131
        foreach ($violations as $violation) {
132
            $property = $violation->getPropertyPath();
133
            $value = $violation->getInvalidValue();
134
135
            switch ($property) {
136
                case 'redirect_uris':
137
                    return new DynamicRegistrationException(
138
                        'Invalid redirect URIs: '.$violation->getMessage(),
139
                        DynamicRegistrationException::ERROR_INVALID_REDIRECT_URI
140
                    );
141
                case 'sector_identifier_uri':
142
                    return new DynamicRegistrationException(
143
                        "Invalid value for '{$property}': {$violation->getMessage()}",
144
                        DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA
145
                    );
146
                default:
147
                    return new DynamicRegistrationException(
148
                        "Invalid value for '{$property}'='{$value}': {$violation->getMessage()}",
149
                        DynamicRegistrationException::ERROR_INVALID_CLIENT_METADATA
150
                    );
151
            }
152
        }
153
154
        throw new \RuntimeException('No errors found but there should be at least one!');
155
    }
156
}
157