Passed
Push — master ( 7276f5...3cc058 )
by Daniel
09:58 queued 02:47
created

SilverbackApiComponentsExtension::prepend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 7
ccs 6
cts 6
cp 1
crap 1
rs 10
c 0
b 0
f 0
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\UserRepository;
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(UserRepository::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_component.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_component.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
        if (class_exists(LogoutEvent::class)) {
97 1
            $definition = $container->getDefinition('silverback.security.logout_listener');
98 1
            $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
99 1
            $definition->setArgument('$cookieProvider', $cookieProvider);
100
        } else {
101
            $definition = $container->getDefinition('silverback.security.logout_handler');
102
            $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
103
            $definition->setArgument('$cookieProvider', $cookieProvider);
104
        }
105
106 1
        $definition = $container->getDefinition('silverback.security.jwt_manager');
107 1
        $definition->setArgument('$userProvider', new Reference(sprintf('security.user.provider.concrete.%s', $config['refresh_token']['database_user_provider'])));
108 1
        $definition->setArgument('$storage', new Reference($config['refresh_token']['handler_id']));
109
110 1
        $definition = $container->getDefinition(PublishableStatusChecker::class);
111 1
        $definition->setArgument('$permission', $config['publishable']['permission']);
112
113 1
        $definition = $container->getDefinition(MetadataNormalizer::class);
114 1
        $definition->setArgument('$metadataKey', $config['metadata_key']);
115
116 1
        $this->setEmailVerificationArguments($container, $config['user']['email_verification'], $config['user']['password_reset']['repeat_ttl_seconds']);
117 1
        $this->setUserClassArguments($container, $config['user']['class_name']);
118 1
        $this->setMailerServiceArguments($container, $config);
119
120 1
        $imagineEnabled = $container->getParameter('api_component.imagine_enabled');
121 1
        $definition = $container->getDefinition(UploadableAnnotationReader::class);
122 1
        $definition->setArgument('$imagineBundleEnabled', $imagineEnabled);
123
124 1
        if ($imagineEnabled) {
125 1
            $definition = $container->getDefinition(UploadableFileManager::class);
126 1
            $definition->setArgument('$filterService', new Reference('liip_imagine.service.filter'));
127 1
            $definition->setArgument('$imagineCacheManager', new Reference('liip_imagine.cache.manager'));
128
129 1
            $definition = $container->getDefinition(MediaObjectFactory::class);
130 1
            $definition->setArgument('$filterService', new Reference('liip_imagine.service.filter'));
131
        }
132
133 1
        $definition = $container->getDefinition(RouteExtension::class);
134 1
        $definition->setArgument('$config', $config['route_security']);
135
136 1
        $definition = $container->getDefinition(RouteVoter::class);
137 1
        $definition->setArgument('$config', $config['route_security']);
138 1
    }
139
140 1
    private function setEmailVerificationArguments(ContainerBuilder $container, array $emailVerificationConfig, int $passwordRepeatTtl): void
141
    {
142 1
        $definition = $container->getDefinition(UserChecker::class);
143 1
        $definition->setArgument('$denyUnverifiedLogin', $emailVerificationConfig['deny_unverified_login']);
144
145 1
        $definition = $container->getDefinition(UserDataProcessor::class);
146 1
        $definition->setArgument('$initialEmailVerifiedState', $emailVerificationConfig['default_value']);
147 1
        $definition->setArgument('$verifyEmailOnRegister', $emailVerificationConfig['verify_on_register']);
148 1
        $definition->setArgument('$verifyEmailOnChange', $emailVerificationConfig['verify_on_change']);
149 1
        $definition->setArgument('$tokenTtl', $passwordRepeatTtl);
150 1
    }
151
152 1
    private function setUserClassArguments(ContainerBuilder $container, string $userClass): void
153
    {
154 1
        $definition = $container->getDefinition(UserFactory::class);
155 1
        $definition->setArgument('$userClass', $userClass);
156
157 1
        $definition = $container->getDefinition(ChangePasswordType::class);
158 1
        $definition->setArgument('$userClass', $userClass);
159
160 1
        $definition = $container->getDefinition(NewEmailAddressType::class);
161 1
        $definition->setArgument('$userClass', $userClass);
162
163 1
        $definition = $container->getDefinition(UserRegisterType::class);
164 1
        $definition->setArgument('$userClass', $userClass);
165
166 1
        $definition = $container->getDefinition(PasswordUpdateType::class);
167 1
        $definition->setArgument('$userClass', $userClass);
168 1
    }
169
170 1
    private function setMailerServiceArguments(ContainerBuilder $container, array $config): void
