Passed
Push — master ( f179a5...b52f67 )
by
unknown
20:41 queued 07:39
created

BackendUserController::processRequest()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 3
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Beuser\Controller;
17
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use TYPO3\CMS\Backend\Authentication\Event\SwitchUserEvent;
21
use TYPO3\CMS\Backend\Authentication\PasswordReset;
22
use TYPO3\CMS\Backend\Routing\UriBuilder;
23
use TYPO3\CMS\Backend\Utility\BackendUtility;
24
use TYPO3\CMS\Beuser\Domain\Model\BackendUser;
25
use TYPO3\CMS\Beuser\Domain\Model\Demand;
26
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserGroupRepository;
27
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserRepository;
28
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserSessionRepository;
29
use TYPO3\CMS\Beuser\Service\ModuleDataStorageService;
30
use TYPO3\CMS\Beuser\Service\UserInformationService;
31
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
32
use TYPO3\CMS\Core\Context\Context;
33
use TYPO3\CMS\Core\Http\PropagateResponseException;
34
use TYPO3\CMS\Core\Http\RedirectResponse;
35
use TYPO3\CMS\Core\Messaging\FlashMessage;
36
use TYPO3\CMS\Core\Pagination\SimplePagination;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
use TYPO3\CMS\Extbase\Http\ForwardResponse;
39
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
40
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
41
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
42
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
43
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
44
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
45
46
/**
47
 * Backend module user administration controller
48
 * @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
49
 */
50
class BackendUserController extends ActionController
51
{
52
    /**
53
     * @var int
54
     */
55
    const RECENT_USERS_LIMIT = 3;
56
57
    /**
58
     * @var \TYPO3\CMS\Beuser\Domain\Model\ModuleData
59
     */
60
    protected $moduleData;
61
62
    /**
63
     * @var ModuleDataStorageService
64
     */
65
    protected $moduleDataStorageService;
66
67
    /**
68
     * @var BackendUserRepository
69
     */
70
    protected $backendUserRepository;
71
72
    /**
73
     * @var BackendUserGroupRepository
74
     */
75
    protected $backendUserGroupRepository;
76
77
    /**
78
     * @var BackendUserSessionRepository
79
     */
80
    protected $backendUserSessionRepository;
81
82
    /**
83
     * @var UserInformationService
84
     */
85
    protected $userInformationService;
86
87
    public function __construct(
88
        ModuleDataStorageService $moduleDataStorageService,
89
        BackendUserRepository $backendUserRepository,
90
        BackendUserGroupRepository $backendUserGroupRepository,
91
        BackendUserSessionRepository $backendUserSessionRepository,
92
        UserInformationService $userInformationService
93
    ) {
94
        $this->moduleDataStorageService = $moduleDataStorageService;
95
        $this->backendUserRepository = $backendUserRepository;
96
        $this->backendUserGroupRepository = $backendUserGroupRepository;
97
        $this->backendUserSessionRepository = $backendUserSessionRepository;
98
        $this->userInformationService = $userInformationService;
99
    }
100
101
    /**
102
     * Load and persist module data
103
     *
104
     * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
105
     * @return ResponseInterface
106
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
107
     */
108
    public function processRequest(RequestInterface $request): ResponseInterface
109
    {
110
        $this->moduleData = $this->moduleDataStorageService->loadModuleData();
111
        // We "finally" persist the module data.
112
        try {
113
            $response = parent::processRequest($request);
114
            $this->moduleDataStorageService->persistModuleData($this->moduleData);
115
            return $response;
116
        } catch (StopActionException $e) {
117
            $this->moduleDataStorageService->persistModuleData($this->moduleData);
118
            throw $e;
119
        }
120
    }
121
122
    /**
123
     * Assign default variables to view
124
     * @param ViewInterface $view
125
     */
126
    protected function initializeView(ViewInterface $view)
127
    {
128
        $view->assignMultiple([
129
            'shortcutLabel' => 'backendUsers',
130
            'route' => $this->getRequest()->getAttribute('route')->getPath(),
131
            'action' => $this->request->getControllerActionName(),
132
            'controller' => $this->request->getControllerName(),
133
            'dateFormat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'],
134
            'timeFormat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
135
        ]);
136
    }
