Completed
Pull Request — master (#355)
by
unknown
03:21
created

Bootstrap::initAuthCollection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
/*
4
 * This file is part of the 2amigos/yii2-usuario project.
5
 *
6
 * (c) 2amigOS! <http://2amigos.us/>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace Da\User;
13
14
use Da\User\Component\AuthDbManagerComponent;
15
use Da\User\Contracts\AuthManagerInterface;
16
use Da\User\Controller\SecurityController;
17
use Da\User\Event\FormEvent;
18
use Da\User\Helper\ClassMapHelper;
19
use Da\User\Model\User;
20
use Yii;
21
use yii\authclient\Collection;
22
use yii\base\Application;
23
use yii\base\BootstrapInterface;
24
use yii\base\Event as YiiEvent;
25
use yii\base\Exception;
26
use yii\base\InvalidConfigException;
27
use yii\console\Application as ConsoleApplication;
28
use yii\i18n\PhpMessageSource;
29
use yii\web\Application as WebApplication;
30
31
/**
32
 * Bootstrap class of the yii2-usuario extension. Configures container services, initializes translations,
33
 * builds class map, and does the other setup actions participating in the application bootstrap process.
34
 */
35
class Bootstrap implements BootstrapInterface
36
{
37
    /**
38
     * {@inheritdoc}
39
     *
40
     * @throws InvalidConfigException
41
     */
42
    public function bootstrap($app)
43
    {
44
        if ($app->hasModule('user') && $app->getModule('user') instanceof Module) {
45
            $map = $this->buildClassMap($app->getModule('user')->classMap);
46
            $this->initContainer($app, $map);
47
            $this->initTranslations($app);
48
            $this->initMailServiceConfiguration($app, $app->getModule('user'));
49
50
            if ($app instanceof WebApplication) {
51
                $this->initControllerNamespace($app);
52
                $this->initUrlRoutes($app);
53
                $this->initAuthCollection($app);
54
                $this->initAuthManager($app);
55
            } else {
56
                /* @var $app ConsoleApplication */
57
                $this->initConsoleCommands($app);
58
                $this->initAuthManager($app);
59
            }
60
        }
61
    }
62
63
    /**
64
     * Initialize container with module classes.
65
     *
66
     * @param \yii\base\Application $app
67
     * @param array                 $map the previously built class map list
68
     */
69 16
    protected function initContainer($app, $map)
70
    {
71
        $di = Yii::$container;
72
        try {
73
            // events
74
            $di->set(Event\FormEvent::class);
75
            $di->set(Event\ProfileEvent::class);
76
            $di->set(Event\ResetPasswordEvent::class);
77
            $di->set(Event\SocialNetworkAuthEvent::class);
78
            $di->set(Event\SocialNetworkConnectEvent::class);
79
            $di->set(Event\UserEvent::class);
80
            $di->set(Event\GdprEvent::class);
81
82
            // forms
83
            $di->set(Form\LoginForm::class);
84
            $di->set(Form\RecoveryForm::class);
85
            $di->set(Form\RegistrationForm::class);
86
            $di->set(Form\ResendForm::class);
87
            $di->set(Form\SettingsForm::class);
88
            $di->set(Form\GdprDeleteForm::class);
89
90
            // helpers
91
            $di->set(Helper\AuthHelper::class);
92
            $di->set(Helper\GravatarHelper::class);
93
            $di->set(Helper\SecurityHelper::class);
94
            $di->set(Helper\TimezoneHelper::class);
95
96
            // services
97
            $di->set(Service\AccountConfirmationService::class);
98
            $di->set(Service\EmailChangeService::class);
99
            $di->set(Service\PasswordExpireService::class);
100
            $di->set(Service\PasswordRecoveryService::class);
101
            $di->set(Service\ResendConfirmationService::class);
102
            $di->set(Service\ResetPasswordService::class);
103
            $di->set(Service\SocialNetworkAccountConnectService::class);
104
            $di->set(Service\SocialNetworkAuthenticateService::class);
105
            $di->set(Service\UserBlockService::class);
106
            $di->set(Service\UserCreateService::class);
107
            $di->set(Service\UserRegisterService::class);
108
            $di->set(Service\UserConfirmationService::class);
109
            $di->set(Service\AuthItemEditionService::class);
110
            $di->set(Service\UpdateAuthAssignmentsService::class);
111
            $di->set(Service\SwitchIdentityService::class);
112
            $di->set(Service\TwoFactorQrCodeUriGeneratorService::class);
113
114
            // email change strategy
115
            $di->set(Strategy\DefaultEmailChangeStrategy::class);
116
            $di->set(Strategy\InsecureEmailChangeStrategy::class);
117
            $di->set(Strategy\SecureEmailChangeStrategy::class);
118
119
            // validators
120
            $di->set(Validator\AjaxRequestModelValidator::class);
121
            $di->set(Validator\TimeZoneValidator::class);
122
            $di->set(Validator\TwoFactorCodeValidator::class);
123
124
            // class map models + query classes
125
            $modelClassMap = [];
126
            foreach ($map as $class => $definition) {
127
                $di->set($class, $definition);
128
                $model = is_array($definition) ? $definition['class'] : $definition;
129
                $name = substr($class, strrpos($class, '\\') + 1);
130
                $modelClassMap[$class] = $model;
131
                if (in_array($name, ['User', 'Profile', 'Token', 'SocialNetworkAccount'])) {
132
                    $di->set(
133
                        "Da\\User\\Query\\{$name}Query",
134
                        function () use ($model) {
135 16
                            return $model::find();
136
                        }
137
                    );
138
                }
139
            }
140
            $di->setSingleton(ClassMapHelper::class, ClassMapHelper::class, [$modelClassMap]);
141
142
            // search classes
143
            if (!$di->has(Search\UserSearch::class)) {
144
                $di->set(Search\UserSearch::class, [$di->get(Query\UserQuery::class)]);
145
            }
146
            if (!$di->has(Search\PermissionSearch::class)) {
147
                $di->set(Search\PermissionSearch::class);
148
            }
149
            if (!$di->has(Search\RoleSearch::class)) {
150
                $di->set(Search\RoleSearch::class);
151
            }
152
153
            // Attach an event to check if the password has expired
154
            if (null !== Yii::$app->getModule('user')->maxPasswordAge) {
155
                YiiEvent::on(SecurityController::class, FormEvent::EVENT_AFTER_LOGIN, function (FormEvent $event) {
156
                    $user = $event->form->user;
157
                    if ($user->password_age >= Yii::$app->getModule('user')->maxPasswordAge) {
158
                        // Force password change
159
                        Yii::$app->session->setFlash('warning', Yii::t('usuario', 'Your password has expired, you must change it now'));
160
                        Yii::$app->response->redirect(['/user/settings/account'])->send();
161
                    }
162
                });
163
            }
164
165
            if ($app instanceof WebApplication) {
166
                // override Yii
167
                $di->set(
168
                    'yii\web\User',
169
                    [
170
                        'enableAutoLogin' => $app->getModule('user')->enableAutoLogin,
171
                        'loginUrl' => ['/user/security/login'],
172
                        'identityClass' => $di->get(ClassMapHelper::class)->get(User::class),
173
                    ]
174
                );
175
            }
176
        } catch (Exception $e) {
177
            die($e);
178
        }
179
    }
180
181
    /**
182
     * Registers module translation messages.
183
     *
184
     * @param Application $app
185
     *
186
     * @throws InvalidConfigException
187
     */
188
    protected function initTranslations(Application $app)
189
    {
190
        if (!isset($app->get('i18n')->translations['usuario*'])) {
191
            $app->get('i18n')->translations['usuario*'] = [
192
                'class' => PhpMessageSource::class,
193
                'basePath' => __DIR__ . '/resources/i18n',
194
                'sourceLanguage' => 'en-US',
195
            ];
196
        }
197
    }
198
199
    /**
200
     * Ensures the auth manager is the one provided by the library.
201
     *
202
     * @param Application $app
203
     *
204
     * @throws InvalidConfigException
205
     */
206
    protected function initAuthManager(Application $app)
207
    {
208
        if (!($app->getAuthManager() instanceof AuthManagerInterface)) {
209
            $app->set(
210
                'authManager',
211
                [
212
                    'class' => AuthDbManagerComponent::class,
213
                ]
214
            );
215
        }
216
    }
217
218
    /**
219
     * Initializes web url routes (rules in Yii2).
220
     *
221
     * @param WebApplication $app
222
     *
223
     * @throws InvalidConfigException
224
     */
225
    protected function initUrlRoutes(WebApplication $app)
226
    {
227
        /** @var $module Module */
228
        $module = $app->getModule('user');
229
        $routesRules = $module->routes;
230
        if(false === $module->disableProfilePage){
231
            $routesRules = array_merge($routesRules, $module->profileShowRoute);
232
        }
233
        $config = [
234
            'class' => 'yii\web\GroupUrlRule',
235
            'prefix' => $module->prefix,
236
            'rules' => $routesRules,
237
        ];
238
239
        if ($module->prefix !== 'user') {
240
            $config['routePrefix'] = 'user';
241
        }
242
243
        $rule = Yii::createObject($config);
244
        $app->getUrlManager()->addRules([$rule], false);
245
    }
246
247
    /**
248
     * Ensures required mail parameters needed for the mail service.
249
     *
250
     * @param Application             $app
251
     * @param Module|\yii\base\Module $module
252
     */
253
    protected function initMailServiceConfiguration(Application $app, Module $module)
254
    {
255
        $defaults = [
256
            'fromEmail' => '[email protected]',
257
            'welcomeMailSubject' => Yii::t('usuario', 'Welcome to {0}', $app->name),
0 ignored issues
show
Documentation introduced by
$app->name is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
258
            'confirmationMailSubject' => Yii::t('usuario', 'Confirm account on {0}', $app->name),
0 ignored issues
show
Documentation introduced by
$app->name is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
259
            'reconfirmationMailSubject' => Yii::t('usuario', 'Confirm email change on {0}', $app->name),
0 ignored issues
show
Documentation introduced by
$app->name is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
260
            'recoveryMailSubject' => Yii::t('usuario', 'Complete password reset on {0}', $app->name),
0 ignored issues
show
Documentation introduced by
$app->name is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
261
        ];
262
263
        $module->mailParams = array_merge($defaults, $module->mailParams);
264
    }
265
266
    /**
267
     * Ensures the authCollection component is configured.
268
     *
269
     * @param WebApplication $app
270
     *
271
     * @throws InvalidConfigException
272
     */
273
    protected function initAuthCollection(WebApplication $app)
274
    {
275
        if (!$app->has('authClientCollection')) {
276
            $app->set('authClientCollection', Collection::class);
277
        }
278
    }
279
280
    /**
281
     * Registers console commands to main app.
282
     *
283
     * @param ConsoleApplication $app
284
     */
285
    protected function initConsoleCommands(ConsoleApplication $app)
286
    {
287
        $app->getModule('user')->controllerNamespace = $app->getModule('user')->consoleControllerNamespace;
0 ignored issues
show
Bug introduced by
The property consoleControllerNamespace does not seem to exist. Did you mean controllerNamespace?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
288
    }
289
290
    /**
291
     * Registers controllers.
292
     *
293
     * @param WebApplication $app
294
     */
295
    protected function initControllerNamespace(WebApplication $app)
296
    {
297
        $app->getModule('user')->controllerNamespace = $app->getModule('user')->controllerNamespace;
298
        $app->getModule('user')->setViewPath('@Da/User/resources/views');
299
    }
300
301
    /**
302
     * Builds class map according to user configuration.
303
     *
304
     * @param array $userClassMap user configuration on the module
305
     *
306
     * @throws Exception
307
     * @return array
308
     */
309
    protected function buildClassMap(array $userClassMap)
310
    {
311
        $map = [];
312
313
        $defaults = [
314
            // --- models
315
            'User' => 'Da\User\Model\User',
316
            'SocialNetworkAccount' => 'Da\User\Model\SocialNetworkAccount',
317
            'Profile' => 'Da\User\Model\Profile',
318
            'Token' => 'Da\User\Model\Token',
319
            'Assignment' => 'Da\User\Model\Assignment',
320
            'Permission' => 'Da\User\Model\Permission',
321
            'Role' => 'Da\User\Model\Role',
322
            // --- search
323
            'UserSearch' => 'Da\User\Search\UserSearch',
324
            'PermissionSearch' => 'Da\User\Search\PermissionSearch',
325
            'RoleSearch' => 'Da\User\Search\RoleSearch',
326
            // --- forms
327
            'RegistrationForm' => 'Da\User\Form\RegistrationForm',
328
            'ResendForm' => 'Da\User\Form\ResendForm',
329
            'LoginForm' => 'Da\User\Form\LoginForm',
330
            'SettingsForm' => 'Da\User\Form\SettingsForm',
331
            'RecoveryForm' => 'Da\User\Form\RecoveryForm',
332
            // --- services
333
            'MailService' => 'Da\User\Service\MailService',
334
        ];
335
336
        $routes = [
337
            'Da\User\Model' => [
338
                'User',
339
                'SocialNetworkAccount',
340
                'Profile',
341
                'Token',
342
                'Assignment',
343
                'Permission',
344
                'Role',
345
            ],
346
            'Da\User\Search' => [
347
                'UserSearch',
348
                'PermissionSearch',
349
                'RoleSearch',
350
            ],
351
            'Da\User\Form' => [
352
                'RegistrationForm',
353
                'ResendForm',
354
                'LoginForm',
355
                'SettingsForm',
356
                'RecoveryForm',
357
            ],
358
            'Da\User\Service' => [
359
                'MailService',
360
            ],
361
        ];
362
363
        $mapping = array_merge($defaults, $userClassMap);
364
365
        foreach ($mapping as $name => $definition) {
366
            $map[$this->getRoute($routes, $name) . "\\$name"] = $definition;
367
        }
368
369
        return $map;
370
    }
371
372
    /**
373
     * Returns the parent class name route of a short class name.
374
     *
375
     * @param array  $routes class name routes
376
     * @param string $name
377
     *
378
     * @throws Exception
379
     * @return int|string
380
     *
381
     */
382
    protected function getRoute(array $routes, $name)
383
    {
384
        foreach ($routes as $route => $names) {
385
            if (in_array($name, $names, false)) {
386
                return $route;
387
            }
388
        }
389
        throw new Exception("Unknown configuration class name '{$name}'");
390
    }
391
}
392