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