Passed
Push — master ( 4ec39a...24c59a )
by
unknown
15:04
created

BackendUserController   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 355
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 134
dl 0
loc 355
rs 9.6
c 0
b 0
f 0
wmc 35

16 Methods

Rating   Name   Duplication   Size   Complexity  
A processRequest() 0 10 2
A initializeView() 0 6 1
A getBackendUserAuthentication() 0 3 1
A indexAction() 0 22 4
A initiatePasswordResetAction() 0 25 4
A removeFromCompareListAction() 0 5 1
A __construct() 0 12 1
A generateListOfMostRecentSwitchedUsers() 0 19 4
A compareAction() 0 16 3
A terminateBackendUserSessionAction() 0 9 2
A onlineAction() 0 15 2
A switchUser() 0 41 4
A addToCompareListAction() 0 5 1
A getSessionBackend() 0 4 1
A getOnlineBackendUsers() 0 10 3
A showAction() 0 6 1
1
<?php
2
3
namespace TYPO3\CMS\Beuser\Controller;
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
use TYPO3\CMS\Backend\Authentication\Event\SwitchUserEvent;
19
use TYPO3\CMS\Backend\Authentication\PasswordReset;
20
use TYPO3\CMS\Beuser\Domain\Model\BackendUser;
21
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserGroupRepository;
22
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserRepository;
23
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserSessionRepository;
24
use TYPO3\CMS\Beuser\Service\ModuleDataStorageService;
25
use TYPO3\CMS\Beuser\Service\UserInformationService;
26
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
27
use TYPO3\CMS\Core\Context\Context;
28
use TYPO3\CMS\Core\Messaging\FlashMessage;
29
use TYPO3\CMS\Core\Session\Backend\SessionBackendInterface;
30
use TYPO3\CMS\Core\Session\SessionManager;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
use TYPO3\CMS\Core\Utility\HttpUtility;
33
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
34
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
35
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
36
37
/**
38
 * Backend module user administration controller
39
 * @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
40
 */
41
class BackendUserController extends ActionController
42
{
43
    /**
44
     * @var int
45
     */
46
    const RECENT_USERS_LIMIT = 3;
47
48
    /**
49
     * @var \TYPO3\CMS\Beuser\Domain\Model\ModuleData
50
     */
51
    protected $moduleData;
52
53
    /**
54
     * @var ModuleDataStorageService
55
     */
56
    protected $moduleDataStorageService;
57
58
    /**
59
     * @var BackendUserRepository
60
     */
61
    protected $backendUserRepository;
62
63
    /**
64
     * @var BackendUserGroupRepository
65
     */
66
    protected $backendUserGroupRepository;
67
68
    /**
69
     * @var BackendUserSessionRepository
70
     */
71
    protected $backendUserSessionRepository;
72
73
    /**
74
     * @var UserInformationService
75
     */
76
    protected $userInformationService;
77
78
    public function __construct(
79
        ModuleDataStorageService $moduleDataStorageService,
80
        BackendUserRepository $backendUserRepository,
81
        BackendUserGroupRepository $backendUserGroupRepository,
82
        BackendUserSessionRepository $backendUserSessionRepository,
83
        UserInformationService $userInformationService
84
    ) {
85
        $this->moduleDataStorageService = $moduleDataStorageService;
86
        $this->backendUserRepository = $backendUserRepository;
87
        $this->backendUserGroupRepository = $backendUserGroupRepository;
88
        $this->backendUserSessionRepository = $backendUserSessionRepository;
89
        $this->userInformationService = $userInformationService;
90
    }
91
92
    /**
93
     * Load and persist module data
94
     *
95
     * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
96
     * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response
97
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
98
     */
99
    public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
100
    {
101
        $this->moduleData = $this->moduleDataStorageService->loadModuleData();
102
        // We "finally" persist the module data.
103
        try {
104
            parent::processRequest($request, $response);
105
            $this->moduleDataStorageService->persistModuleData($this->moduleData);
106
        } catch (\TYPO3\CMS\Extbase\Mvc\Exception\StopActionException $e) {
107
            $this->moduleDataStorageService->persistModuleData($this->moduleData);
108
            throw $e;
109
        }
110
    }
111
112
    /**
113
     * Assign default variables to view
114
     * @param ViewInterface $view
115
     */
116
    protected function initializeView(ViewInterface $view)
117
    {
118
        $view->assignMultiple([
119
            'shortcutLabel' => 'backendUsers',
120
            'dateFormat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'],
121
            'timeFormat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
122
        ]);
123
    }
