Passed
Push — issue/643 ( 7acfa0 )
by Tomas Norre
07:34
created

FrontendUserAuthenticator   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Test Coverage

Coverage 4.08%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 36
c 3
b 0
f 0
dl 0
loc 94
ccs 2
cts 49
cp 0.0408
rs 10
wmc 9

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A isRequestHashMatchingQueueRecord() 0 3 2
A getFrontendUser() 0 12 2
A process() 0 43 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Middleware;
6
7
/*
8
 * (c) 2020 AOE GmbH <[email protected]>
9
 *
10
 * This file is part of the TYPO3 Crawler Extension.
11
 *
12
 * It is free software; you can redistribute it and/or modify it under
13
 * the terms of the GNU General Public License, either version 2
14
 * of the License, or any later version.
15
 *
16
 * For the full copyright and license information, please read the
17
 * LICENSE.txt file that was distributed with this source code.
18
 *
19
 * The TYPO3 project - inspiring people to share!
20
 */
21
22
use AOE\Crawler\Converter\JsonCompatibilityConverter;
23
use AOE\Crawler\Domain\Repository\QueueRepository;
24
use Psr\Http\Message\ResponseInterface;
25
use Psr\Http\Message\ServerRequestInterface;
26
use Psr\Http\Server\MiddlewareInterface;
27
use Psr\Http\Server\RequestHandlerInterface;
28
use TYPO3\CMS\Core\Context\Context;
29
use TYPO3\CMS\Core\Context\UserAspect;
30
use TYPO3\CMS\Core\Utility\DebugUtility;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
33
use TYPO3\CMS\Extbase\Object\ObjectManager;
34
use TYPO3\CMS\Frontend\Controller\ErrorController;
35
36
/**
37
 * Evaluates HTTP headers and checks if Crawler should register itself.
38
 */
39
class FrontendUserAuthenticator implements MiddlewareInterface
40
{
41
    /**
42
     * @var string
43
     */
44
    protected $headerName = 'X-T3CRAWLER';
45
46
    /**
47
     * @var Context
48
     */
49
    protected $context;
50
51
    /**
52
     * @var QueueRepository
53
     */
54
    protected $queueRepository;
55
56
    public function __construct(?Context $context = null)
57
    {
58
        $this->context = $context ?? GeneralUtility::makeInstance(Context::class);
59
        $this->queueRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(QueueRepository::class);
60
    }
61
62
    /**
63
     * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
64
     * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
65
     */
66
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
67
    {
68
69
        /** @var JsonCompatibilityConverter $jsonCompatibilityConverter */
70
        $jsonCompatibilityConverter = GeneralUtility::makeInstance(JsonCompatibilityConverter::class);
71
72
        $crawlerInformation = $request->getHeaderLine($this->headerName) ?? null;
73
        if (empty($crawlerInformation)) {
74
            return $handler->handle($request);
75
        }
76
77
        // Authenticate crawler request:
78
        //@todo: ask service to exclude current call for special reasons: for example no relevance because the language version is not affected
79
        [$queueId, $hash] = explode(':', $crawlerInformation);
80
        $queueRec = $this->queueRepository->findByQueueId($queueId);
81
82
        // If a crawler record was found and hash was matching, set it up
83
        if (! $this->isRequestHashMatchingQueueRecord($queueRec, $hash)) {
84
            return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, 'No crawler entry found');
85
        }
86
87
        $queueParameters = $jsonCompatibilityConverter->convert($queueRec['parameters']);
88
        $request = $request->withAttribute('tx_crawler', $queueParameters);
89
90
        // Now ensure to set the proper user groups
91
        $grList = $queueParameters['feUserGroupList'];
92
        if ($grList) {
93
            $frontendUser = $this->getFrontendUser($grList, $request);
94
95
            // we have to set the fe user group to the user aspect since indexed_search only reads the user aspect
96
            // to get the groups. otherwise groups are ignored during indexing.
97
            // we need to add the groups 0, and -2 too, like the getGroupIds getter does.
98
            $this->context->setAspect(
99
                'frontend.user',
100
                GeneralUtility::makeInstance(
101
                    UserAspect::class,
102
                    $frontendUser,
103
                    explode(',', '0,-2,' . $grList)
104
                )
105
            );
106
        }
107
108
        return $handler->handle($request);
109
    }
110
111 3
    protected function isRequestHashMatchingQueueRecord(?array $queueRec, string $hash): bool
112
    {
113 3
        return is_array($queueRec) && hash_equals($hash, md5($queueRec['qid'] . '|' . $queueRec['set_id'] . '|' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']));
114
    }
115
116
    /**
117
     * @param $grList
118
     * @param ServerRequestInterface $request
119
     * @return mixed|string|\TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
120
     */
121
    private function getFrontendUser($grList, ServerRequestInterface $request)
122
    {
123
        $currentTYPO3VersionAsInteger = VersionNumberUtility::convertVersionNumberToInteger(VersionNumberUtility::getNumericTypo3Version());
124
        if ($currentTYPO3VersionAsInteger < 10000000) {
125
            $frontendUser = $GLOBALS['TSFE']->fe_user;
126
            $frontendUser->user[$frontendUser->usergroup_column] = $grList;
127
        } else {
128
            $frontendUser = $request->getAttribute('frontend.user');
129
            $frontendUser->user[$frontendUser->usergroup_column] = '0,-2,' . $grList;
130
            $frontendUser->user['uid'] = PHP_INT_MAX;
131
        }
132
        return $frontendUser;
133
    }
134
}
135