Passed
Push — master ( cad405...711cdb )
by Torben
04:30
created

FrontendUserService::injectSignalSlotDispatcher()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
declare(strict_types=1);
3
namespace Derhansen\FeChangePwd\Service;
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 TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
13
use TYPO3\CMS\Core\Database\ConnectionPool;
14
use TYPO3\CMS\Core\Utility\GeneralUtility;
15
use TYPO3\CMS\Saltedpasswords\Salt\SaltFactory;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Saltedpasswords\Salt\SaltFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Saltedpassword...\SaltedPasswordsUtility was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
18
/**
19
 * Class FrontendUserService
20
 */
21
class FrontendUserService
22
{
23
    /**
24
     * The session key
25
     */
26
    const SESSION_KEY = 'mustChangePasswordReason';
27
28
    /**
29
     * @var SettingsService
30
     */
31
    protected $settingsService = null;
32
33
    /**
34
     * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
35
     */
36
    protected $signalSlotDispatcher = null;
37
38
    /**
39
     * @param SettingsService $settingsService
40
     */
41
    public function injectSettingsService(\Derhansen\FeChangePwd\Service\SettingsService $settingsService)
42
    {
43
        $this->settingsService = $settingsService;
44
    }
45
46
    /**
47
     * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
48
     */
49
    public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
50
    {
51
        $this->signalSlotDispatcher = $signalSlotDispatcher;
52
    }
53
54
    /**
55
     * Returns if the frontend user must change the password
56
     *
57
     * @param array $feUserRecord
58
     * @return bool
59
     */
60
    public function mustChangePassword(array $feUserRecord)
61
    {
62
        $reason = '';
63
        $result = false;
64
        $mustChangePassword = $feUserRecord['must_change_password'] ?? 0;
65
        $passwordExpiryTimestamp = $feUserRecord['password_expiry_date'] ?? 0;
66
        if ((bool)$mustChangePassword) {
67
            $reason = 'forcedChange';
68
            $result = true;
69
        } elseif (((int)$passwordExpiryTimestamp > 0 && (int)$passwordExpiryTimestamp < time())) {
70
            $reason = 'passwordExpired';
71
            $result = true;
72
        }
73
74
        if ($result) {
75
            // Store reason for password change in user session
76
            $this->getFrontendUser()->setKey('ses', self::SESSION_KEY, $reason);
77
            $this->getFrontendUser()->storeSessionData();
78
        }
79
        return $result;
80
    }
81
82
    /**
83
     * Returns the reason for the password change stored in the session
84
     *
85
     * @return mixed
86
     */
87
    public function getMustChangePasswordReason()
88
    {
89
        return $this->getFrontendUser()->getKey('ses', self::SESSION_KEY);
90
    }
91
92
    /**
93
     * Updates the password of the current user if a current user session exist
94
     *
95
     * @param string $newPassword
96
     * @return void
97
     */
98
    public function updatePassword(string $newPassword)
99
    {
100
        if (!$this->isUserLoggedIn()) {
101
            return;
102
        }
103
104
        $password = $this->getPasswordHash($newPassword);
105
106
        $userTable = $this->getFrontendUser()->user_table;
107
        $userUid = $this->getFrontendUser()->user['uid'];
108
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($userTable);
109
        $queryBuilder->getRestrictions()->removeAll();
110
        $queryBuilder->update($userTable)
111
            ->set('password', $password)
112
            ->set('must_change_password', 0)
113
            ->set('password_expiry_date', $this->settingsService->getPasswordExpiryTimestamp())
114
            ->set('tstamp', (int)$GLOBALS['EXEC_TIME'])
115
            ->where(
116
                $queryBuilder->expr()->eq(
117
                    'uid',
118
                    $queryBuilder->createNamedParameter($userUid, \PDO::PARAM_INT)
119
                )
120
            )
121
            ->execute();
122
123
        $this->signalSlotDispatcher->dispatch(
124
            __CLASS__,
125
            __FUNCTION__ . 'AfterUpdate',
126
            [
127
                $this->getFrontendUser()->user,
128
                $this
129
            ]
130
        );
131
132
        // Unset reason for password change in user session
133
        $this->getFrontendUser()->setKey('ses', self::SESSION_KEY, null);
134
    }
135
136
    /**
137
     * Returns the changeHmac for the current logged in user
138
     *
139
     * @return string
140
     */
141
    public function getChangeHmac(): string
142
    {
143
        if (!$this->isUserLoggedIn()) {
144
            return '';
145
        }
146
147
        $userUid = $this->getFrontendUser()->user['uid'];
148
        if (!is_int($userUid) || (int)$userUid <= 0) {
149
            throw new InvalidUserException('The fe_user uid is not a positive number.', 1574102778917);
150
        }
151
152
        $tstamp = $this->getFrontendUser()->user['tstamp'];
153
        return GeneralUtility::hmac('fe_user_' . $userUid . '_' . $tstamp, 'fe_change_pwd');
154
    }
155
156
    /**
157
     * Validates the given changeHmac
158
     *
159
     * @param string $changeHmac
160
     * @return bool
161
     */
162
    public function validateChangeHmac(string $changeHmac): bool
163
    {
164
        return is_string($changeHmac) && $changeHmac !== '' && hash_equals($this->getChangeHmac(), $changeHmac);
165
    }
166
167
    /**
168
     * Returns a password hash
169
     *
170
     * @param string $password
171
     * @return string
172
     * @throws MissingPasswordHashServiceException
173
     * @throws \TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException
174
     */
175
    protected function getPasswordHash(string $password)
176
    {
177
        if (class_exists(PasswordHashFactory::class)) {
178
            $hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('FE');
179
            $password = $hashInstance->getHashedPassword($password);
180
        } elseif (SaltedPasswordsUtility::isUsageEnabled('FE')) {
181
            $saltingInstance = SaltFactory::getSaltingInstance();
182
            $password = $saltingInstance->getHashedPassword($password);
183
        } else {
184
            throw new MissingPasswordHashServiceException(
185
                'No secure password hashing service could be initialized. Please check your TYPO3 system configuration',
186
                1557550040515
187
            );
188
        }
189
190
        return $password;
191
    }
192
193
    /**
194
     * Returns is there is a current user login
195
     *
196
     * @return bool
197
     */
198
    public function isUserLoggedIn()
199
    {
200
        return $GLOBALS['TSFE']->loginUser;
201
    }
202
203
    /**
204
     * Returns the frontendUserAuthentication
205
     *
206
     * @return \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
207
     */
208
    protected function getFrontendUser()
209
    {
210
        return $GLOBALS['TSFE']->fe_user;
211
    }
212
}
213