Failed Conditions
Pull Request — master (#22)
by Florent
08:54
created

ClientManagerPlugin::isConfigurationPathValid()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 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\Server\ClientManagerPlugin;
15
16
use Assert\Assertion;
17
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
18
use Matthias\BundlePlugins\BundlePlugin;
19
use OAuth2Framework\Bundle\Server\ClientManagerPlugin\DependencyInjection\Compiler\AssertionJwtMetadataCompilerPass;
20
use OAuth2Framework\Bundle\Server\ClientManagerPlugin\DependencyInjection\Compiler\ClientAssertionJWTEncryptionSupportConfigurationCompilerPass;
21
use OAuth2Framework\Bundle\Server\ClientManagerPlugin\DependencyInjection\Compiler\ClientConfigurationRouteCompilerPass;
22
use OAuth2Framework\Bundle\Server\ClientManagerPlugin\DependencyInjection\Compiler\ClientManagementCompilerPass;
23
use OAuth2Framework\Bundle\Server\ClientManagerPlugin\DependencyInjection\Compiler\ClientRegistrationRouteCompilerPass;
24
use OAuth2Framework\Bundle\Server\ClientManagerPlugin\DependencyInjection\Compiler\ClientRuleCompilerPass;
25
use OAuth2Framework\Bundle\Server\CommonPluginMethod;
26
use SpomkyLabs\JoseBundle\Helper\ConfigurationHelper;
27
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
28
use Symfony\Component\Config\FileLocator;
29
use Symfony\Component\DependencyInjection\ContainerBuilder;
30
use Symfony\Component\DependencyInjection\ContainerInterface;
31
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
32
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
33
use Symfony\Component\PropertyAccess\PropertyAccess;
34
35
class ClientManagerPlugin extends CommonPluginMethod implements BundlePlugin, PrependExtensionInterface
36
{
37
    const ERROR_EMPTY_CLIENT_ASSERTION_SIGNATURE_ALGORITHMS = 'The parameter "signature_algorithms" must be set when the "client_assertion_jwt" authentication method is enabled.';
38
    const ERROR_EMPTY_CLIENT_ASSERTION_KEY_ENCRYPTION_ALGORITHMS = 'The parameter "encryption.key_encryption_algorithms" must be set when "client_assertion_jwt" authentication method and encryption support are enabled.';
39
    const ERROR_EMPTY_CLIENT_ASSERTION_CONTENT_ENCRYPTION_ALGORITHMS = 'The parameter "encryption.content_encryption_algorithms" must be set when "client_assertion_jwt" authentication method and encryption support are enabled.';
40
    const ERROR_EMPTY_CLIENT_ASSERTION_KEY_SET = 'The parameter "encryption.key_set" must be set when "client_assertion_jwt" authentication method and encryption support are enabled.';
41
    const ERROR_EMPTY_CLIENT_SECRET_BASIC_REALM = 'The child node "realm" at path must be configured.';
42
    const ERROR_INVALID_REGISTRATION_PATH = 'The parameter "registration_path" must be set.';
43
    const ERROR_INVALID_CONFIGURATION_PATH = 'The parameter "configuration_path" must be set.';
44
    const ERROR_INVALID_INITIAL_ACCESS_TOKEN_CLASS = 'The Initial Access Token class is not set or does not exist.';
45
    const ERROR_INVALID_INITIAL_ACCESS_TOKEN_MANAGER = 'The Initial Access Token manager must be set.';
46
    const ERROR_INVALID_SOFTWARE_STATEMENT_KEY_SET = 'The Software Statement key set must be set.';
47
    const ERROR_INVALID_SOFTWARE_STATEMENT_ALGORITHM = 'The Software Statement algorithm must be set.';
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function name()
53
    {
54
        return 'client_manager';
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function build(ContainerBuilder $container)
61
    {
62
        $mappings = [
63
            realpath(__DIR__.'/Resources/config/doctrine-mapping') => 'OAuth2Framework\Bundle\Server\ClientManagerPlugin\Model',
64
        ];
65
        if (class_exists('Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass')) {
66
            $container->addCompilerPass(DoctrineOrmMappingsPass::createYamlMappingDriver($mappings, []));
67
        }
68
69
        $container->addCompilerPass(new AssertionJwtMetadataCompilerPass());
70
        $container->addCompilerPass(new ClientAssertionJWTEncryptionSupportConfigurationCompilerPass());
71
        $container->addCompilerPass(new ClientManagementCompilerPass());
72
        $container->addCompilerPass(new ClientRegistrationRouteCompilerPass());
73
        $container->addCompilerPass(new ClientConfigurationRouteCompilerPass());
74
        $container->addCompilerPass(new ClientRuleCompilerPass());
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function load(array $pluginConfiguration, ContainerBuilder $container)
81
    {
82
        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/Resources/config'));
83
        $files = ['services'];
84
85
        $parameters = [
86
            'oauth2_server.client_manager' => ['type' => 'alias', 'path' => '[manager]'],
87
            'oauth2_server.client_manager.class' => ['type' => 'parameter', 'path' => '[class]'],
88
        ];
89
90
        if (true === $pluginConfiguration['token_endpoint_auth_methods']['none']) {
91
            $files[] = 'token_endpoint_auth_method.none';
92
        }
93
        if (true === $pluginConfiguration['token_endpoint_auth_methods']['client_secret_basic']['enabled']) {
94
            $files[] = 'token_endpoint_auth_method.client_secret_basic';
95
            $parameters['oauth2_server.token_endpoint_auth_method.client_secret_basic.realm'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_secret_basic][realm]'];
96
        }
97
        if (true === $pluginConfiguration['token_endpoint_auth_methods']['client_secret_post']) {
98
            $files[] = 'token_endpoint_auth_method.client_secret_post';
99
        }
100
        if (true === $pluginConfiguration['token_endpoint_auth_methods']['client_assertion_jwt']['enabled']) {
101
            $files[] = 'token_endpoint_auth_method.client_assertion_jwt';
102
            $accessor = PropertyAccess::createPropertyAccessor();
103
104
            $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.signature_algorithms'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][signature_algorithms]'];
105
            $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.claim_checkers'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][claim_checkers]'];
106
            $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.header_checkers'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][header_checkers]'];
107
108
            $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.encryption.enabled'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][encryption][enabled]'];
109
            if (true === $accessor->getValue($pluginConfiguration, '[token_endpoint_auth_methods][client_assertion_jwt][encryption][enabled]')) {
110
                $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.encryption.key_encryption_algorithms'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][encryption][key_encryption_algorithms]'];
111
                $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.encryption.content_encryption_algorithms'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][encryption][content_encryption_algorithms]'];
112
                $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.encryption.required'] = ['type' => 'parameter', 'path' => '[token_endpoint_auth_methods][client_assertion_jwt][encryption][required]'];
113
                $parameters['oauth2_server.token_endpoint_auth_method.client_assertion_jwt.encryption.key_set'] = ['type' => 'alias',     'path' => '[token_endpoint_auth_methods][client_assertion_jwt][encryption][key_set]'];
114
            }