137
138
    /**
139
     * Displays all BackendUsers
140
     * - Switch session to different user
141
     *
142
     * @param \TYPO3\CMS\Beuser\Domain\Model\Demand $demand
143
     * @param int $currentPage
144
     * @param string $operation
145
     */
146
    public function indexAction(Demand $demand = null, int $currentPage = 1, string $operation = ''): ResponseInterface
147
    {
148
        if ($operation === 'reset-filters') {
149
            // Reset the module data demand object
150
            $this->moduleData->setDemand(GeneralUtility::makeInstance(Demand::class));
151
            $this->moduleDataStorageService->persistModuleData($this->moduleData);
152
            $demand = null;
153
        }
154
        if ($demand === null) {
155
            $demand = $this->moduleData->getDemand();
156
        } else {
157
            $this->moduleData->setDemand($demand);
158
        }
159
        // Switch user until logout
160
        $switchUser = (int)GeneralUtility::_GP('SwitchUser');
161
        if ($switchUser > 0) {
162
            $this->switchUser($switchUser);
163
        }
164
        $compareUserList = $this->moduleData->getCompareUserList();
165
166
        $backendUsers = $this->backendUserRepository->findDemanded($demand);
167
        $paginator = new QueryResultPaginator($backendUsers, $currentPage, 50);
168
        $pagination = new SimplePagination($paginator);
169
170
        $this->view->assignMultiple([
171
            'onlineBackendUsers' => $this->getOnlineBackendUsers(),
172
            'demand' => $demand,
173
            'paginator' => $paginator,
174
            'pagination' => $pagination,
175
            'totalAmountOfBackendUsers' => $backendUsers->count(),
176
            'backendUserGroups' => array_merge([''], $this->backendUserGroupRepository->findAll()->toArray()),
177
            'compareUserUidList' => array_combine($compareUserList, $compareUserList),
178
            'currentUserUid' => $this->getBackendUserAuthentication()->user['uid'],
179
            'compareUserList' => !empty($compareUserList) ? $this->backendUserRepository->findByUidList($compareUserList) : '',
180
        ]);
181
182
        return $this->htmlResponse($this->view->render());
183
    }
184
185
    /**
186
     * Views all currently logged in BackendUsers and their sessions
187
     */
188
    public function onlineAction(): ResponseInterface
189
    {
190
        $onlineUsersAndSessions = [];
191
        $onlineUsers = $this->backendUserRepository->findOnline();
192
        foreach ($onlineUsers as $onlineUser) {
193
            $onlineUsersAndSessions[] = [
194
                'backendUser' => $onlineUser,
195
                'sessions' => $this->backendUserSessionRepository->findByBackendUser($onlineUser)
196
            ];
197
        }
198
199
        $currentSessionId = $this->backendUserSessionRepository->getPersistedSessionIdentifier($this->getBackendUserAuthentication());
200
201
        $this->view->assignMultiple([
202
            'shortcutLabel' => 'onlineUsers',
203
            'onlineUsersAndSessions' => $onlineUsersAndSessions,
204
            'currentSessionId' => $currentSessionId,
205
        ]);
206
207
        return $this->htmlResponse($this->view->render());
208
    }
209
210
    /**
211
     * @param int $uid
212
     */
213
    public function showAction(int $uid = 0): ResponseInterface
214
    {
215
        $data = $this->userInformationService->getUserInformation($uid);
216
        $this->view->assignMultiple([
217
            'shortcutLabel' => 'showUser',
218
            'data' => $data
219
        ]);
220
221
        return $this->htmlResponse($this->view->render());
222
    }
223
224
    /**
225
     * Compare backend users from demand
226
     */
227
    public function compareAction(): ResponseInterface
