‪TYPO3CMS  9.5
FilePersistenceSlot.php
Go to the documentation of this file.
1 <?php
2 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 
24 
29 {
30  const ‪COMMAND_FILE_ADD = 'fileAdd';
31  const ‪COMMAND_FILE_CREATE = 'fileCreate';
32  const ‪COMMAND_FILE_MOVE = 'fileMove';
33  const ‪COMMAND_FILE_RENAME = 'fileRename';
34  const ‪COMMAND_FILE_REPLACE = 'fileReplace';
35  const ‪COMMAND_FILE_SET_CONTENTS = 'fileSetContents';
36 
40  protected ‪$definedInvocations = [];
41 
45  protected ‪$allowedInvocations = [];
46 
51  public function ‪getContentSignature(string $content): string
52  {
53  return GeneralUtility::hmac($content);
54  }
55 
66  public function ‪defineInvocation(string $command, bool $type = null)
67  {
68  $this->definedInvocations[$command] = $type;
69  if ($type === null) {
70  unset($this->definedInvocations[$command]);
71  }
72  }
73 
85  public function ‪allowInvocation(
86  string $command,
87  string $combinedFileIdentifier,
88  string $contentSignature = null
89  ): bool {
90  $index = $this->‪searchAllowedInvocation(
91  $command,
92  $combinedFileIdentifier,
93  $contentSignature
94  );
95 
96  if ($index !== null) {
97  return false;
98  }
99 
100  $this->allowedInvocations[] = [
101  'command' => $command,
102  'combinedFileIdentifier' => $combinedFileIdentifier,
103  'contentSignature' => $contentSignature,
104  ];
105 
106  return true;
107  }
108 
113  public function ‪onPreFileCreate(string $fileName, FolderInterface $targetFolder): void
114  {
115  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
116  $targetFolder,
117  $fileName
118  );
119 
120  $this->‪assertFileName(
121  self::COMMAND_FILE_CREATE,
122  $combinedFileIdentifier
123  );
124  }
125 
131  public function ‪onPreFileAdd(
132  string $targetFileName,
133  FolderInterface $targetFolder,
134  string $sourceFilePath
135  ): void {
136  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
137  $targetFolder,
138  $targetFileName
139  );
140  // while assertFileName below also checks if it's a form definition
141  // we want an early return here to get rid of the file_get_contents
142  // below which would be triggered on every file add command otherwise
143  if (!$this->‪isFormDefinition($combinedFileIdentifier)) {
144  return;
145  }
146  $this->‪assertFileName(
147  self::COMMAND_FILE_ADD,
148  $combinedFileIdentifier,
149  file_get_contents($sourceFilePath)
150  );
151  }
152 
157  public function ‪onPreFileRename(FileInterface $file, string $targetFileName): void
158  {
159  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
160  $file->getParentFolder(),
161  $targetFileName
162  );
163 
164  $this->‪assertFileName(
165  self::COMMAND_FILE_RENAME,
166  $combinedFileIdentifier
167  );
168  }
169 
174  public function ‪onPreFileReplace(FileInterface $file, string $localFilePath): void
175  {
176  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
177  $file->getParentFolder(),
178  $file->getName()
179  );
180 
181  $this->‪assertFileName(
182  self::COMMAND_FILE_REPLACE,
183  $combinedFileIdentifier
184  );
185  }
186 
192  public function ‪onPreFileMove(FileInterface $file, FolderInterface $targetFolder, string $targetFileName): void
193  {
194  // Skip check, in case file extension would not change during this
195  // command. In case e.g. "file.txt" shall be renamed to "file.form.yaml"
196  // the invocation still has to be granted.
197  // Any file moved to a recycle folder is accepted as well.
198  if ($this->‪isFormDefinition($file->getIdentifier())
199  && $this->isFormDefinition($targetFileName)
200  || $this->isRecycleFolder($targetFolder)) {
201  return;
202  }
203 
204  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
205  $targetFolder,
206  $targetFileName
207  );
208 
209  $this->‪assertFileName(
210  self::COMMAND_FILE_MOVE,
211  $combinedFileIdentifier
212  );
213  }
214 
219  public function ‪onPreFileSetContents(FileInterface $file, $content = null): void
220  {
221  $combinedFileIdentifier = $this->‪buildCombinedIdentifier(
222  $file->getParentFolder(),
223  $file->getName()
224  );
225 
226  $this->‪assertFileName(
227  self::COMMAND_FILE_SET_CONTENTS,
228  $combinedFileIdentifier,
229  $content
230  );
231  }
232 
239  protected function ‪assertFileName(
240  string $command,
241  string $combinedFileIdentifier,
242  string $content = null
243  ): void {
244  if (!$this->‪isFormDefinition($combinedFileIdentifier)) {
245  return;
246  }
247 
248  $definedInvocation = $this->definedInvocations[$command] ?? null;
249  // whitelisted command
250  if ($definedInvocation === true) {
251  return;
252  }
253  // blacklisted command
254  if ($definedInvocation === false) {
255  throw new FormDefinitionPersistenceException(
256  sprintf(
257  'Persisting form definition "%s" is denied',
258  $combinedFileIdentifier
259  ),
260  1530281201
261  );
262  }
263 
264  $contentSignature = null;
265  if ($content !== null) {
266  $contentSignature = $this->‪getContentSignature((string)$content);
267  }
268  $allowedInvocationIndex = $this->‪searchAllowedInvocation(
269  $command,
270  $combinedFileIdentifier,
271  $contentSignature
272  );
273 
274  if ($allowedInvocationIndex === null) {
275  throw new FormDefinitionPersistenceException(
276  sprintf(
277  'Persisting form definition "%s" is denied',
278  $combinedFileIdentifier
279  ),
280  1530281202
281  );
282  }
283  unset($this->allowedInvocations[$allowedInvocationIndex]);
284  }
285 
292  protected function ‪searchAllowedInvocation(
293  string $command,
294  string $combinedFileIdentifier,
295  string $contentSignature = null
296  ): ?int {
297  foreach ($this->allowedInvocations as $index => $allowedInvocation) {
298  if (
299  $command === $allowedInvocation['command']
300  && $combinedFileIdentifier === $allowedInvocation['combinedFileIdentifier']
301  && $contentSignature === $allowedInvocation['contentSignature']
302  ) {
303  return $index;
304  }
305  }
306  return null;
307  }
308 
314  protected function ‪buildCombinedIdentifier(FolderInterface $folder, string $fileName): string
315  {
316  return sprintf(
317  '%d:%s%s',
318  $folder->getStorage()->getUid(),
319  $folder->getIdentifier(),
320  $fileName
321  );
322  }
323 
328  protected function ‪isFormDefinition(string $identifier): bool
329  {
331  $identifier,
333  );
334  }
335 
340  protected function ‪isRecycleFolder(FolderInterface $folder): bool
341  {
342  $role = $folder->getStorage()->getRole($folder);
343  return $role === ‪FolderInterface::ROLE_RECYCLER;
344  }
345 }
‪TYPO3\CMS\Core\Resource\ResourceStorage\getUid
‪int getUid()
Definition: ResourceStorage.php:271
‪TYPO3\CMS\Core\Utility\StringUtility\endsWith
‪static bool endsWith($haystack, $needle)
Definition: StringUtility.php:60
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileCreate
‪onPreFileCreate(string $fileName, FolderInterface $targetFolder)
Definition: FilePersistenceSlot.php:111
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:21
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_SET_CONTENTS
‪const COMMAND_FILE_SET_CONTENTS
Definition: FilePersistenceSlot.php:35
‪TYPO3\CMS\Core\Resource\ResourceInterface\getIdentifier
‪string getIdentifier()
‪TYPO3\CMS\Core\Resource\ResourceInterface\getStorage
‪ResourceStorage getStorage()
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_MOVE
‪const COMMAND_FILE_MOVE
Definition: FilePersistenceSlot.php:32
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_CREATE
‪const COMMAND_FILE_CREATE
Definition: FilePersistenceSlot.php:31
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_ADD
‪const COMMAND_FILE_ADD
Definition: FilePersistenceSlot.php:30
‪TYPO3\CMS\Form\Slot
Definition: FilePersistenceSlot.php:3
‪TYPO3\CMS\Core\Resource\ResourceStorage\getRole
‪string getRole(FolderInterface $folder)
Definition: ResourceStorage.php:2968
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager
Definition: FormPersistenceManager.php:48
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\assertFileName
‪assertFileName(string $command, string $combinedFileIdentifier, string $content=null)
Definition: FilePersistenceSlot.php:237
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileRename
‪onPreFileRename(FileInterface $file, string $targetFileName)
Definition: FilePersistenceSlot.php:155
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\isFormDefinition
‪bool isFormDefinition(string $identifier)
Definition: FilePersistenceSlot.php:326
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\defineInvocation
‪defineInvocation(string $command, bool $type=null)
Definition: FilePersistenceSlot.php:64
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\$definedInvocations
‪array $definedInvocations
Definition: FilePersistenceSlot.php:39
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileAdd
‪onPreFileAdd(string $targetFileName, FolderInterface $targetFolder, string $sourceFilePath)
Definition: FilePersistenceSlot.php:129
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot
Definition: FilePersistenceSlot.php:29
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\buildCombinedIdentifier
‪string buildCombinedIdentifier(FolderInterface $folder, string $fileName)
Definition: FilePersistenceSlot.php:312
‪TYPO3\CMS\Core\Resource\ResourceInterface\getName
‪string getName()
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\searchAllowedInvocation
‪int null searchAllowedInvocation(string $command, string $combinedFileIdentifier, string $contentSignature=null)
Definition: FilePersistenceSlot.php:290
‪TYPO3\CMS\Form\Slot\FormDefinitionPersistenceException
Definition: FormDefinitionPersistenceException.php:22
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileMove
‪onPreFileMove(FileInterface $file, FolderInterface $targetFolder, string $targetFileName)
Definition: FilePersistenceSlot.php:190
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_REPLACE
‪const COMMAND_FILE_REPLACE
Definition: FilePersistenceSlot.php:34
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\allowInvocation
‪bool allowInvocation(string $command, string $combinedFileIdentifier, string $contentSignature=null)
Definition: FilePersistenceSlot.php:83
‪TYPO3\CMS\Core\Resource\FolderInterface\ROLE_RECYCLER
‪const ROLE_RECYCLER
Definition: FolderInterface.php:26
‪TYPO3\CMS\Core\Resource\FolderInterface
Definition: FolderInterface.php:21
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\COMMAND_FILE_RENAME
‪const COMMAND_FILE_RENAME
Definition: FilePersistenceSlot.php:33
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\$allowedInvocations
‪array $allowedInvocations
Definition: FilePersistenceSlot.php:43
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\getContentSignature
‪string getContentSignature(string $content)
Definition: FilePersistenceSlot.php:49
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileReplace
‪onPreFileReplace(FileInterface $file, string $localFilePath)
Definition: FilePersistenceSlot.php:172
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:21
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\isRecycleFolder
‪bool isRecycleFolder(FolderInterface $folder)
Definition: FilePersistenceSlot.php:338
‪TYPO3\CMS\Core\Resource\ResourceInterface\getParentFolder
‪FolderInterface getParentFolder()
‪TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager\FORM_DEFINITION_FILE_EXTENSION
‪const FORM_DEFINITION_FILE_EXTENSION
Definition: FormPersistenceManager.php:49
‪TYPO3\CMS\Form\Slot\FilePersistenceSlot\onPreFileSetContents
‪onPreFileSetContents(FileInterface $file, $content=null)
Definition: FilePersistenceSlot.php:217