115
        }
116
117
        $parameters['oauth2_server.client_manager.management.enabled'] = ['type' => 'parameter', 'path' => '[management][enabled]'];
118
        if (true === $pluginConfiguration['management']['enabled']) {
119
            $files[] = 'management_endpoint';
120
            $parameters['oauth2_server.client_manager.management.registration_path'] = ['type' => 'parameter', 'path' => '[management][registration_path]'];
121
            $parameters['oauth2_server.client_manager.management.configuration_path'] = ['type' => 'parameter', 'path' => '[management][configuration_path]'];
122
123
            $parameters['oauth2_server.initial_access_token.enabled'] = ['type' => 'parameter', 'path' => '[management][initial_access_token][enabled]'];
124
            if (true === $pluginConfiguration['management']['initial_access_token']['enabled']) {
125
                $files[] = 'initial_access_token';
126
                $parameters['oauth2_server.initial_access_token.required'] = ['type' => 'parameter', 'path' => '[management][initial_access_token][required]'];
127
                $parameters['oauth2_server.initial_access_token.class'] = ['type' => 'parameter', 'path' => '[management][initial_access_token][class]'];
128
                $parameters['oauth2_server.initial_access_token.manager'] = ['type' => 'alias', 'path' => '[management][initial_access_token][manager]'];
129
            }
130
131
            $parameters['oauth2_server.software_statement.enabled'] = ['type' => 'parameter', 'path' => '[management][software_statement][enabled]'];
132
            if (true === $pluginConfiguration['management']['software_statement']['enabled']) {
133
                $parameters['oauth2_server.software_statement.required'] = ['type' => 'parameter', 'path' => '[management][software_statement][required]'];
134
                $parameters['oauth2_server.software_statement.key_set'] = ['type' => 'alias', 'path' => '[management][software_statement][key_set]'];
135
                $parameters['oauth2_server.software_statement.signature_algorithms'] = ['type' => 'parameter', 'path' => '[management][software_statement][signature_algorithms]'];
136
            }
137
        }