124
125
    /**
126
     * Displays all BackendUsers
127
     * - Switch session to different user
128
     *
129
     * @param \TYPO3\CMS\Beuser\Domain\Model\Demand $demand
130
     */
131
    public function indexAction(\TYPO3\CMS\Beuser\Domain\Model\Demand $demand = null)
132
    {
133
        if ($demand === null) {
134
            $demand = $this->moduleData->getDemand();
135
        } else {
136
            $this->moduleData->setDemand($demand);
137
        }
138
        // Switch user until logout
139
        $switchUser = (int)GeneralUtility::_GP('SwitchUser');
140
        if ($switchUser > 0) {
141
            $this->switchUser($switchUser);
142
        }
143
        $compareUserList = $this->moduleData->getCompareUserList();
144
145
        $this->view->assignMultiple([
146
            'onlineBackendUsers' => $this->getOnlineBackendUsers(),
147
            'demand' => $demand,
148
            'backendUsers' => $this->backendUserRepository->findDemanded($demand),
149
            'backendUserGroups' => array_merge([''], $this->backendUserGroupRepository->findAll()->toArray()),
150
            'compareUserUidList' => array_combine($compareUserList, $compareUserList),
151
            'currentUserUid' => $this->getBackendUserAuthentication()->user['uid'],
152
            'compareUserList' => !empty($compareUserList) ? $this->backendUserRepository->findByUidList($compareUserList) : '',
153
        ]);
154
    }
155
156
    /**
157
     * Views all currently logged in BackendUsers and their sessions
158
     */
159
    public function onlineAction()
160
    {
161
        $onlineUsersAndSessions = [];
162
        $onlineUsers = $this->backendUserRepository->findOnline();
163
        foreach ($onlineUsers as $onlineUser) {
164
            $onlineUsersAndSessions[] = [
165
                'backendUser' => $onlineUser,
166
                'sessions' => $this->backendUserSessionRepository->findByBackendUser($onlineUser)
167
            ];
168
        }
169
170
        $this->view->assignMultiple([
171
            'shortcutLabel' => 'onlineUsers',
172
            'onlineUsersAndSessions' => $onlineUsersAndSessions,
173
            'currentSessionId' => $this->getBackendUserAuthentication()->user['ses_id'],
174
        ]);
175
    }
176
177
    /**
178
     * @param int $uid
179
     */
180
    public function showAction(int $uid = 0): void
181
    {
182
        $data = $this->userInformationService->get($uid);
183
        $this->view->assignMultiple([
184
            'shortcutLabel' => 'showUser',
185
            'data' => $data
186
        ]);
187
    }
188
189
    /**
190
     * Compare backend users from demand
191
     */
192
    public function compareAction()
193
    {
194
        $compareUserList = $this->moduleData->getCompareUserList();
195
        if (empty($compareUserList)) {
196
            $this->redirect('index');
197
        }
198
199
        $compareData = [];
200
        foreach ($compareUserList as $uid) {
201
            $compareData[] =  $this->userInformationService->get($uid);
202
        }
203
204
        $this->view->assignMultiple([
205
            'shortcutLabel' => 'compareUsers',
206
            'compareUserList' => $compareData,
207
            'onlineBackendUsers' => $this->getOnlineBackendUsers()
208
        ]);
209
    }
210
211
    /**
212
     * Starts the password reset process for a selected user.
213
     *
214
     * @param int $user
215
     */
216
    public function initiatePasswordResetAction(int $user): void
