‪TYPO3CMS  ‪main
CspAjaxController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
23 use Symfony\Component\Uid\UuidV4;
42 
47 #[AsController]
49 {
50  public function ‪__construct(
51  protected readonly ‪ModelService $modelService,
52  protected readonly ‪PolicyProvider $policyProvider,
53  protected readonly ‪ReportRepository $reportRepository,
54  protected readonly ‪ResolutionRepository $resolutionRepository,
55  protected readonly EventDispatcherInterface $eventDispatcher,
56  protected readonly ‪HashService $hashService,
57  ) {}
58 
59  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
60  {
61  if (!$this->‪isSystemMaintainer()) {
62  return (new ‪NullResponse())->withStatus(403);
63  }
64  return $this->‪dispatchAction($request)
65  ?? (new ‪NullResponse())->withStatus(500);
66  }
67 
68  protected function ‪dispatchAction(ServerRequestInterface $request): ?ResponseInterface
69  {
70  $parsedBody = $request->getParsedBody();
71  $hmac = $parsedBody['hmac'] ?? null;
72  $action = $parsedBody['action'] ?? null;
73  $summaries = $parsedBody['summaries'] ?? [];
74  $scope = ‪Scope::tryFrom($parsedBody['scope'] ?? '');
75  $uuid = $parsedBody['uuid'] ?? null;
76  if ($uuid !== null) {
77  $uuid = Uuidv4::fromString($uuid);
78  }
79  if (!empty($parsedBody['suggestion'])) {
80  $suggestion = $this->modelService->buildMutationSuggestionFromArray($parsedBody['suggestion']);
81  }
82  // reports
83  if ($action === 'fetchReports') {
84  return $this->‪fetchReportsAction($scope);
85  }
86  if ($action === 'muteReport' && is_array($summaries)) {
87  return $this->‪muteReportAction(...$summaries);
88  }
89  if ($action === 'deleteReport' && is_array($summaries)) {
90  return $this->‪deleteReportAction(...$summaries);
91  }
92  if ($action === 'deleteReports') {
93  return $this->‪deleteReportsAction($scope);
94  }
95  if ($action === 'handleReport' && $uuid !== null) {
96  return $this->‪handleReportAction($uuid);
97  }
98  if ($action === 'mutateReport'
99  && $scope !== null
100  && is_array($summaries)
101  && isset($suggestion)
102  && hash_equals($suggestion->hmac(), $hmac)
103  ) {
104  return $this->‪mutateReportAction($scope, $suggestion, ...$summaries);
105  }
106  return null;
107  }
108 
109  protected function ‪fetchReportsAction(?‪Scope $scope): ResponseInterface
110  {
111  $demand = ‪ReportDemand::create();
112  $demand->scope = $scope;
113  $reports = $this->reportRepository->findAllSummarized($demand);
114  // @todo not sure whether this is a good idea performance-wise
115  $reports = array_map(
116  function (‪SummarizedReport $report): ‪SummarizedReport {
117  $event = $this->‪dispatchInvestigateMutationsEvent($report);
118  if ($event->getMutationSuggestions() !== []) {
119  $mutationHashes = array_map(
120  static fn(‪MutationSuggestion $suggestion): string => $suggestion->‪hash(),
121  $event->getMutationSuggestions()
122  );
123  $report = $report->‪withMutationHashes(...$mutationHashes)
124  ->withAttribute(ReportAttribute::fixable);
125  }
126  return $report;
127  },
128  $reports
129  );
130  return new ‪JsonResponse($reports);
131  }
132 
133  protected function ‪muteReportAction(string ...$summaries): ResponseInterface
134  {
135  $reports = $this->reportRepository->findBySummary(...$summaries);
136  $uuids = array_map(static fn(‪Report $report): UuidV4 => $report->uuid, $reports);
137  $this->reportRepository->updateStatus(ReportStatus::Muted, ...$uuids);
138  return new ‪JsonResponse(['uuids' => $uuids]);
139  }
140 
141  protected function ‪deleteReportAction(string ...$summaries): ResponseInterface
142  {
143  $reports = $this->reportRepository->findBySummary(...$summaries);
144  $reportUuids = $this->‪resolveReportUuids(...$reports);
145  $this->reportRepository->updateStatus(‪ReportStatus::Deleted, ...$reportUuids);
146  return new ‪JsonResponse(['uuids' => $reportUuids]);
147  }
148 
149  protected function ‪deleteReportsAction(?‪Scope $scope): ResponseInterface
150  {
151  $amount = $this->reportRepository->removeAll($scope);
152  return new ‪JsonResponse(['amount' => $amount]);
153  }
154 
155  protected function ‪handleReportAction(UuidV4 $uuid): ResponseInterface
156  {
157  $report = $this->reportRepository->findByUuid($uuid);
158  if ($report === null) {
159  return new ‪JsonResponse();
160  }
161  $event = $this->‪dispatchInvestigateMutationsEvent($report);
162  $suggestions = $event->getMutationSuggestions();
163  // reverse sort by priority (higher priorities take precedence)
164  usort($suggestions, static fn(‪MutationSuggestion $a, ‪MutationSuggestion $b) => $b->priority <=> $a->priority);
165  return new ‪JsonResponse(array_values($suggestions));
166  }
167 
168  protected function ‪mutateReportAction(‪Scope $scope, ‪MutationSuggestion $suggestion, string ...$initiators): ResponseInterface
169  {
170  $summary = $this->‪generateResolutionSummary($scope, $suggestion);
171  $resolution = $this->resolutionRepository->findBySummary($summary);
172  $reports = $this->reportRepository->findBySummary(...$initiators);
173  if ($resolution !== null || $reports === []) {
174  return new ‪JsonResponse();
175  }
176  $resolution = new ‪Resolution($summary, $scope, $suggestion->identifier, $suggestion->collection, ['initiators' => $initiators]);
177  $this->resolutionRepository->add($resolution);
178  $reportUuids = $this->‪resolveReportUuids(...$reports);
179  $this->reportRepository->updateStatus(ReportStatus::Handled, ...$reportUuids);
180  return new ‪JsonResponse(['initiators' => $initiators, 'uuids' => $reportUuids]);
181  }
182 
184  {
185  $policy = $this->policyProvider->provideFor($report->scope);
186  $event = new ‪InvestigateMutationsEvent($policy, $report);
187  $this->eventDispatcher->dispatch($event);
188  return $event;
189  }
190 
191  protected function ‪generateResolutionSummary(‪Scope $scope, ‪MutationSuggestion $suggestion): string
192  {
193  return $this->hashService->hmac(
194  json_encode([
195  $scope,
196  $suggestion->identifier,
197  $suggestion->collection,
198  ]),
199  self::class,
200  );
201  }
202 
203  protected function ‪resolveReportUuids(‪Report ...$reports): array
204  {
205  return array_map(static fn(‪Report $report): UuidV4 => $report->uuid, $reports);
206  }
207 
208  protected function ‪isSystemMaintainer(): bool
209  {
210  $backendUser = ‪$GLOBALS['BE_USER'] ?? null;
211  return $backendUser instanceof ‪BackendUserAuthentication && $backendUser->‪isSystemMaintainer();
212  }
213 }
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationSuggestion\hash
‪hash()
Definition: MutationSuggestion.php:49
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\mutateReportAction
‪mutateReportAction(Scope $scope, MutationSuggestion $suggestion, string ... $initiators)
Definition: CspAjaxController.php:168
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\dispatchInvestigateMutationsEvent
‪dispatchInvestigateMutationsEvent(Report $report)
Definition: CspAjaxController.php:183
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportRepository
Definition: ReportRepository.php:31
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportDemand\create
‪static create()
Definition: ReportDemand.php:37
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\resolveReportUuids
‪resolveReportUuids(Report ... $reports)
Definition: CspAjaxController.php:203
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\ModelService
Definition: ModelService.php:28
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\Deleted
‪@ Deleted
Definition: ReportStatus.php:28
‪TYPO3\CMS\Core\Http\NullResponse
Definition: NullResponse.php:26
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportAttribute
‪ReportAttribute
Definition: ReportAttribute.php:24
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportDemand
Definition: ReportDemand.php:28
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\deleteReportAction
‪deleteReportAction(string ... $summaries)
Definition: CspAjaxController.php:141
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy
Definition: CspAjaxController.php:18
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication\isSystemMaintainer
‪isSystemMaintainer(bool $pure=false)
Definition: BackendUserAuthentication.php:358
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportStatus
‪ReportStatus
Definition: ReportStatus.php:24
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\generateResolutionSummary
‪generateResolutionSummary(Scope $scope, MutationSuggestion $suggestion)
Definition: CspAjaxController.php:191
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\InvestigateMutationsEvent
Definition: InvestigateMutationsEvent.php:29
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\SummarizedReport\withMutationHashes
‪withMutationHashes(string ... $mutationHashes)
Definition: SummarizedReport.php:53
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\muteReportAction
‪muteReportAction(string ... $summaries)
Definition: CspAjaxController.php:133
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: CspAjaxController.php:59
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController
Definition: CspAjaxController.php:49
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Scope\tryFrom
‪static tryFrom(string $value)
Definition: Scope.php:88
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Scope
Definition: Scope.php:30
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\handleReportAction
‪handleReportAction(UuidV4 $uuid)
Definition: CspAjaxController.php:155
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationSuggestion
Definition: MutationSuggestion.php:29
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\deleteReportsAction
‪deleteReportsAction(?Scope $scope)
Definition: CspAjaxController.php:149
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\Resolution
Definition: Resolution.php:29
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\dispatchAction
‪dispatchAction(ServerRequestInterface $request)
Definition: CspAjaxController.php:68
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\Report
Definition: Report.php:27
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:28
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\SummarizedReport
Definition: SummarizedReport.php:24
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ResolutionRepository
Definition: ResolutionRepository.php:29
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\fetchReportsAction
‪fetchReportsAction(?Scope $scope)
Definition: CspAjaxController.php:109
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\__construct
‪__construct(protected readonly ModelService $modelService, protected readonly PolicyProvider $policyProvider, protected readonly ReportRepository $reportRepository, protected readonly ResolutionRepository $resolutionRepository, protected readonly EventDispatcherInterface $eventDispatcher, protected readonly HashService $hashService,)
Definition: CspAjaxController.php:50
‪TYPO3\CMS\Backend\Security\ContentSecurityPolicy\CspAjaxController\isSystemMaintainer
‪isSystemMaintainer()
Definition: CspAjaxController.php:208
‪TYPO3\CMS\Core\Crypto\HashService
Definition: HashService.php:27
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\PolicyProvider
Definition: PolicyProvider.php:38