138
139
        foreach ($files as $basename) {
140
            $loader->load(sprintf('%s.yml', $basename));
141
        }
142
143
        $this->loadParameters($parameters, $pluginConfiguration, $container);
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function addConfiguration(ArrayNodeDefinition $pluginNode)
150
    {
151
        $pluginNode
152
            ->isRequired()
153
            ->addDefaultsIfNotSet()
154
            ->children()
155
                ->scalarNode('class')
156
                    ->info('The Client class.')
157
                    ->isRequired()
158
                    ->validate()
159
                    ->ifTrue(function ($value) {
160
                        return !class_exists($value);
161
                    })
162
                    ->thenInvalid('The class does not exist.')
163
                    ->end()
164
                ->end()
165
                ->scalarNode('manager')
166
                    ->info('The Client manager.')
167
                    ->defaultValue('oauth2_server.client_manager.default')
168
                ->end()
169
            ->end();
170
        $this->addTokenEndpointAuthMethodsSection($pluginNode);
171
        $this->addManagementSection($pluginNode);
172
    }
173
174
    /**
175
     * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node
176
     */
177
    private function addTokenEndpointAuthMethodsSection(ArrayNodeDefinition $node)
178
    {
179
        $node->children()
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method addDefaultsIfNotSet() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
180
            ->arrayNode('token_endpoint_auth_methods')
181
                ->addDefaultsIfNotSet()
182
                ->children()
183
                    ->booleanNode('none')
184
                        ->info('The "none" authentication method. Should be enabled to allow public clients to be authenticated against the endpoints other than the authorization one (e.g. Token Endpoint, Token Introspection Endpoint...)')
185
                        ->defaultTrue()
186
                    ->end()
187
                    ->arrayNode('client_secret_basic')
188
                        ->info('The "client_secret_basic" authentication method. Must be enabled to allow confidential clients with a secret to be authenticated using the authorization header. This method is recommended.')
189
                        ->validate()->ifTrue($this->isClientSecretBasicRealmInvalid())->thenInvalid(self::ERROR_EMPTY_CLIENT_SECRET_BASIC_REALM)->end()
190
                        ->addDefaultsIfNotSet()
191
                        ->children()
192
                            ->booleanNode('enabled')
193
                                ->info('If true, the "client_secret_basic" authentication method is enabled.')
194
                                ->defaultTrue()
195
                            ->end()
196
                            ->scalarNode('realm')
197
                                ->info('The realm display during the authentication process.')
198
                            ->end()
199
                        ->end()
200
                    ->end()
201
                    ->booleanNode('client_secret_post')
202
                        ->info('The "client_secret_post" authentication method. Should be enabled to allow confidential clients with a secret to be authenticated using the request body parameters. This method is not recommended.')
203
                        ->defaultFalse()
204
                    ->end()
205
                    ->arrayNode('client_assertion_jwt')
206
                        ->validate()->ifTrue($this->areClientAssertionSignatureAlgorithmsInvalid())->thenInvalid(self::ERROR_EMPTY_CLIENT_ASSERTION_SIGNATURE_ALGORITHMS)->end()
207
                        ->validate()->ifTrue($this->isClientAssertionEncryptionParameterInvalid('key_encryption_algorithms'))->thenInvalid(self::ERROR_EMPTY_CLIENT_ASSERTION_KEY_ENCRYPTION_ALGORITHMS)->end()
208
                        ->validate()->ifTrue($this->isClientAssertionEncryptionParameterInvalid('content_encryption_algorithms'))->thenInvalid(self::ERROR_EMPTY_CLIENT_ASSERTION_CONTENT_ENCRYPTION_ALGORITHMS)->end()
209
                        ->validate()->ifTrue($this->isClientAssertionEncryptionParameterInvalid('key_set'))->thenInvalid(self::ERROR_EMPTY_CLIENT_ASSERTION_KEY_SET)->end()
210
                        ->info('The "client_secret_jwt" and "private_key_jwt" authentication methods. Should be enabled to allow confidential clients with a secret or keys to be authenticated using the JWT assertions. These methods are recommended, but require cryptographic knowledge.')
211
                        ->addDefaultsIfNotSet()
212
                        ->children()
213
                            ->booleanNode('enabled')->defaultFalse()->end()
214
                            ->arrayNode('signature_algorithms')
215
                                ->info('Supported signature algorithms.')
216
                                ->useAttributeAsKey('name')
217
                                ->prototype('scalar')->end()
218
                                ->treatNullLike([])
219
                            ->end()
220
                            ->arrayNode('claim_checkers')
221
                                ->info('Checkers will verify the JWT claims.')
222
                                ->useAttributeAsKey('name')
223
                                ->prototype('scalar')->end()
224
                                ->treatNullLike(['exp', 'iat', 'nbf'])
225
                            ->end()
226
                            ->arrayNode('header_checkers')
227
                                ->info('Checkers will verify the JWT headers.')
228
                                ->useAttributeAsKey('name')
229
                                ->prototype('scalar')->end()
230
                                ->treatNullLike(['crit'])
231
                            ->end()
232
                            ->arrayNode('encryption')
233
                                ->addDefaultsIfNotSet()
234
                                ->children()
235
                                    ->booleanNode('enabled')
236
                                        ->defaultFalse()
237
                                        ->info('If true, encrypted assertion support is enabled. Key set and encryption algorithms must be set..')
238
                                    ->end()
239
                                    ->booleanNode('required')
240
                                        ->defaultFalse()
241
                                        ->info('If true, assertion must be encrypted by clients.')
242
                                    ->end()
243
                                    ->scalarNode('key_set')
244
                                        ->info('Key set used to decrypt the assertion.')
245
                                        ->defaultNull()
246
                                    ->end()
247
                                    ->arrayNode('key_encryption_algorithms')
248
                                        ->info('Supported key encryption algorithms.')
249
                                        ->useAttributeAsKey('name')
250
                                        ->prototype('scalar')->end()
251
                                        ->treatNullLike([])
252
                                    ->end()
253
                                    ->arrayNode('content_encryption_algorithms')
254
                                        ->info('Supported content encryption algorithms.')
255
                                        ->useAttributeAsKey('name')
256
                                        ->prototype('scalar')->end()
257
                                        ->treatNullLike([])
258
                                    ->end()
259
                                ->end()
260
                            ->end()
261
                        ->end()
262
                    ->end()
263
                ->end()
264
            ->end()
265
        ->end();
266
    }
