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