228
    {
229
        $compareUserList = $this->moduleData->getCompareUserList();
230
        if (empty($compareUserList)) {
231
            $this->redirect('index');
232
        }
233
234
        $compareData = [];
235
        foreach ($compareUserList as $uid) {
236
            if ($compareInformation = $this->userInformationService->getUserInformation($uid)) {
237
                $compareData[] = $compareInformation;
238
            }
239
        }
240
241
        $this->view->assignMultiple([
242
            'shortcutLabel' => 'compareUsers',
243
            'compareUserList' => $compareData,
244
            'onlineBackendUsers' => $this->getOnlineBackendUsers()
245
        ]);
246
247
        return $this->htmlResponse($this->view->render());
248
    }
249
250
    /**
251
     * Starts the password reset process for a selected user.
252
     *
253
     * @param int $user
254
     */
255
    public function initiatePasswordResetAction(int $user): ResponseInterface
256
    {
257
        $context = GeneralUtility::makeInstance(Context::class);
258
        /** @var BackendUser $user */
259
        $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

259
        $user = $this->backendUserRepository->findByUid(/** @scrutinizer ignore-type */ $user);
Loading history...
260
        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...
261
            // Add an error message
262
            $this->addFlashMessage(
263
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.error.text', 'beuser') ?? '',
264
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.error.title', 'beuser') ?? '',
265
                FlashMessage::ERROR
266
            );
267
        } else {
268
            GeneralUtility::makeInstance(PasswordReset::class)->initiateReset(
269
                $this->getRequest(),
270
                $context,
271
                $user->getEmail()
272
            );
273
            $this->addFlashMessage(
274
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.success.text', 'beuser', [$user->getEmail()]) ?? '',
275
                LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.success.title', 'beuser') ?? '',
276
                FlashMessage::OK
277
            );
278
        }
279
        return new ForwardResponse('index');
280
    }
281
282
    /**
283
     * Attaches one backend user to the compare list
284
     *
285
     * @param int $uid
286
     */
287
    public function addToCompareListAction($uid)
288
    {
289
        $this->moduleData->attachUidCompareUser($uid);
290
        $this->moduleDataStorageService->persistModuleData($this->moduleData);
291
        return new ForwardResponse('index');
292
    }
293
294
    /**
295
     * Removes given backend user to the compare list
296
     *
297
     * @param int $uid
298
     * @param int $redirectToCompare
299
     */
300
    public function removeFromCompareListAction($uid, int $redirectToCompare = 0)
301
    {
302
        $this->moduleData->detachUidCompareUser($uid);
303
        $this->moduleDataStorageService->persistModuleData($this->moduleData);
304
        if ($redirectToCompare) {
305
            $this->redirect('compare');
306
        } else {
307
            $this->redirect('index');
308
        }
309
    }
310
311
    /**
312
     * Removes all backend users from the compare list
313
     */
314
    public function removeAllFromCompareListAction(): void
315
    {
316
        foreach ($this->moduleData->getCompareUserList() as $user) {
317
            $this->moduleData->detachUidCompareUser($user);
318
        }
319
        $this->moduleDataStorageService->persistModuleData($this->moduleData);
320
        $this->redirect('index');
321
    }
322
323
    /**
324
     * Terminate BackendUser session and logout corresponding client
325
     * Redirects to onlineAction with message
326
     *
327
     * @param \TYPO3\CMS\Beuser\Domain\Model\BackendUser $backendUser
328
     * @param string $sessionId
329
     */
330
    protected function terminateBackendUserSessionAction(BackendUser $backendUser, $sessionId)
