Passed
Push — master ( c48dee...d4613e )
by Daniel
06:59
created

loadServiceConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2.004

Importance

Changes 0
Metric Value
cc 2
eloc 9
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 14
ccs 9
cts 10
cp 0.9
crap 2.004
rs 9.9666
1
<?php
2
3
/*
4
 * This file is part of the Silverback API Components Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentsBundle\DependencyInjection;
15
16
use Exception;
17
use Ramsey\Uuid\Doctrine\UuidType;
18
use Silverback\ApiComponentsBundle\AnnotationReader\UploadableAnnotationReader;
19
use Silverback\ApiComponentsBundle\Doctrine\Extension\ORM\RouteExtension;
20
use Silverback\ApiComponentsBundle\Doctrine\Extension\ORM\TablePrefixExtension;
21
use Silverback\ApiComponentsBundle\Entity\Core\ComponentInterface;
22
use Silverback\ApiComponentsBundle\Exception\ApiPlatformAuthenticationException;
23
use Silverback\ApiComponentsBundle\Exception\UnparseableRequestHeaderException;
24
use Silverback\ApiComponentsBundle\Exception\UserDisabledException;
25
use Silverback\ApiComponentsBundle\Factory\Uploadable\MediaObjectFactory;
26
use Silverback\ApiComponentsBundle\Factory\User\Mailer\ChangeEmailConfirmationEmailFactory;
27
use Silverback\ApiComponentsBundle\Factory\User\Mailer\PasswordChangedEmailFactory;
28
use Silverback\ApiComponentsBundle\Factory\User\Mailer\PasswordResetEmailFactory;
29
use Silverback\ApiComponentsBundle\Factory\User\Mailer\UserEnabledEmailFactory;
30
use Silverback\ApiComponentsBundle\Factory\User\Mailer\UsernameChangedEmailFactory;
31
use Silverback\ApiComponentsBundle\Factory\User\Mailer\VerifyEmailFactory;
32
use Silverback\ApiComponentsBundle\Factory\User\Mailer\WelcomeEmailFactory;
33
use Silverback\ApiComponentsBundle\Factory\User\UserFactory;
34
use Silverback\ApiComponentsBundle\Form\FormTypeInterface;
35
use Silverback\ApiComponentsBundle\Form\Type\User\ChangePasswordType;
36
use Silverback\ApiComponentsBundle\Form\Type\User\NewEmailAddressType;
37
use Silverback\ApiComponentsBundle\Form\Type\User\PasswordUpdateType;
38
use Silverback\ApiComponentsBundle\Form\Type\User\UserRegisterType;
39
use Silverback\ApiComponentsBundle\Helper\Publishable\PublishableStatusChecker;
40
use Silverback\ApiComponentsBundle\Helper\Uploadable\UploadableFileManager;
41
use Silverback\ApiComponentsBundle\Helper\User\UserDataProcessor;
42
use Silverback\ApiComponentsBundle\Helper\User\UserMailer;
43
use Silverback\ApiComponentsBundle\Repository\Core\RefreshTokenRepository;
44
use Silverback\ApiComponentsBundle\Repository\User\UserRepositoryInterface;
45
use Silverback\ApiComponentsBundle\Security\UserChecker;
46
use Silverback\ApiComponentsBundle\Security\Voter\RouteVoter;
47
use Silverback\ApiComponentsBundle\Serializer\Normalizer\MetadataNormalizer;
48
use Symfony\Component\Config\FileLocator;
49
use Symfony\Component\DependencyInjection\ContainerBuilder;
50
use Symfony\Component\DependencyInjection\Extension\Extension;
51
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
52
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
53
use Symfony\Component\DependencyInjection\Reference;
54
use Symfony\Component\Security\Http\Event\LogoutEvent;
55
56
/**
57
 * @author Daniel West <[email protected]>
58
 */
