Passed
Push — master ( c4afc2...9cde23 )
by Pieter van der
27:49 queued 12:42
created

loadProviderConfiguration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 74
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 48
nc 2
nop 4
dl 0
loc 74
rs 9.1344
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupSelfService\SamlStepupProviderBundle\DependencyInjection;
20
21
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
22
use Symfony\Component\Config\FileLocator;
23
use Symfony\Component\DependencyInjection\ContainerBuilder;
24
use Symfony\Component\DependencyInjection\Definition;
25
use Symfony\Component\DependencyInjection\Loader;
26
use Symfony\Component\DependencyInjection\Reference;
27
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
28
29
/**
30
 * @SuppressWarnings(PHPMD.LongClassName)
31
 */
32
class SurfnetStepupSelfServiceSamlStepupProviderExtension extends Extension
33
{
34
    const VIEW_CONFIG_TAG_NAME = 'gssp.view_config';
35
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public function load(array $configs, ContainerBuilder $container)
40
    {
41
        $configuration = new Configuration();
42
        $config = $this->processConfiguration($configuration, $configs);
43
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
44
        $loader->load('services.yml');
45
46
        foreach ($config['providers'] as $provider => $providerConfiguration) {
47
            // may seem a bit strange, but this prevents casing issue when getting/setting/creating provider
48
            // service definitions etc.
49
            if ($provider !== strtolower($provider)) {
50
                throw new InvalidConfigurationException('The provider name must be completely lowercase');
51
            }
52
53
            $this->loadProviderConfiguration($provider, $providerConfiguration, $config['routes'], $container);
54
        }
55
    }
56
57
    private function loadProviderConfiguration(
58
        $provider,
59
        array $configuration,
60
        array $routes,
61
        ContainerBuilder $container
62
    ) {
63
64
        if ($container->has('gssp.provider.' . $provider)) {
65
            throw new InvalidConfigurationException(sprintf('Cannot create the same provider "%s" twice', $provider));
66
        }
67
68
        $this->createHostedDefinitions($provider, $configuration['hosted'], $routes, $container);
69
        $this->createMetadataDefinition($provider, $configuration['hosted'], $routes, $container);
70
        $this->createRemoteDefinition($provider, $configuration['remote'], $container);
71
72
        $stateHandlerDefinition = new Definition(
73
            'Surfnet\StepupSelfService\SamlStepupProviderBundle\Saml\StateHandler',
74
            [
75
                new Reference('gssp.sessionbag'),
76
                $provider
77
            ]
78
        );
79
        $container->setDefinition('gssp.provider.' . $provider . '.statehandler', $stateHandlerDefinition);
80
81
        $providerDefinition = new Definition('Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\Provider', [
82
            $provider,
83
            new Reference('gssp.provider.' . $provider . '.hosted.sp'),
84
            new Reference('gssp.provider.' . $provider . '.remote.idp'),
85
            new Reference('gssp.provider.' . $provider . '.statehandler')
86
        ]);
87
88
        $providerDefinition->setPublic(false);
89
        $container->setDefinition('gssp.provider.' . $provider, $providerDefinition);
90
91
        // When the android url is set, the description should contain the android play store url parameter.
92
        // The same goes for the iOs app url.
93
        $this->validateDescriptions(
94
            $configuration['view_config']['description'],
95
            $configuration['view_config']['app_android_url'],
96
            $provider,
97
            'android'
98
        );
99
100
        $this->validateDescriptions(
101
            $configuration['view_config']['description'],
102
            $configuration['view_config']['app_ios_url'],
103
            $provider,
104
            'ios'
105
        );
106
107
        $viewConfigDefinition = new Definition('Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\ViewConfig', [
108
            new Reference('request_stack'),
109
            $configuration['view_config']['loa'],
110
            $configuration['view_config']['logo'],
111
            $configuration['view_config']['app_android_url'],
112
            $configuration['view_config']['app_ios_url'],
113
            $configuration['view_config']['alt'],
114
            $configuration['view_config']['title'],
115
            $configuration['view_config']['description'],
116
            $configuration['view_config']['button_use'],
117
            $configuration['view_config']['initiate_title'],
118
            $configuration['view_config']['initiate_button'],
119
            $configuration['view_config']['explanation'],
120
            $configuration['view_config']['authn_failed'],
121
            $configuration['view_config']['pop_failed'],
122
        ]);
123
        $viewConfigDefinition->addTag(self::VIEW_CONFIG_TAG_NAME);
124
        $viewConfigDefinition->setPublic(true);
125
126
        $container->setDefinition('gssp.view_config.' . $provider, $viewConfigDefinition);
127
128
        $container
129
            ->getDefinition('gssp.provider_repository')
130
            ->addMethodCall('addProvider', [new Reference('gssp.provider.' . $provider)]);
131
    }
132
133
    /**
134
     * @param string           $provider
135
     * @param array            $configuration
136
     * @param array            $routes
137
     * @param ContainerBuilder $container
138
     */
139
    private function createHostedDefinitions(
140
        $provider,
141
        array $configuration,
142
        array $routes,
143
        ContainerBuilder $container
144
    ) {
145
        $hostedDefinition = $this->buildHostedEntityDefinition($provider, $configuration, $routes);
146
        $container->setDefinition('gssp.provider.' . $provider . '.hosted_entities', $hostedDefinition);
147
148
        $hostedSpDefinition  = (new Definition())
149
            ->setClass('Surfnet\SamlBundle\Entity\ServiceProvider')
150
            ->setFactory([
151
                new Reference('gssp.provider.' . $provider . '.hosted_entities'),
152
                'getServiceProvider'
153
            ])
154
            ->setPublic(false);
155
        $container->setDefinition('gssp.provider.' . $provider . '.hosted.sp', $hostedSpDefinition);
156
    }
157
158
    /**
159
     * @param string $provider
160
     * @param array  $configuration
161
     * @param array  $routes
162
     * @return Definition
163
     */
164
    private function buildHostedEntityDefinition($provider, array $configuration, array $routes)
165
    {
166
        $entityId = ['entity_id_route' => $this->createRouteConfig($provider, $routes['metadata'])];
167
        $spAdditional = [
168
            'enabled' => true,
169
            'assertion_consumer_route' => $this->createRouteConfig($provider, $routes['consume_assertion'])
170
        ];
171
        $idpAdditional = [
172
            'enabled' => false,
173
        ];
174
175
        $serviceProvider  = array_merge($configuration['service_provider'], $spAdditional, $entityId);
176
        $identityProvider = array_merge($idpAdditional, $entityId);
177
178
        $hostedDefinition = new Definition('Surfnet\SamlBundle\Entity\HostedEntities', [
179
            new Reference('router'),
180
            new Reference('request_stack'),
181
            $serviceProvider,
182
            $identityProvider
183
        ]);
184
185
        $hostedDefinition->setPublic(false);
186
187
        return $hostedDefinition;
188
    }
189
190
    /**
191
     * @param string           $provider
192
     * @param array            $configuration
193
     * @param ContainerBuilder $container
194
     */
195
    private function createRemoteDefinition($provider, array $configuration, ContainerBuilder $container)
196
    {
197
        $definition    = new Definition('Surfnet\SamlBundle\Entity\IdentityProvider', [
198
            [
199
                'entityId'        => $configuration['entity_id'],
200
                'ssoUrl'          => $configuration['sso_url'],
201
                'certificateData' => $configuration['certificate'],
202
            ]
203
        ]);
204
205
        $definition->setPublic(false);
206
        $container->setDefinition('gssp.provider.' . $provider . '.remote.idp', $definition);
207
    }
208
209
    /**
210
     * @param string           $provider
211
     * @param array            $configuration
212
     * @param array            $routes
213
     * @param ContainerBuilder $container
214
     * @return Definition
215
     */
216
    private function createMetadataDefinition(
217
        $provider,
218
        array $configuration,
219
        array $routes,
220
        ContainerBuilder $container
221
    ) {
222
        $metadataConfiguration = new Definition('Surfnet\SamlBundle\Metadata\MetadataConfiguration');
223
224
        $propertyMap = [
225
            'entityIdRoute'          => $this->createRouteConfig($provider, $routes['metadata']),
226
            'isSp'                   => true,
227
            'assertionConsumerRoute' => $this->createRouteConfig($provider, $routes['consume_assertion']),
228
            'isIdP'                  => false,
229
            'publicKey'              => $configuration['metadata']['public_key'],
230
            'privateKey'             => $configuration['metadata']['private_key'],
231
        ];
232
233
        $metadataConfiguration->setProperties($propertyMap);
234
        $metadataConfiguration->setPublic(false);
235
        $container->setDefinition('gssp.provider.' . $provider . 'metadata.configuration', $metadataConfiguration);
236
237
        $metadataFactory = new Definition('Surfnet\SamlBundle\Metadata\MetadataFactory', [
238
            new Reference('templating'),
239
            new Reference('router'),
240
            new Reference('surfnet_saml.signing_service'),
241
            new Reference('gssp.provider.' . $provider . 'metadata.configuration')
242
        ]);
243
        $container->setDefinition('gssp.provider.' . $provider . '.metadata.factory', $metadataFactory);
244
    }
245
246
    private function createRouteConfig($provider, $routeName)
247
    {
248
        // In the future, we ought to wrap this in an object.
249
        // https://www.pivotaltracker.com/story/show/90095392
250
        return [
251
            'route'      => $routeName,
252
            'parameters' => ['provider' => $provider]
253
        ];
254
    }
255
256
    private function validateDescriptions($descriptions, $appUrl, $provider, $type)
257
    {
258
        $regex ="/%%{$type}_link_start%%[a-zA-Z0-9 ]+%%{$type}_link_end%%/";
259
        foreach ($descriptions as $lang => $description) {
260
            if ($appUrl !== false && preg_match($regex, $description) === 0) {
261
                throw new InvalidConfigurationException(
262
                    sprintf(
263
                        'You have configured a GSSP provider with app URL\'s but the description is not ' .
264
                        'configured correctly yet. Missing "%%%1$s_link_start%%" or "%%%1$s_link_end%%" in ' .
265
                        'GSSP description for language "%2$s" in "providers.%3$s.view_config.description" of '.
266
                        'samlstepupproviders.yml',
267
                        $type,
268
                        $lang,
269
                        $provider
270
                    )
271
                );
272
            }
273
        }
274
    }
275
}
276