331
    {
332
        // terminating value of persisted session ID
333
        $success = $this->backendUserSessionRepository->terminateSessionByIdentifier($sessionId);
334
        if ($success) {
335
            $this->addFlashMessage(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:terminateSessionSuccess', 'beuser') ?? '');
336
        }
337
        return new ForwardResponse('online');
338
    }
339
340
    /**
341
     * Switches to a given user (SU-mode) and then redirects to the start page of the backend to refresh the navigation etc.
342
     *
343
     * @param int $switchUser BE-user record that will be switched to
344
     */
345
    protected function switchUser($switchUser)
346
    {
347
        $targetUser = BackendUtility::getRecord('be_users', $switchUser);
348
        if (is_array($targetUser) && $this->getBackendUserAuthentication()->isAdmin()) {
349
            // Set backend user listing module as starting module for switchback
350
            $this->getBackendUserAuthentication()->uc['startModuleOnFirstLogin'] = 'system_BeuserTxBeuser';
351
            $this->getBackendUserAuthentication()->uc['recentSwitchedToUsers'] = $this->generateListOfMostRecentSwitchedUsers($targetUser['uid']);
352
            $this->getBackendUserAuthentication()->writeUC();
353
354
            // User switch   written to log
355
            $this->getBackendUserAuthentication()->writelog(
356
                255,
357
                2,
358
                0,
359
                1,
360
                'User %s switched to user %s (be_users:%s)',
361
                [
362
                    $this->getBackendUserAuthentication()->user['username'],
363
                    $targetUser['username'],
364
                    $targetUser['uid'],
365
                ]
366
            );
367
368
            $this->backendUserSessionRepository->switchToUser($this->getBackendUserAuthentication(), (int)$targetUser['uid']);
369
370
            $event = new SwitchUserEvent(
371
                $this->getBackendUserAuthentication()->getSession()->getIdentifier(),
372
                $targetUser,
373
                (array)$this->getBackendUserAuthentication()->user
374
            );
375
            $this->eventDispatcher->dispatch($event);
376
377
            $redirectUri = GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute(
378
                'main',
379
                $GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces'] ? [] : ['commandLI' => '1']
380
            );
381
            throw new PropagateResponseException(new RedirectResponse($redirectUri, 303), 1607271592);
382
        }
383
    }
384
385
    /**
386
     * Generates a list of users to whom where switched in the past. This is limited by RECENT_USERS_LIMIT.
387
     *
388
     * @param int $targetUserUid
389
     * @return int[]
390
     */
391
    protected function generateListOfMostRecentSwitchedUsers(int $targetUserUid): array
392
    {
393
        $latestUserUids = [];
394
        $backendUser = $this->getBackendUserAuthentication();
395
396
        if (isset($backendUser->uc['recentSwitchedToUsers']) && is_array($backendUser->uc['recentSwitchedToUsers'])) {
397
            $latestUserUids = $backendUser->uc['recentSwitchedToUsers'];
398
        }
399
400
        // Remove potentially existing user in that list
401
        $index = array_search($targetUserUid, $latestUserUids, true);
402
        if ($index !== false) {
403
            unset($latestUserUids[$index]);
404
        }
405
406
        array_unshift($latestUserUids, $targetUserUid);
407
        $latestUserUids = array_slice($latestUserUids, 0, static::RECENT_USERS_LIMIT);
408
409
        return $latestUserUids;
410
    }
411
412
    /**
413
     * @return BackendUserAuthentication
414
     */
415
    protected function getBackendUserAuthentication(): BackendUserAuthentication
416
    {
417
        return $GLOBALS['BE_USER'];
418
    }
419
420
    /**
421
     * @return ServerRequestInterface
422
     */
423
    protected function getRequest(): ServerRequestInterface
424
    {
425
        return $GLOBALS['TYPO3_REQUEST'];
426
    }
427
428
    /**
429
     * Create an array with the uids of online users as the keys
430
     * [
431
     *   1 => true,
432
     *   5 => true
433
     * ]
434
     * @return array
435
     */
436
    protected function getOnlineBackendUsers(): array
437
    {
438
        $onlineUsers = $this->backendUserSessionRepository->findAllActive();
439
        $onlineBackendUsers = [];
440
        foreach ($onlineUsers as $onlineUser) {
441
            $onlineBackendUsers[$onlineUser['ses_userid']] = true;
442
        }
443
        return $onlineBackendUsers;
444
    }
445
}
446