Passed
Push — master ( 8c95d4...e694c7 )
by Torben
03:09 queued 11s
created

FrontendUserService::getFrontendUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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