Completed
Push — master ( 504537...babb2f )
by Torben
04:05
created

evaluateRequireCurrentPassword()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 22
rs 9.7998
cc 4
nc 4
nop 1
1
<?php
2
declare(strict_types=1);
3
namespace Derhansen\FeChangePwd\Validation\Validator;
4
5
/*
6
 * This file is part of the Extension "fe_change_pwd" for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
use Derhansen\FeChangePwd\Domain\Model\Dto\ChangePassword;
13
use Derhansen\FeChangePwd\Service\LocalizationService;
14
use Derhansen\FeChangePwd\Service\OldPasswordService;
15
use Derhansen\FeChangePwd\Service\PwnedPasswordsService;
16
use Derhansen\FeChangePwd\Service\SettingsService;
17
18
/**
19
 * Class RegistrationValidator
20
 */
21
class ChangePasswordValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
22
{
23
    /**
24
     * Available password checks
25
     *
26
     * @var array
27
     */
28
    protected $checks = [
29
        'capitalCharCheck',
30
        'lowerCaseCharCheck',
31
        'digitCheck',
32
        'specialCharCheck',
33
    ];
34
35
    /**
36
     * @var SettingsService
37
     */
38
    protected $settingsService = null;
39
40
    /**
41
     * @var LocalizationService
42
     */
43
    protected $localizationService = null;
44
45
    /**
46
     * @var PwnedPasswordsService
47
     */
48
    protected $pwnedPasswordsService = null;
49
50
    /**
51
     * @var OldPasswordService
52
     */
53
    protected $oldPasswordService = null;
54
55
    /**
56
     * @param SettingsService $settingsService
57
     */
58
    public function injectSettingsService(\Derhansen\FeChangePwd\Service\SettingsService $settingsService)
59
    {
60
        $this->settingsService = $settingsService;
61
    }
62
63
    /**
64
     * @param LocalizationService $localizationService
65
     */
66
    public function injectLocalizationService(
67
        \Derhansen\FeChangePwd\Service\LocalizationService $localizationService
68
    ) {
69
        $this->localizationService = $localizationService;
70
    }
71
72
    /**
73
     * @param OldPasswordService $oldPasswordService
74
     */
75
    public function injectOldPasswordService(\Derhansen\FeChangePwd\Service\OldPasswordService $oldPasswordService)
76
    {
77
        $this->oldPasswordService = $oldPasswordService;
78
    }
79
80
    /**
81
     * @param PwnedPasswordsService $PwnedPasswordsService
82
     */
83
    public function injectPwnedPasswordsService(
84
        \Derhansen\FeChangePwd\Service\PwnedPasswordsService $PwnedPasswordsService
85
    ) {
86
        $this->pwnedPasswordsService = $PwnedPasswordsService;
87
    }
88
89
    /**
90
     * Validates the password of the given ChangePassword object against the configured password complexity
91
     *
92
     * @param ChangePassword $value
93
     *
94
     * @return bool
95
     */
96
    protected function isValid($value)
97
    {
98
        $result = true;
99
        $settings = $this->settingsService->getSettings();
100
101
        // Early return if old password is required, but either empty or not valid
102
        if (isset($settings['requireCurrentPassword']['enabled']) &&
103
            (bool)$settings['requireCurrentPassword']['enabled']
104
        ) {
105
            $requireCurrentPasswordResult = $this->evaluateRequireCurrentPassword($value);
106
            if ($requireCurrentPasswordResult === false) {
107
                return false;
108
            }
109
        }
110
111
        // Early return if no passwords are given
112
        if ($value->getPassword1() === '' || $value->getPassword2() === '') {
113
            $this->addError(
114
                $this->localizationService->translate('passwordFieldsEmptyOrNotBothFilledOut'),
115
                1537701950
116
            );
117
118
            return false;
119
        }
120
121
        if ($value->getPassword1() !== $value->getPassword2()) {
122
            $this->addError(
123
                $this->localizationService->translate('passwordsDoNotMatch'),
124
                1537701950
125
            );
126
            // Early return, no other checks need to be done if passwords do not match
127
            return false;
128
        }
129
130
        if (isset($settings['passwordComplexity']['minLength'])) {
131
            $this->evaluateMinLengthCheck($value, (int)$settings['passwordComplexity']['minLength']);
132
        }
133
134
        foreach ($this->checks as $check) {
135
            if (isset($settings['passwordComplexity'][$check]) &&
136
                (bool)$settings['passwordComplexity'][$check]
137
            ) {
138
                $this->evaluatePasswordCheck($value, $check);
139
            }
140
        }
141
142
        if (isset($settings['pwnedpasswordsCheck']['enabled']) && (bool)$settings['pwnedpasswordsCheck']['enabled']) {
143
            $this->evaluatePwnedPasswordCheck($value);
144
        }
145
146
        if (isset($settings['oldPasswordCheck']['enabled']) && (bool)$settings['oldPasswordCheck']['enabled']) {
147
            $this->evaluateOldPasswordCheck($value);
148
        }
149
150
        if ($this->result->hasErrors()) {
151
            $result = false;
152
        }
153
154
        return $result;
155
    }
156
157
    /**
158
     * Checks if the password complexity in regards to minimum password length in met
159
     *
160
     * @param ChangePassword $changePassword
161
     * @param int $minLength
162
     * @return void
163
     */
164
    protected function evaluateMinLengthCheck(ChangePassword $changePassword, int $minLength)
165
    {
166
        if (strlen($changePassword->getPassword1()) < $minLength) {
167
            $this->addError(
168
                $this->localizationService->translate('passwordComplexity.failure.minLength', [$minLength]),
169
                1537898028
170
            );
171
        };
172
    }
173
174
    /**
175
     * Evaluates the password complexity in regards to the given check
176
     *
177
     * @param ChangePassword $changePassword
178
     * @param string $check
179
     * @return void
180
     */
181
    protected function evaluatePasswordCheck(ChangePassword $changePassword, $check)
182
    {
183
        $patterns = [
184
            'capitalCharCheck' => '/[A-Z]/',
185
            'lowerCaseCharCheck' => '/[a-z]/',
186
            'digitCheck' => '/[0-9]/',
187
            'specialCharCheck' => '/[^0-9a-z]/i'
188
        ];
189
190
        if (isset($patterns[$check])) {
191
            if (!preg_match($patterns[$check], $changePassword->getPassword1()) > 0) {
192
                $this->addError(
193
                    $this->localizationService->translate('passwordComplexity.failure.' . $check),
194
                    1537898029
195
                );
196
            }
197
        }
198
    }
199
200
    /**
201
     * Evaluates the password using the pwnedpasswords API
202
     *
203
     * @param ChangePassword $changePassword
204
     * @return void
205
     */
206
    protected function evaluatePwnedPasswordCheck(ChangePassword $changePassword)
207
    {
208
        $foundCount = $this->pwnedPasswordsService->checkPassword($changePassword->getPassword1());
209
        if ($foundCount > 0) {
210
            $this->addError(
211
                $this->localizationService->translate('pwnedPasswordFailure', [$foundCount]),
212
                1537898030
213
            );
214
        }
215
    }
216
217
    /**
218
     * Evaluates the password against the current password
219
     *
220
     * @param ChangePassword $changePassword
221
     * @return void
222
     */
223
    protected function evaluateOldPasswordCheck(ChangePassword $changePassword)
224
    {
225
        if ($this->oldPasswordService->checkEqualsOldPassword($changePassword->getPassword1())) {
226
            $this->addError(
227
                $this->localizationService->translate('oldPasswordFailure'),
228
                1570880406065
229
            );
230
        }
231
    }
232
233
    /**
234
     * Evaluates if the current password is not empty and valid
235
     *
236
     * @param ChangePassword $changePassword
237
     * @return bool
238
     */
239
    protected function evaluateRequireCurrentPassword(ChangePassword $changePassword): bool
240
    {
241
        $result = true;
242
        $oldPasswordEmpty = $changePassword->getCurrentPassword() === '';
243
        if ($oldPasswordEmpty) {
244
            $result = false;
245
            $this->addError(
246
                $this->localizationService->translate('currentPasswordEmpty'),
247
                1570880411334
248
            );
249
        }
250
251
        if ($oldPasswordEmpty === false &&
252
            !$this->oldPasswordService->checkEqualsOldPassword($changePassword->getCurrentPassword())
253
        ) {
254
            $result = false;
255
            $this->addError(
256
                $this->localizationService->translate('currentPasswordFailure'),
257
                1570880417020
258
            );
259
        }
260
        return $result;
261
    }
262
}
263