Passed
Push — master ( d4fa3b...7b82e8 )
by
unknown
13:26
created

BackendUserController::getSessionBackend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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