171
    {
172 1
        $definition = $container->getDefinition(UserMailer::class);
173 1
        $definition->setArgument(
174 1
            '$context',
175
            [
176 1
                'website_name' => $config['website_name'],
177
            ]
178
        );
179
180
        $mapping = [
181 1
            PasswordChangedEmailFactory::class => 'password_changed',
182
            UserEnabledEmailFactory::class => 'user_enabled',
183
            UsernameChangedEmailFactory::class => 'username_changed',
184
            WelcomeEmailFactory::class => 'welcome',
185
        ];
186 1
        foreach ($mapping as $class => $key) {
187 1
            $definition = $container->getDefinition($class);
188 1
            $definition->setArgument('$subject', $config['user']['emails'][$key]['subject']);
189 1
            $definition->setArgument('$enabled', $config['user']['emails'][$key]['enabled']);
190 1
            if (WelcomeEmailFactory::class === $class) {
191 1
                $definition->setArgument('$defaultRedirectPath', $config['user']['email_verification']['email']['default_redirect_path']);
192 1
                $definition->setArgument('$redirectPathQueryKey', $config['user']['email_verification']['email']['redirect_path_query']);
193
            }
194
        }
195
196
        $mapping = [
197 1
            VerifyEmailFactory::class => 'email_verification',
198
            ChangeEmailConfirmationEmailFactory::class => 'new_email_confirmation',
199
            PasswordResetEmailFactory::class => 'password_reset',
200
        ];
201 1
        foreach ($mapping as $class => $key) {
202 1
            $definition = $container->getDefinition($class);
203 1
            $definition->setArgument('$subject', $config['user'][$key]['email']['subject']);
204 1
            $definition->setArgument('$enabled', true);
205 1
            $definition->setArgument('$defaultRedirectPath', $config['user'][$key]['email']['default_redirect_path']);
206 1
            $definition->setArgument('$redirectPathQueryKey', $config['user'][$key]['email']['redirect_path_query']);
207
        }
208 1
    }
209
210
    /**
211
     * @throws Exception
212
     */
213 1
    private function loadServiceConfig(ContainerBuilder $container): void
214
    {
215 1
        $container->registerForAutoconfiguration(FormTypeInterface::class)
216 1
            ->addTag('silverback_api_components.form_type');
217
218 1
        $container->registerForAutoconfiguration(ComponentInterface::class)
219 1
            ->addTag('silverback_api_components.entity.component');
220
221 1
        $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
222 1
        $loader->load('services.php');
223 1
        $loader->load('services_normalizers.php');
224 1
    }
225
226 1
    public function prepend(ContainerBuilder $container): void
227
    {
228 1
        $configs = $container->getExtensionConfig($this->getAlias());
229 1
        $configuration = new Configuration();
230 1
        $config = $this->processConfiguration($configuration, $configs);
231 1
        $this->prependApiAPlatformConfig($container, $config);
232 1
        $this->prependDoctrineConfiguration($container);
233 1
    }
234
235 1
    private function prependDoctrineConfiguration(ContainerBuilder $container): void
236
    {
237 1
        $container->prependExtensionConfig(
238 1
            'doctrine',
239
            [
240
                'dbal' => [
241
                    'types' => [
242 1
                        'uuid' => UuidType::class,
243
                    ],
244
                ],
245
            ]
246
        );
247 1
    }
248
249 1
    private function prependApiAPlatformConfig(ContainerBuilder $container, array $config): void
250
    {
251 1
        $srcBase = __DIR__ . '/..';
252 1
        $configBasePath = $srcBase . '/Resources/config/api_platform';
253
254 1
        $mappingPaths = [$srcBase . '/Entity/Core'];
255 1
        $mappingPaths[] = sprintf('%s/%s.xml', $configBasePath, 'uploadable');
256 1
        foreach ($config['enabled_components'] as $key => $enabled_component) {
257 1
            if (true === $enabled_component) {
258 1
                $mappingPaths[] = sprintf('%s/%s.xml', $configBasePath, $key);
259
            }
260
        }
261
262 1
        $websiteName = $config['website_name'];
263
264 1
        $container->prependExtensionConfig(
265 1
            'api_platform',
266
            [
267 1
                'title' => $websiteName,
268 1
                'description' => sprintf('API for %s', $websiteName),
269
                'collection' => [
270
                    'pagination' => [
271
                        'client_items_per_page' => true,
272
                        'items_per_page_parameter_name' => 'perPage',
273
                        'maximum_items_per_page' => 100,
274
                    ],
275
                ],
276
                'mapping' => [
277 1
                    'paths' => $mappingPaths,
278
                ],
279
                'swagger' => [
280
                    'api_keys' => [
281
                        'API Token' => [
282
                            'name' => 'X-AUTH-TOKEN',
283
                            'type' => 'header',
284
                        ],
285
                        'JWT (use prefix `Bearer`)' => [
286
                            'name' => 'Authorization',
287
                            'type' => 'header',
288
                        ],
289
                    ],
290
                ],
291
                'exception_to_status' => [
292
                    UnparseableRequestHeaderException::class => 400,
293
                    ApiPlatformAuthenticationException::class => 401,
294
                    UserDisabledException::class => 401,
295
                ],
296
            ]
297
        );
298 1
    }
299
}
300