267
268
    /**
269
     * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node
270
     */
271
    private function addManagementSection(ArrayNodeDefinition $node)
272
    {
273
        $node->children()
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method addDefaultsIfNotSet() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
274
            ->arrayNode('management')
275
                ->validate()->ifTrue($this->isRegistrationPathValid())->thenInvalid(self::ERROR_INVALID_REGISTRATION_PATH)->end()
276
                ->validate()->ifTrue($this->isConfigurationPathValid())->thenInvalid(self::ERROR_INVALID_CONFIGURATION_PATH)->end()
277
                ->addDefaultsIfNotSet()
278
                ->children()
279
                    ->booleanNode('enabled')
280
                        ->info('If true, Client Registration and Client Configuration Endpoint will be enabled.')
281
                        ->defaultFalse()
282
                    ->end()
283
                    ->scalarNode('registration_path')
284
                        ->info('The path to the client registration endpoint.')
285
                        ->defaultValue('/client/register')
286
                    ->end()
287
                    ->scalarNode('configuration_path')
288
                        ->info('The path to the client configuration endpoint. It must contain the {client_id} parameters.')
289
                        ->defaultValue('/client/register/{client_id}')
290
                    ->end()
291
                    ->arrayNode('initial_access_token')
292
                        ->validate()->ifTrue($this->isInitialAccessTokenClassValid())->thenInvalid(self::ERROR_INVALID_INITIAL_ACCESS_TOKEN_CLASS)->end()
293
                        ->validate()->ifTrue($this->isInitialAccessTokenManagerValid())->thenInvalid(self::ERROR_INVALID_INITIAL_ACCESS_TOKEN_MANAGER)->end()
294
                        ->addDefaultsIfNotSet()
295
                        ->children()
296
                            ->booleanNode('enabled')
297
                                ->info('If true, Initial Access Token support is enabled.')
298
                                ->defaultFalse()
299
                            ->end()
300
                            ->booleanNode('required')
301
                                ->info('If true, an Initial Access Token is required to register a new client.')
302
                                ->defaultFalse()
303
                            ->end()
304
                            ->scalarNode('class')
305
                                ->info('Initial Access Token class.')
306
                                ->defaultNull()
307
                            ->end()
308
                            ->scalarNode('manager')
309
                                ->info('Initial Access Token manager.')
310
                                ->defaultValue('oauth2_server.initial_access_token.manager.default')
311
                            ->end()
312
                        ->end()
313
                    ->end()
314
                    ->arrayNode('software_statement')
315
                        ->validate()->ifTrue($this->isSoftwareStatementKeySetValid())->thenInvalid(self::ERROR_INVALID_SOFTWARE_STATEMENT_KEY_SET)->end()
316
                        ->validate()->ifTrue($this->isSoftwareStatementAlgorithmValid())->thenInvalid(self::ERROR_INVALID_SOFTWARE_STATEMENT_ALGORITHM)->end()
317
                        ->addDefaultsIfNotSet()
318
                        ->children()
319
                            ->booleanNode('enabled')
320
                                ->info('If true, Software Statement support is enabled.')
321
                                ->defaultFalse()
322
                            ->end()
323
                            ->booleanNode('required')
324
                                ->info('If true, an Software Statement is required to register a new client.')
325
                                ->defaultFalse()
326
                            ->end()
327
                            ->scalarNode('key_set')
328
                                ->info('Signature key set.')
329
                                ->defaultNull()
330
                            ->end()
331
                            ->scalarNode('signature_algorithm')
332
                                ->info('Supported signature algorithm.')
333
                                ->defaultNull()
334
                            ->end()
335
                        ->end()
336
                    ->end()
337
                ->end()
338
            ->end()
339
        ->end();
340
    }