59
class SilverbackApiComponentsExtension extends Extension implements PrependExtensionInterface
60
{
61
    /**
62
     * @throws Exception
63
     */
64 1
    public function load(array $configs, ContainerBuilder $container): void
65
    {
66 1
        $configuration = new Configuration();
67 1
        $config = $this->processConfiguration($configuration, $configs);
68
69 1
        $this->loadServiceConfig($container);
70
71 1
        $definition = $container->getDefinition(TablePrefixExtension::class);
72 1
        $definition->setArgument('$prefix', $config['table_prefix']);
73
74 1
        $definition = $container->getDefinition(UserRepositoryInterface::class);
75 1
        $definition->setArgument('$entityClass', $config['user']['class_name']);
76 1
        $definition->setArgument('$passwordRequestTimeout', $config['user']['password_reset']['request_timeout_seconds']);
77 1
        $definition->setArgument('$newEmailConfirmTimeout', $config['user']['new_email_confirmation']['request_timeout_seconds']);
78
79 1
        $cookieProvider = new Reference('lexik_jwt_authentication.cookie_provider.' . $config['refresh_token']['cookie_name']);
80 1
        $definition = $container->getDefinition('silverback.security.jwt_event_listener');
81 1
        $definition->setArgument('$cookieProvider', $cookieProvider);
82 1
        $container->setParameter('silverback.api_components.refresh_token.ttl', (int) $config['refresh_token']['ttl']);
83
84 1
        if (!empty($config['refresh_token']['options'])) {
85 1
            $definition = $container->getDefinition($config['refresh_token']['handler_id']);
86 1
            $definition->setArgument('$options', $config['refresh_token']['options']);
87
        }
88
89 1
        if ('silverback.api_components.refresh_token.storage.doctrine' === $config['refresh_token']['handler_id']) {
90
            $container
91 1
                ->register(RefreshTokenRepository::class)
92 1
                ->setArguments([new Reference('doctrine'), $config['refresh_token']['options']['class']])
93 1
                ->addTag('doctrine.repository_service');
94
        }
95
96 1
        $definition = $container->getDefinition('silverback.command.refresh_tokens_expire');
97 1
        $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
98
99 1
        if (class_exists(LogoutEvent::class)) {
100 1
            $definition = $container->getDefinition('silverback.security.logout_listener');
101 1
            $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
102 1
            $definition->setArgument('$cookieProvider', $cookieProvider);
103
        } else {
104
            $definition = $container->getDefinition('silverback.security.logout_handler');
105
            $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
106
            $definition->setArgument('$cookieProvider', $cookieProvider);
107
        }
108
109 1
        $definition = $container->getDefinition('silverback.security.jwt_invalid_event_listener');
110 1
        $definition->setArgument('$cookieProvider', $cookieProvider);
111
112 1
        $definition = $container->getDefinition('silverback.security.jwt_manager');
113 1
        $definition->setArgument('$userProvider', new Reference(sprintf('security.user.provider.concrete.%s', $config['refresh_token']['database_user_provider'])));
114 1
        $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
115
116 1
        $definition = $container->getDefinition(PublishableStatusChecker::class);
117 1
        $definition->setArgument('$permission', $config['publishable']['permission']);
118
119 1
        $definition = $container->getDefinition(MetadataNormalizer::class);
120 1
        $definition->setArgument('$metadataKey', $config['metadata_key']);
121
122 1
        $this->setEmailVerificationArguments($container, $config['user']['email_verification'], $config['user']['password_reset']['repeat_ttl_seconds']);
123 1
        $this->setUserClassArguments($container, $config['user']['class_name']);
124 1
        $this->setMailerServiceArguments($container, $config);
125
126 1
        $imagineEnabled = $container->getParameter('api_components.imagine_enabled');
127 1
        $definition = $container->getDefinition(UploadableAnnotationReader::class);
128 1
        $definition->setArgument('$imagineBundleEnabled', $imagineEnabled);
129
130 1
        if ($imagineEnabled) {
131 1
            $definition = $container->getDefinition(UploadableFileManager::class);
132 1
            $definition->setArgument('$filterService', new Reference('liip_imagine.service.filter'));
133 1
            $definition->setArgument('$imagineCacheManager', new Reference('liip_imagine.cache.manager'));
134
135 1
            $definition = $container->getDefinition(MediaObjectFactory::class);
136 1
            $definition->setArgument('$filterService', new Reference('liip_imagine.service.filter'));
137
        }
138
139 1
        $definition = $container->getDefinition(RouteExtension::class);
140 1
        $definition->setArgument('$config', $config['route_security']);
141
142 1
        $definition = $container->getDefinition(RouteVoter::class);
143 1
        $definition->setArgument('$config', $config['route_security']);
144 1
    }
145
146 1
    private function setEmailVerificationArguments(ContainerBuilder $container, array $emailVerificationConfig, int $passwordRepeatTtl): void
147
    {
148 1
        $definition = $container->getDefinition(UserChecker::class);
149 1
        $definition->setArgument('$denyUnverifiedLogin', $emailVerificationConfig['deny_unverified_login']);
150
151 1
        $definition = $container->getDefinition(UserDataProcessor::class);
152 1
        $definition->setArgument('$initialEmailVerifiedState', $emailVerificationConfig['default_value']);
153 1
        $definition->setArgument('$verifyEmailOnRegister', $emailVerificationConfig['verify_on_register']);
154 1
        $definition->setArgument('$verifyEmailOnChange', $emailVerificationConfig['verify_on_change']);
155 1
        $definition->setArgument('$tokenTtl', $passwordRepeatTtl);
156 1
    }
157
158 1
    private function setUserClassArguments(ContainerBuilder $container, string $userClass): void
159
    {
160 1
        $definition = $container->getDefinition(UserFactory::class);
161 1
        $definition->setArgument('$userClass', $userClass);
162
163 1
        $definition = $container->getDefinition(ChangePasswordType::class);
164 1
        $definition->setArgument('$userClass', $userClass);
165
166 1
        $definition = $container->getDefinition(NewEmailAddressType::class);
167 1
        $definition->setArgument('$userClass', $userClass);
168
169 1
        $definition = $container->getDefinition(UserRegisterType::class);
170 1
        $definition->setArgument('$userClass', $userClass);
171
172 1
        $definition = $container->getDefinition(PasswordUpdateType::class);
173 1
        $definition->setArgument('$userClass', $userClass);
174 1
    }
175
176 1
    private function setMailerServiceArguments(ContainerBuilder $container, array $config): void
177
    {
178 1
        $definition = $container->getDefinition(UserMailer::class);
179 1
        $definition->setArgument(
180 1
            '$context',
181
            [
182 1
                'website_name' => $config['website_name'],
183
            ]
184
        );
185
186
        $mapping = [
187 1
            PasswordChangedEmailFactory::class => 'password_changed',
188
            UserEnabledEmailFactory::class => 'user_enabled',
189
            UsernameChangedEmailFactory::class => 'username_changed',
190
            WelcomeEmailFactory::class => 'welcome',
191
        ];
192 1
        foreach ($mapping as $class => $key) {
193 1
            $definition = $container->getDefinition($class);
194 1
            $definition->setArgument('$subject', $config['user']['emails'][$key]['subject']);
195 1
            $definition->setArgument('$enabled', $config['user']['emails'][$key]['enabled']);
196 1
            if (WelcomeEmailFactory::class === $class) {
197 1
                $definition->setArgument('$defaultRedirectPath', $config['user']['email_verification']['email']['default_redirect_path']);
198 1
                $definition->setArgument('$redirectPathQueryKey', $config['user']['email_verification']['email']['redirect_path_query']);
199
            }
200
        }
201
202
        $mapping = [
203 1
            VerifyEmailFactory::class => 'email_verification',
204
            ChangeEmailConfirmationEmailFactory::class => 'new_email_confirmation',
205
            PasswordResetEmailFactory::class => 'password_reset',
206
        ];
207 1
        foreach ($mapping as $class => $key) {
208 1
            $definition = $container->getDefinition($class);
209 1
            $definition->setArgument('$subject', $config['user'][$key]['email']['subject']);
210 1
            $definition->setArgument('$enabled', true);
211 1
            $definition->setArgument('$defaultRedirectPath', $config['user'][$key]['email']['default_redirect_path']);
212 1
            $definition->setArgument('$redirectPathQueryKey', $config['user'][$key]['email']['redirect_path_query']);
213
        }
214 1
    }
215
216
    /**
217
     * @throws Exception
218
     */
219 1
    private function loadServiceConfig(ContainerBuilder $container): void
220
    {
221 1
        $container->registerForAutoconfiguration(FormTypeInterface::class)
222 1
            ->addTag('silverback_api_components.form_type');
223
224 1
        $container->registerForAutoconfiguration(ComponentInterface::class)
225 1
            ->addTag('silverback_api_components.entity.component');
226
227 1
        $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
228 1
        $loader->load('services.php');
229 1
        $loader->load('services_normalizers.php');
230
231 1
        if ($container->hasDefinition('api_platform.http_cache.purger')) {
232
            $loader->load('services_doctrine_cache.php');
233
        }
234 1
    }
235
236 1
    public function prepend(ContainerBuilder $container): void
237
    {
238 1
        $configs = $container->getExtensionConfig($this->getAlias());
239 1
        $configuration = new Configuration();
240 1
        $config = $this->processConfiguration($configuration, $configs);
241 1
        $this->prependApiPlatformConfig($container, $config);
242 1
        $this->prependDoctrineConfiguration($container);
243 1
    }
244
245 1
    private function prependDoctrineConfiguration(ContainerBuilder $container): void
246
    {
247 1
        $container->prependExtensionConfig(
248 1
            'doctrine',
249
            [
250
                'dbal' => [
251
                    'types' => [
252 1
                        'uuid' => UuidType::class,
253
                    ],
254
                ],
255
            ]
256
        );
257 1
    }
258
259 1
    private function prependApiPlatformConfig(ContainerBuilder $container, array $config): void
260
    {
261 1
        $srcBase = __DIR__ . '/..';
262 1
        $configBasePath = $srcBase . '/Resources/config/api_platform';
263
264 1
        $mappingPaths = [$srcBase . '/Entity/Core'];
265 1
        $mappingPaths[] = sprintf('%s/%s.xml', $configBasePath, 'uploadable');
266 1
        foreach ($config['enabled_components'] as $key => $enabled_component) {
267 1
            if (true === $enabled_component) {
268 1
                $mappingPaths[] = sprintf('%s/%s.xml', $configBasePath, $key);
269
            }
270
        }
271
272 1
        $websiteName = $config['website_name'];
273
274 1
        $container->prependExtensionConfig(
275 1
            'api_platform',
276
            [
277 1
                'title' => $websiteName,
278 1
                'description' => sprintf('API for %s', $websiteName),
279
                // 'defaults' => [
280
                //   'pagination_client_items_per_page' => true,
281
                //   'pagination_maximum_items_per_page' => 100,
282
                // ],
283
                'collection' => [
284
                    'pagination' => [
285
                        'items_per_page_parameter_name' => 'perPage',
286
                        'client_items_per_page' => true,
287
                        'maximum_items_per_page' => 100,
288
                    ],
289
                ],
290
                'mapping' => [
291 1
                    'paths' => $mappingPaths,
292
                ],
293
                'swagger' => [
294
                    'api_keys' => [
295
                        'API Token' => [
296
                            'name' => 'X-AUTH-TOKEN',
297
                            'type' => 'header',
298
                        ],
299
                        'JWT (use prefix `Bearer`)' => [
300
                            'name' => 'Authorization',
301
                            'type' => 'header',
302
                        ],
303
                    ],
304
                ],
305
                'exception_to_status' => [
306
                    UnparseableRequestHeaderException::class => 400,
307
                    ApiPlatformAuthenticationException::class => 401,
308
                    UserDisabledException::class => 401,
309
                ],
310
            ]
311
        );
312 1
    }
313
}
314