217
    {
218
        $context = GeneralUtility::makeInstance(Context::class);
219
        /** @var BackendUser $user */
220
        $user = $this->backendUserRepository->findByUid($user);
0 ignored issues
show
Bug introduced by
$user of type TYPO3\CMS\Beuser\Domain\Model\BackendUser is incompatible with the type integer expected by parameter $uid of TYPO3\CMS\Extbase\Persis...Repository::findByUid(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

220
        $user = $this->backendUserRepository->findByUid(/** @scrutinizer ignore-type */ $user);
Loading history...
221
        if (!$user || !$user->isPasswordResetEnabled() || !$context->getAspect('backend.user')->isAdmin()) {
0 ignored issues
show
introduced by
$user is of type TYPO3\CMS\Beuser\Domain\Model\BackendUser, thus it always evaluated to true.
Loading history...
222
            // Add an error message
223
            $this->addFlashMessage(
224
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.error.text', 'beuser'),
225
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.error.title', 'beuser'),
226
                FlashMessage::ERROR
227
            );
228
        } else {
229
            GeneralUtility::makeInstance(PasswordReset::class)->initiateReset(
230
                $GLOBALS['TYPO3_REQUEST'],
231
                $context,
232
                $user->getEmail()
233
            );
234
            $this->addFlashMessage(
235
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.success.text', 'beuser', [$user->getEmail()]),
236
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.success.title', 'beuser'),
237
                FlashMessage::OK
238
            );
239
        }
240
        $this->forward('index');
241
    }
242
243
    /**
244
     * Attaches one backend user to the compare list
245
     *
246
     * @param int $uid
247
     */
248
    public function addToCompareListAction($uid)
249
    {
250
        $this->moduleData->attachUidCompareUser($uid);
251
        $this->moduleDataStorageService->persistModuleData($this->moduleData);
252
        $this->forward('index');
253
    }
254
255
    /**
256
     * Removes given backend user to the compare list
257
     *
258
     * @param int $uid
259
     */
260
    public function removeFromCompareListAction($uid)
261
    {
262
        $this->moduleData->detachUidCompareUser($uid);
263
        $this->moduleDataStorageService->persistModuleData($this->moduleData);
264
        $this->forward('index');
265
    }
266
267
    /**
268
     * Terminate BackendUser session and logout corresponding client
269
     * Redirects to onlineAction with message
270
     *
271
     * @param \TYPO3\CMS\Beuser\Domain\Model\BackendUser $backendUser
272
     * @param string $sessionId
273
     */
274
    protected function terminateBackendUserSessionAction(\TYPO3\CMS\Beuser\Domain\Model\BackendUser $backendUser, $sessionId)
0 ignored issues
show
Unused Code introduced by
The parameter $backendUser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

274
    protected function terminateBackendUserSessionAction(/** @scrutinizer ignore-unused */ \TYPO3\CMS\Beuser\Domain\Model\BackendUser $backendUser, $sessionId)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
275
    {
276
        $sessionBackend = $this->getSessionBackend();
277
        $success = $sessionBackend->remove($sessionId);
278
279
        if ($success) {
280
            $this->addFlashMessage(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:terminateSessionSuccess', 'beuser'));
281
        }
282
        $this->forward('online');
283
    }
284
285
    /**
286
     * Switches to a given user (SU-mode) and then redirects to the start page of the backend to refresh the navigation etc.
287
     *
288
     * @param string $switchUser BE-user record that will be switched to
289
     */
290
    protected function switchUser($switchUser)
291
    {
292
        $targetUser = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('be_users', $switchUser);
0 ignored issues
show
Bug introduced by
$switchUser of type string is incompatible with the type integer expected by parameter $uid of TYPO3\CMS\Backend\Utilit...endUtility::getRecord(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

292
        $targetUser = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('be_users', /** @scrutinizer ignore-type */ $switchUser);
Loading history...
293
        if (is_array($targetUser) && $this->getBackendUserAuthentication()->isAdmin()) {
294
            // Set backend user listing module as starting module for switchback
295
            $this->getBackendUserAuthentication()->uc['startModuleOnFirstLogin'] = 'system_BeuserTxBeuser';
296
            $this->getBackendUserAuthentication()->uc['recentSwitchedToUsers'] = $this->generateListOfMostRecentSwitchedUsers($targetUser['uid']);
297
            $this->getBackendUserAuthentication()->writeUC();
298
299
            // User switch   written to log
300
            $this->getBackendUserAuthentication()->writelog(
301
                255,
302
                2,
303
                0,
304
                1,
305
                'User %s switched to user %s (be_users:%s)',
306
                [
307
                    $this->getBackendUserAuthentication()->user['username'],
308
                    $targetUser['username'],
309
                    $targetUser['uid'],
310
                ]
311
            );
312
313
            $sessionBackend = $this->getSessionBackend();
314
            $sessionBackend->update(
315
                $this->getBackendUserAuthentication()->getSessionId(),
316
                [
317
                    'ses_userid' => (int)$targetUser['uid'],
318
                    'ses_backuserid' => (int)$this->getBackendUserAuthentication()->user['uid']
319
                ]
320
            );
321
322
            $event = new SwitchUserEvent(
323
                $this->getBackendUserAuthentication()->getSessionId(),
324
                $targetUser,
325
                $this->getBackendUserAuthentication()->user
326
            );
327
            $this->eventDispatcher->dispatch($event);
328
329
            $redirectUrl = 'index.php' . ($GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces'] ? '' : '?commandLI=1');
330
            HttpUtility::redirect($redirectUrl);
331
        }
332
    }
333
334
    /**
335
     * Generates a list of users to whom where switched in the past. This is limited by RECENT_USERS_LIMIT.
336
     *
337
     * @param int $targetUserUid
338
     * @return int[]
339
     */
340
    protected function generateListOfMostRecentSwitchedUsers(int $targetUserUid): array
341
    {
342
        $latestUserUids = [];
343
        $backendUser = $this->getBackendUserAuthentication();
344
345
        if (isset($backendUser->uc['recentSwitchedToUsers']) && is_array($backendUser->uc['recentSwitchedToUsers'])) {
346
            $latestUserUids = $backendUser->uc['recentSwitchedToUsers'];
347
        }
348
349
        // Remove potentially existing user in that list
350
        $index = array_search($targetUserUid, $latestUserUids, true);
351
        if ($index !== false) {
352
            unset($latestUserUids[$index]);
353
        }
354
355
        array_unshift($latestUserUids, $targetUserUid);
356
        $latestUserUids = array_slice($latestUserUids, 0, static::RECENT_USERS_LIMIT);
357
358
        return $latestUserUids;
359
    }
360
361
    /**
362
     * @return BackendUserAuthentication
363
     */
364
    protected function getBackendUserAuthentication(): BackendUserAuthentication
365
    {
366
        return $GLOBALS['BE_USER'];
367
    }
368
369
    /**
370
     * @return SessionBackendInterface
371
     */
372
    protected function getSessionBackend()
373
    {
374
        $loginType = $this->getBackendUserAuthentication()->getLoginType();
375
        return GeneralUtility::makeInstance(SessionManager::class)->getSessionBackend($loginType);
376
    }
377
378
    /**
379
     * Create an array with the uids of online users as the keys
380
     * [
381
     *   1 => true,
382
     *   5 => true
383
     * ]
384
     * @return array
385
     */
386
    protected function getOnlineBackendUsers(): array
387
    {
388
        $onlineUsers = $this->backendUserSessionRepository->findAllActive();
389
        $onlineBackendUsers = [];
390
        if (is_array($onlineUsers)) {
0 ignored issues
show
introduced by
The condition is_array($onlineUsers) is always true.
Loading history...
391
            foreach ($onlineUsers as $onlineUser) {
392
                $onlineBackendUsers[$onlineUser['ses_userid']] = true;
393
            }
394
        }
395
        return $onlineBackendUsers;
396
    }
397
}
398