Passed
Push — master ( e8f83f...f116c2 )
by Jan
05:37
created

redirectToSettingsIfNeeded()   B

Complexity

Conditions 10
Paths 14

Size

Total Lines 41
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 18
c 0
b 0
f 0
nc 14
nop 1
dl 0
loc 41
rs 7.6666

How to fix   Complexity   

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
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software; you can redistribute it and/or
29
 * modify it under the terms of the GNU General Public License
30
 * as published by the Free Software Foundation; either version 2
31
 * of the License, or (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU General Public License
39
 * along with this program; if not, write to the Free Software
40
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
41
 */
42
43
namespace App\EventSubscriber\UserSystem;
44
45
use App\Entity\UserSystem\User;
46
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
47
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
48
use Symfony\Component\HttpKernel\Event\RequestEvent;
49
use Symfony\Component\HttpKernel\KernelEvents;
50
use Symfony\Component\Security\Core\Security;
51
use Symfony\Component\Security\Http\HttpUtils;
52
53
/**
54
 * This event subscriber redirects a user to its settings page, when it needs to change its password or is enforced
55
 * to setup a 2FA method (enforcement can be set per group).
56
 * In this cases the user is unable to access sites other than the whitelisted (see ALLOWED_ROUTES).
57
 */
58
final class PasswordChangeNeededSubscriber implements EventSubscriberInterface
59
{
60
    /**
61
     * @var string[] The routes the user is allowed to access without being redirected.
62
     *               This should be only routes related to login/logout and user settings
63
     */
64
    public const ALLOWED_ROUTES = [
65
        '2fa_login',
66
        '2fa_login_check',
67
        'user_settings',
68
        'club_base_register_u2f',
69
        'logout',
70
    ];
71
72
    /**
73
     * @var string The route the user will redirected to, if he needs to change this password
74
     */
75
    public const REDIRECT_TARGET = 'user_settings';
76
    private $security;
77
    private $flashBag;
78
    private $httpUtils;
79
80
    public function __construct(Security $security, FlashBagInterface $flashBag, HttpUtils $httpUtils)
81
    {
82
        $this->security = $security;
83
        $this->flashBag = $flashBag;
84
        $this->httpUtils = $httpUtils;
85
    }
86
87
    /**
88
     * This function is called when the kernel encounters a request.
89
     * It checks if the user must change its password or add an 2FA mehtod and redirect it to the user settings page,
90
     * if needed.
91
     */
92
    public function redirectToSettingsIfNeeded(RequestEvent $event): void
93
    {
94
        $user = $this->security->getUser();
95
        $request = $event->getRequest();
96
97
        if (! $event->isMasterRequest()) {
98
            return;
99
        }
100
        if (! $user instanceof User) {
101
            return;
102
        }
103
104
        //Abort if we dont need to redirect the user.
105
        if (! $user->isNeedPwChange() && ! static::TFARedirectNeeded($user)) {
106
            return;
107
        }
108
109
        //Check for a whitelisted URL
110
        foreach (static::ALLOWED_ROUTES as $route) {
111
            //Dont do anything if we encounter an allowed route
112
            if ($this->httpUtils->checkRequestPath($request, $route)) {
113
                return;
114
            }
115
        }
116
117
        /* Dont redirect tree endpoints, as this would cause trouble and creates multiple flash
118
        warnigs for one page reload */
119
        if (false !== strpos($request->getUri(), '/tree/')) {
120
            return;
121
        }
122
123
        //Show appropriate message to user about the reason he was redirected
124
        if ($user->isNeedPwChange()) {
125
            $this->flashBag->add('warning', 'user.pw_change_needed.flash');
126
        }
127
128
        if (static::TFARedirectNeeded($user)) {
129
            $this->flashBag->add('warning', 'user.2fa_needed.flash');
130
        }
131
132
        $event->setResponse($this->httpUtils->createRedirectResponse($request, static::REDIRECT_TARGET));
133
    }
134
135
    /**
136
     * Check if a redirect because of a missing 2FA method is needed.
137
     * That is the case if the group of the user enforces 2FA, but the user has neither Google Authenticator nor an
138
     * U2F key setup.
139
     *
140
     * @param User $user The user for which should be checked if it needs to be redirected.
141
     *
142
     * @return bool True if the user needs to be redirected.
143
     */
144
    public static function TFARedirectNeeded(User $user): bool
145
    {
146
        $tfa_enabled = $user->isU2FAuthEnabled() || $user->isGoogleAuthenticatorEnabled();
147
148
        if (null !== $user->getGroup() && $user->getGroup()->isEnforce2FA() && ! $tfa_enabled) {
149
            return true;
150
        }
151
152
        return false;
153
    }
154
155
    public static function getSubscribedEvents()
156
    {
157
        return [
158
            KernelEvents::REQUEST => 'redirectToSettingsIfNeeded',
159
        ];
160
    }
161
}
162