341
342
    /**
343
     * {@inheritdoc}
344
     */
345
    public function boot(ContainerInterface $container)
346
    {
347
    }
348
349
    /**
350
     * {@inheritdoc}
351
     */
352
    public function prepend(ContainerBuilder $container)
353
    {
354
        $bundle_config = current($container->getExtensionConfig('oauth2_server'))[$this->name()];
355
356
        if (true === $bundle_config['management']['enabled']) {
357
            $plugins = $container->getExtensionConfig('oauth2_server');
358
            if (!empty($plugins)) {
359
                $plugins = array_shift($plugins);
360
            }
361
            Assertion::keyExists($plugins, 'bearer_token', 'The plugin "BearerTokenPlugin" must be enabled to use the client management endpoints.');
362
363
            if (true === $bundle_config['management']['software_statement']['enabled']) {
364
                $this->updateJoseBundleConfigurationForVerifier($container, 'oauth2_server_software_statement', ['signature_algorithms' => [$bundle_config['management']['software_statement']['signature_algorithm']]]);
365
                $this->updateJoseBundleConfigurationForChecker($container, 'oauth2_server_software_statement', ['header_checkers' => [], 'claim_checkers' => []]);
366
                $this->updateJoseBundleConfigurationForJWTLoader($container, 'oauth2_server_software_statement', ['encryption' => ['enabled' => false]]);
367
            }
368
        }
369
370
        if (true === $bundle_config['token_endpoint_auth_methods']['client_assertion_jwt']['enabled']) {
371
            $this->updateJoseBundleConfigurationForVerifier($container, 'client_manager_client_assertion_jwt', $bundle_config['token_endpoint_auth_methods']['client_assertion_jwt']);
372
            $this->updateJoseBundleConfigurationForDecrypter($container, 'client_manager_client_assertion_jwt', $bundle_config['token_endpoint_auth_methods']['client_assertion_jwt']);
373
            $this->updateJoseBundleConfigurationForChecker($container, 'client_manager_client_assertion_jwt', $bundle_config['token_endpoint_auth_methods']['client_assertion_jwt']);
374
            $this->updateJoseBundleConfigurationForJWTLoader($container, 'client_manager_client_assertion_jwt', $bundle_config['token_endpoint_auth_methods']['client_assertion_jwt']);
375
        }
376
    }
377
378
    /**
379
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
380
     * @param string                                                  $service_name
381
     * @param array                                                   $bundle_config
382
     */
383
    private function updateJoseBundleConfigurationForVerifier(ContainerBuilder $container, $service_name, array $bundle_config)
