Failed Conditions
Push — ng ( 06d981...bf1375 )
by Florent
11:23
created

CreateClientCommand::isAnUrlOrUrn()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Bundle\Command;
15
16
use OAuth2Framework\Bundle\Model\ClientRepository;
17
use OAuth2Framework\Component\Core\Client\Client;
18
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Question\ChoiceQuestion;
22
use Symfony\Component\Console\Question\Question;
23
24
final class CreateClientCommand extends ContainerAwareCommand
25
{
26
    /**
27
     * {@inheritdoc}
28
     */
29
    protected function configure()
30
    {
31
        $this
32
            ->setName('oauth2-server:client:create')
33
            ->setDescription('Create a new client')
34
            ->setHelp(<<<'EOT'
35
The <info>%command.name%</info> command will create a new client.
36
37
  <info>php %command.full_name%</info>
38
EOT
39
        );
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    protected function execute(InputInterface $input, OutputInterface $output)
46
    {
47
        /**
48
         * @var ClientRepository
49
         */
50
        $service = $this->getContainer()->get(ClientRepository::class);
51
52
        $this->selectResponseTypes($input, $output, $client);
0 ignored issues
show
Bug introduced by
The variable $client does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
53
        $this->selectGrantTypes($input, $output, $client);
54
        $this->selectRedirectUris($input, $output, $client);
55
        $this->selectTokenEndpointAuthenticationMethod($input, $output, $client);
56
57
        $service->saveClient($client);
58
59
        $output->writeln('A client has been created');
60
        $output->writeln(sprintf('Its configuration is "%s"', json_encode($client, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)));
61
    }
62
63
    /**
64
     * @param InputInterface  $input
65
     * @param OutputInterface $output
66
     * @param Client          $client
67
     */
68
    private function selectTokenEndpointAuthenticationMethod(InputInterface $input, OutputInterface $output, Client $client)
69
    {
70
        $token_endpoint_auth_method_manager = $this->getContainer()->get('oauth2_server.token_endpoint_auth_method.manager');
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $token_endpoint_auth_method_manager exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
71
        $helper = $this->getHelper('question');
72
        $question = new ChoiceQuestion(
73
            'Please select the token endpoint authentication method.',
74
            $token_endpoint_auth_method_manager->getSupportedTokenEndpointAuthMethods()
75
        );
76
        $question->setMultiselect(false);
77
        $question->setErrorMessage('The token endpoint authentication method "%s" is invalid.');
78
79
        $token_endpoint_auth_method = $helper->ask($input, $output, $question);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $token_endpoint_auth_method exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
80
        $client->set('token_endpoint_auth_method', $token_endpoint_auth_method);
81
    }
82
83
    /**
84
     * @param InputInterface  $input
85
     * @param OutputInterface $output
86
     * @param Client          $client
87
     */
88
    private function selectRedirectUris(InputInterface $input, OutputInterface $output, Client $client)
89
    {
90
        $redirect_uris = [];
91
        $helper = $this->getHelper('question');
92
93
        do {
94
            $question = new Question('Enter a redirect URI/URN (leave blank to continue).');
95
            $uri = $helper->ask($input, $output, $question);
96
            if (!empty($uri)) {
97
                if (false === $this->isAnUrlOrUrn($uri, false)) {
98
                    $output->writeln('Invalid input.');
99
                } else {
100
                    $redirect_uris[] = $uri;
101
                }
102
            }
103
        } while (!empty($uri));
104
105
        if (!empty($redirect_uris)) {
106
            $client->set('redirect_uris', $redirect_uris);
107
        }
108
    }
109
110
    /**
111
     * @param InputInterface  $input
112
     * @param OutputInterface $output
113
     * @param Client          $client
114
     */
115
    private function selectResponseTypes(InputInterface $input, OutputInterface $output, Client $client)
116
    {
117
        $authorization_factory = $this->getContainer()->get('oauth2_server.response_type.manager');
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $authorization_factory exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
118
119
        $helper = $this->getHelper('question');
120
        $question = new ChoiceQuestion(
121
            'Please select allowed response types.',
122
            $authorization_factory->getSupportedResponseTypes()
123
        );
124
        $question->setMultiselect(true);
125
        $question->setErrorMessage('The response type "%s" is invalid.');
126
127
        $grant_types = $helper->ask($input, $output, $question);
128
        $client->set('response_types', $grant_types);
129
    }
130
131
    /**
132
     * @param InputInterface  $input
133
     * @param OutputInterface $output
134
     * @param Client          $client
135
     */
136
    private function selectGrantTypes(InputInterface $input, OutputInterface $output, Client $client)
137
    {
138
        $token_endpoint = $this->getContainer()->get('oauth2_server.grant_type.manager');
139
140
        $helper = $this->getHelper('question');
141
        $question = new ChoiceQuestion(
142
            'Please select allowed grant types.',
143
            $token_endpoint->getSupportedGrantTypes()
144
        );
145
        $question->setMultiselect(true);
146
        $question->setErrorMessage('The grant type "%s" is invalid.');
147
148
        $grant_types = $helper->ask($input, $output, $question);
149
        $client->set('grant_types', $grant_types);
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155
    public function isEnabled()
156
    {
157
        return $this->getContainer()->has('oauth2_server.client_manager');
158
    }
159
160
    /**
161
     * @param string $uri
162
     * @param bool   $path_traversal_allowed
163
     *
164
     * @return bool
165
     */
166
    private static function isAnUrlOrUrn($uri, $path_traversal_allowed)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $path_traversal_allowed exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
167
    {
168
        if ('urn:' === mb_substr($uri, 0, 4, '8bit')) {
169
            if (false === self::checkUrn($uri)) {
170
                return false;
171
            }
172
        } else {
173
            if (false === self::checkUrl($uri, $path_traversal_allowed)) {
174
                return false;
175
            }
176
        }
177
178
        return true;
179
    }
180
181
    /**
182
     * @param string $url
183
     * @param bool   $path_traversal_allowed
184
     *
185
     * @return bool
186
     */
187
    public static function checkUrl($url, $path_traversal_allowed)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $path_traversal_allowed exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
188
    {
189
        // If URI is not a valid URI, return false
190
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
191
            return false;
192
        }
193
194
        $parsed = parse_url($url);
195
196
        // Checks for path traversal (e.g. http://foo.bar/redirect/../bad/url)
197
        if (isset($parsed['path']) && !$path_traversal_allowed) {
198
            $path = urldecode($parsed['path']);
199
            // check for 'path traversal'
200
            if (preg_match('#/\.\.?(/|$)#', $path)) {
201
                return false;
202
            }
203
        }
204
205
        return true;
206
    }
207
208
    /**
209
     * @param string $urn
210
     *
211
     * @return bool
212
     */
213
    private static function checkUrn($urn)
214
    {
215
        return 1 === preg_match('/^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,\-.:=@;$_!*\'%\/?#]+$/', $urn);
216
    }
217
}
218