‪TYPO3CMS  11.5
FilePersistenceSlot.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 
30 
37 {
38  public const ‪COMMAND_FILE_ADD = 'fileAdd';
39  public const ‪COMMAND_FILE_CREATE = 'fileCreate';
40  public const ‪COMMAND_FILE_MOVE = 'fileMove';
41  public const ‪COMMAND_FILE_RENAME = 'fileRename';
42  public const ‪COMMAND_FILE_REPLACE = 'fileReplace';
43  public const ‪COMMAND_FILE_SET_CONTENTS = 'fileSetContents';
44 
48  protected ‪$definedInvocations = [];
49 
53  protected ‪$allowedInvocations = [];
54 
59  public function ‪getContentSignature(string $content): string
60  {
61  return GeneralUtility::hmac($content);
62  }
63 
74  public function ‪defineInvocation(string $command, bool $type = null)
75  {
76  $this->definedInvocations[$command] = $type;
77  if ($type === null) {
78  unset($this->definedInvocations[$command]);
79  }
80  }
81 
93  public function ‪allowInvocation(
94  string $command,
95  string $combinedFileIdentifier,
96  string $contentSignature = null
97  ): bool {
98  $index = $this->‪searchAllowedInvocation(
99  $command,
100  $combinedFileIdentifier,
101  $contentSignature
102  );
103 
104  if ($index !== null) {
105  return false;
106  }
107 
108  $this->allowedInvocations[] = [
109  'command' => $command,
110  'combinedFileIdentifier' => $combinedFileIdentifier,
111  'contentSignature' => $contentSignature,
112  ];
113 
114  return true;
115  }
116 
117  public function ‪onPreFileCreate(‪BeforeFileCreatedEvent $event): void
118  {
119  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
120  $event->‪getFolder(),
121  $event->‪getFileName()
122  );
123 
124  $this->‪assertFileName(
125  self::COMMAND_FILE_CREATE,
126  $combinedFileIdentifier
127  );
128  }
129 
130  public function ‪onPreFileAdd(‪BeforeFileAddedEvent $event): void
131  {
132  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
133  $event->‪getTargetFolder(),
134  $event->‪getFileName()
135  );
136  // while assertFileName below also checks if it's a form definition
137  // we want an early return here to get rid of the file_get_contents
138  // below which would be triggered on every file add command otherwise
139  if (!$this->‪isFormDefinition($combinedFileIdentifier)) {
140  return;
141  }
142  $this->‪assertFileName(
143  self::COMMAND_FILE_ADD,
144  $combinedFileIdentifier,
145  (string)file_get_contents($event->‪getSourceFilePath())
146  );
147  }
148 
149  public function ‪onPreFileRename(‪BeforeFileRenamedEvent $event): void
150  {
151  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
152  $event->‪getFile()->getParentFolder(),
153  $event->‪getTargetFileName() ?? ''
154  );
155 
156  $this->‪assertFileName(
157  self::COMMAND_FILE_RENAME,
158  $combinedFileIdentifier
159  );
160  }
161 
162  public function ‪onPreFileReplace(‪BeforeFileReplacedEvent $event): void
163  {
164  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
165  $event->‪getFile()->getParentFolder(),
166  $event->‪getFile()->getName()
167  );
168 
169  $this->‪assertFileName(
170  self::COMMAND_FILE_REPLACE,
171  $combinedFileIdentifier
172  );
173  }
174 
175  public function ‪onPreFileMove(‪BeforeFileMovedEvent $event): void
176  {
177  // Skip check, in case file extension would not change during this
178  // command. In case e.g. "file.txt" shall be renamed to "file.form.yaml"
179  // the invocation still has to be granted.
180  // Any file moved to a recycle folder is accepted as well.
181  if ($this->‪isFormDefinition($event->‪getFile()->getIdentifier())
182  && $this->isFormDefinition($event->‪getTargetFileName())
183  || $this->isRecycleFolder($event->‪getFolder())) {
184  return;
185  }
186 
187  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
188  $event->‪getFolder(),
189  $event->‪getTargetFileName()
190  );
191 
192  $this->‪assertFileName(
193  self::COMMAND_FILE_MOVE,
194  $combinedFileIdentifier
195  );
196  }
197 
198  public function ‪onPreFileSetContents(‪BeforeFileContentsSetEvent $event): void
199  {
200  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
201  $event->‪getFile()->getParentFolder(),
202  $event->‪getFile()->getName()
203  );
204 
205  $this->‪assertFileName(
206  self::COMMAND_FILE_SET_CONTENTS,
207  $combinedFileIdentifier,
208  $event->‪getContent()
209  );
210  }
211 
218  protected function ‪assertFileName(
219  string $command,
220  string $combinedFileIdentifier,
221  string $content = null
222  ): void {
223  if (!$this->‪isFormDefinition($combinedFileIdentifier)) {
224  return;
225  }
226 
227  $definedInvocation = $this->definedInvocations[$command] ?? null;
228  // whitelisted command
229  if ($definedInvocation === true) {
230  return;
231  }
232  // blacklisted command
233  if ($definedInvocation === false) {
234  throw new FormDefinitionPersistenceException(
235  sprintf(
236  'Persisting form definition "%s" is denied',
237  $combinedFileIdentifier
238  ),
239  1530281201
240  );
241  }
242 
243  $contentSignature = null;
244  if ($content !== null) {
245  $contentSignature = $this->‪getContentSignature((string)$content);
246  }
247  $allowedInvocationIndex = $this->‪searchAllowedInvocation(
248  $command,
249  $combinedFileIdentifier,
250  $contentSignature
251  );
252 
253  if ($allowedInvocationIndex === null) {
254  throw new FormDefinitionPersistenceException(
255  sprintf(
256  'Persisting form definition "%s" is denied',
257  $combinedFileIdentifier
258  ),
259  1530281202
260  );
261  }
262  unset($this->allowedInvocations[$allowedInvocationIndex]);
263  }
264 
271  protected function ‪searchAllowedInvocation(
272  string $command,
273  string $combinedFileIdentifier,
274  string $contentSignature = null
275  ): ?int {
276  foreach ($this->allowedInvocations as $index => $allowedInvocation) {
277  if (
278  $command === $allowedInvocation['command']
279  && $combinedFileIdentifier === $allowedInvocation['combinedFileIdentifier']
280  && $contentSignature === $allowedInvocation['contentSignature']
281  ) {
282  return $index;
283  }
284  }
285  return null;
286  }
287 
293  protected function ‪buildCombinedIdentifier(FolderInterface $folder, string $fileName): string
294  {
295  return sprintf(
296  '%d:%s%s',
297  $folder->getStorage()->getUid(),
298  $folder->getIdentifier(),
299  $fileName
300  );
301  }
302 
307  protected function ‪isFormDefinition(string $identifier): bool
308  {
309  return str_ends_with(
310  $identifier,
312  );
313  }
314 
319  protected function ‪isRecycleFolder(FolderInterface $folder): bool
320  {
321  $role = $folder->getStorage()->getRole($folder);
322  return $role === ‪FolderInterface::ROLE_RECYCLER;
323  }
324 }
‪TYPO3\CMS\Core\Resource\Event\BeforeFileCreatedEvent
Definition: BeforeFileCreatedEvent.php:29
‪TYPO3\CMS\Core\Resource\ResourceStorage\getUid
‪int getUid()
Definition: ResourceStorage.php:330
‪TYPO3\CMS\Core\Resource\Event\BeforeFileReplacedEvent
Definition: BeforeFileReplacedEvent.php:27
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileMove
‪onPreFileMove(BeforeFileMovedEvent $event)
Definition: FilePersistenceSlot.php:173
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_SET_CONTENTS
‪const COMMAND_FILE_SET_CONTENTS
Definition: FilePersistenceSlot.php:43
‪TYPO3\CMS\Core\Resource\Event\BeforeFileRenamedEvent
Definition: BeforeFileRenamedEvent.php:27
‪TYPO3\CMS\Core\Resource\Event\BeforeFileMovedEvent\getTargetFileName
‪getTargetFileName()
Definition: BeforeFileMovedEvent.php:58
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileSetContents
‪onPreFileSetContents(BeforeFileContentsSetEvent $event)
Definition: FilePersistenceSlot.php:196
‪TYPO3\CMS\Core\Resource\Event\BeforeFileMovedEvent
Definition: BeforeFileMovedEvent.php:28
‪TYPO3\CMS\Core\Resource\ResourceInterface\getIdentifier
‪string getIdentifier()
‪TYPO3\CMS\Core\Resource\ResourceInterface\getStorage
‪ResourceStorage getStorage()
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileAdd
‪onPreFileAdd(BeforeFileAddedEvent $event)
Definition: FilePersistenceSlot.php:128
‪TYPO3\CMS\Core\Resource\Event\BeforeFileCreatedEvent\getFolder
‪getFolder()
Definition: BeforeFileCreatedEvent.php:49
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_MOVE
‪const COMMAND_FILE_MOVE
Definition: FilePersistenceSlot.php:40
‪TYPO3\CMS\Core\Resource\Event\BeforeFileRenamedEvent\getFile
‪getFile()
Definition: BeforeFileRenamedEvent.php:42
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_CREATE
‪const COMMAND_FILE_CREATE
Definition: FilePersistenceSlot.php:39
‪TYPO3\CMS\Core\Resource\Event\BeforeFileCreatedEvent\getFileName
‪getFileName()
Definition: BeforeFileCreatedEvent.php:44
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_ADD
‪const COMMAND_FILE_ADD
Definition: FilePersistenceSlot.php:38
‪TYPO3\CMS\Form\Slot
Definition: FilePersistenceSlot.php:18
‪TYPO3\CMS\Core\Resource\ResourceStorage\getRole
‪string getRole(FolderInterface $folder)
Definition: ResourceStorage.php:2746
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager
Definition: FormPersistenceManager.php:54
‪TYPO3\CMS\Core\Resource\Event\BeforeFileContentsSetEvent\getContent
‪getContent()
Definition: BeforeFileContentsSetEvent.php:48
‪TYPO3\CMS\Core\Resource\Event\BeforeFileAddedEvent\getSourceFilePath
‪getSourceFilePath()
Definition: BeforeFileAddedEvent.php:70
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\assertFileName
‪assertFileName(string $command, string $combinedFileIdentifier, string $content=null)
Definition: FilePersistenceSlot.php:216
‪TYPO3\CMS\Core\Resource\Event\BeforeFileReplacedEvent\getFile
‪getFile()
Definition: BeforeFileReplacedEvent.php:42
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\isFormDefinition
‪bool isFormDefinition(string $identifier)
Definition: FilePersistenceSlot.php:305
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\defineInvocation
‪defineInvocation(string $command, bool $type=null)
Definition: FilePersistenceSlot.php:72
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\$definedInvocations
‪array $definedInvocations
Definition: FilePersistenceSlot.php:47
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileRename
‪onPreFileRename(BeforeFileRenamedEvent $event)
Definition: FilePersistenceSlot.php:147
‪TYPO3\CMS\Core\Resource\Event\BeforeFileAddedEvent\getFileName
‪getFileName()
Definition: BeforeFileAddedEvent.php:60
‪TYPO3\CMS\Core\Resource\Event\BeforeFileRenamedEvent\getTargetFileName
‪getTargetFileName()
Definition: BeforeFileRenamedEvent.php:47
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot
Definition: FilePersistenceSlot.php:37
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\buildCombinedIdentifier
‪string buildCombinedIdentifier(FolderInterface $folder, string $fileName)
Definition: FilePersistenceSlot.php:291
‪TYPO3\CMS\Core\Resource\Event\BeforeFileMovedEvent\getFolder
‪getFolder()
Definition: BeforeFileMovedEvent.php:53
‪TYPO3\CMS\Core\Resource\Event\BeforeFileAddedEvent
Definition: BeforeFileAddedEvent.php:30
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\searchAllowedInvocation
‪int null searchAllowedInvocation(string $command, string $combinedFileIdentifier, string $contentSignature=null)
Definition: FilePersistenceSlot.php:269
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileCreate
‪onPreFileCreate(BeforeFileCreatedEvent $event)
Definition: FilePersistenceSlot.php:115
‪TYPO3\CMS\Form\Slot\FormDefinitionPersistenceException
Definition: FormDefinitionPersistenceException.php:23
‪TYPO3\CMS\Core\Resource\Event\BeforeFileContentsSetEvent\getFile
‪getFile()
Definition: BeforeFileContentsSetEvent.php:43
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_REPLACE
‪const COMMAND_FILE_REPLACE
Definition: FilePersistenceSlot.php:42
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Resource\Event\BeforeFileMovedEvent\getFile
‪getFile()
Definition: BeforeFileMovedEvent.php:48
‪TYPO3\CMS\Core\Resource\Event\BeforeFileAddedEvent\getTargetFolder
‪getTargetFolder()
Definition: BeforeFileAddedEvent.php:75
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\allowInvocation
‪bool allowInvocation(string $command, string $combinedFileIdentifier, string $contentSignature=null)
Definition: FilePersistenceSlot.php:91
‪TYPO3\CMS\Core\Resource\FolderInterface\ROLE_RECYCLER
‪const ROLE_RECYCLER
Definition: FolderInterface.php:27
‪TYPO3\CMS\Core\Resource\FolderInterface
Definition: FolderInterface.php:22
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileReplace
‪onPreFileReplace(BeforeFileReplacedEvent $event)
Definition: FilePersistenceSlot.php:160
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_RENAME
‪const COMMAND_FILE_RENAME
Definition: FilePersistenceSlot.php:41
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\$allowedInvocations
‪array $allowedInvocations
Definition: FilePersistenceSlot.php:51
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\getContentSignature
‪string getContentSignature(string $content)
Definition: FilePersistenceSlot.php:57
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\isRecycleFolder
‪bool isRecycleFolder(FolderInterface $folder)
Definition: FilePersistenceSlot.php:317
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\FORM_DEFINITION_FILE_EXTENSION
‪const FORM_DEFINITION_FILE_EXTENSION
Definition: FormPersistenceManager.php:55
‪TYPO3\CMS\Core\Resource\Event\BeforeFileContentsSetEvent
Definition: BeforeFileContentsSetEvent.php:28