384
    {
385
        ConfigurationHelper::addVerifier($container, $service_name, $bundle_config['signature_algorithms'], false);
386
    }
387
388
    /**
389
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
390
     * @param string                                                  $service_name
391
     * @param array                                                   $bundle_config
392
     */
393
    private function updateJoseBundleConfigurationForDecrypter(ContainerBuilder $container, $service_name, array $bundle_config)
394
    {
395
        if (true === $bundle_config['encryption']['enabled']) {
396
            ConfigurationHelper::addDecrypter($container, $service_name, $bundle_config['encryption']['key_encryption_algorithms'], $bundle_config['encryption']['content_encryption_algorithms'], ['DEF'], false);
397
        }
398
    }
399
400
    /**
401
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
402
     * @param string                                                  $service_name
403
     * @param array                                                   $bundle_config
404
     */
405
    private function updateJoseBundleConfigurationForChecker(ContainerBuilder $container, $service_name, array $bundle_config)
406
    {
407
        ConfigurationHelper::addChecker($container, $service_name, $bundle_config['header_checkers'], $bundle_config['claim_checkers'], false);
408
    }
409
410
    /**
411
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
412
     * @param string                                                  $service_name
413
     * @param array                                                   $bundle_config
414
     */
415
    private function updateJoseBundleConfigurationForJWTLoader(ContainerBuilder $container, $service_name, array $bundle_config)
416
    {
417
        $decrypter = null;
418
        if (true === $bundle_config['encryption']['enabled']) {
419
            $decrypter = sprintf('jose.decrypter.%s', $service_name);
420
        }
421
        ConfigurationHelper::addJWTLoader($container, $service_name, sprintf('jose.verifier.%s', $service_name), sprintf('jose.checker.%s', $service_name), $decrypter, false);
422
    }
423
424
    /**
425
     * @return \Closure
426
     */
427
    private function areClientAssertionSignatureAlgorithmsInvalid()
428
    {
429
        return function ($data) {
430
            if (false === $data['enabled']) {
431
                return false;
432
            }
433
434
            return empty($data['signature_algorithms']);
435
        };
436
    }
437
438
    /**
439
     * @param string $parameter
440
     *
441
     * @return \Closure
442
     */
443
    private function isClientAssertionEncryptionParameterInvalid($parameter)
444
    {
445
        return function ($data) use ($parameter) {
446
            if (false === $data['encryption']['enabled']) {
447
                return false;
448
            }
449
450
            return empty($data['encryption'][$parameter]);
451
        };
452
    }
453
454
    /**
455
     * @return \Closure
456
     */
457
    private function isClientSecretBasicRealmInvalid()
458
    {
459
        return function ($data) {
460
            if (false === $data['enabled']) {
461
                return false;
462
            }
463
464
            return empty($data['realm']);
465
        };
466
    }
467
468
    /**
469
     * @return \Closure
470
     */
471
    private function isRegistrationPathValid()
472
    {
473
        return function ($data) {
474
            return true === $data['enabled'] && empty($data['registration_path']);
475
        };
476
    }
477
478
    /**
479
     * @return \Closure
480
     */
481
    private function isConfigurationPathValid()
482
    {
483
        return function ($data) {
484
            return true === $data['enabled'] && empty($data['configuration_path']);
485
        };
486
    }
487
488
    /**
489
     * @return \Closure
490
     */
491
    private function isInitialAccessTokenClassValid()
492
    {
493
        return function ($data) {
494
            return true === $data['enabled'] && (empty($data['class']) || !class_exists($data['class']));
495
        };
496
    }
497
498
    /**
499
     * @return \Closure
500
     */
501
    private function isInitialAccessTokenManagerValid()
502
    {
503
        return function ($data) {
504
            return true === $data['enabled'] && empty($data['class']);
505
        };
506
    }
507
508
    /**
509
     * @return \Closure
510
     */
511
    private function isSoftwareStatementKeySetValid()
512
    {
513
        return function ($data) {
514
            return true === $data['enabled'] && empty($data['key_set']);
515
        };
516
    }
517
518
    /**
519
     * @return \Closure
520
     */
521
    private function isSoftwareStatementAlgorithmValid()
522
    {
523
        return function ($data) {
524
            return true === $data['enabled'] && empty($data['signature_algorithm']);
525
        };
526
    }
527
}
528