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