Skip to content

Commit b235e07

Browse files
authored
fix(files): Fix lock release handling when the file upload does not validate (#1118)
1 parent 582704b commit b235e07

2 files changed

Lines changed: 79 additions & 36 deletions

File tree

src/GraphQL/Utility/FileUpload.php

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -223,46 +223,51 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
223223
return $response;
224224
}
225225

226-
// Begin building file entity.
227-
/** @var \Drupal\file\FileInterface $file */
228-
$file = $this->fileStorage->create([]);
229-
$file->setOwnerId($this->currentUser->id());
230-
$file->setFilename($prepared_filename);
231-
$file->setMimeType($this->mimeTypeGuesser->guess($prepared_filename));
232-
$file->setFileUri($file_uri);
233-
// Set the size. This is done in File::preSave() but we validate the file
234-
// before it is saved.
235-
$file->setSize(@filesize($temp_file_path));
236-
237-
// Validate the file entity against entity-level validation and field-level
238-
// validators.
239-
if (!$this->validate($file, $validators, $response)) {
240-
return $response;
241-
}
242-
243-
// Move the file to the correct location after validation. Use
244-
// FileSystemInterface::EXISTS_ERROR as the file location has already been
245-
// determined above in FileSystem::getDestinationFilename().
246226
try {
247-
$this->fileSystem->move($temp_file_path, $file_uri, FileSystemInterface::EXISTS_ERROR);
248-
}
249-
catch (FileException $e) {
250-
$response->addViolation($this->t('Unknown error while uploading the file "@file".', [
251-
'@file' => $uploaded_file->getClientOriginalName(),
252-
]));
253-
$this->logger->error('Unable to move file from "@file" to "@destination".', [
254-
'@file' => $uploaded_file->getRealPath(),
255-
'@destination' => $file->getFileUri(),
256-
]);
257-
return $response;
258-
}
227+
// Begin building file entity.
228+
/** @var \Drupal\file\FileInterface $file */
229+
$file = $this->fileStorage->create([]);
230+
$file->setOwnerId($this->currentUser->id());
231+
$file->setFilename($prepared_filename);
232+
$file->setMimeType($this->mimeTypeGuesser->guess($prepared_filename));
233+
$file->setFileUri($file_uri);
234+
// Set the size. This is done in File::preSave() but we validate the file
235+
// before it is saved.
236+
$file->setSize(@filesize($temp_file_path));
237+
238+
// Validate the file entity against entity-level validation and
239+
// field-level validators.
240+
if (!$this->validate($file, $validators, $response)) {
241+
return $response;
242+
}
259243

260-
$file->save();
244+
// Move the file to the correct location after validation. Use
245+
// FileSystemInterface::EXISTS_ERROR as the file location has already been
246+
// determined above in FileSystem::getDestinationFilename().
247+
try {
248+
$this->fileSystem->move($temp_file_path, $file_uri, FileSystemInterface::EXISTS_ERROR);
249+
}
250+
catch (FileException $e) {
251+
$response->addViolation($this->t('Unknown error while uploading the file "@file".', [
252+
'@file' => $uploaded_file->getClientOriginalName(),
253+
]));
254+
$this->logger->error('Unable to move file from "@file" to "@destination".', [
255+
'@file' => $uploaded_file->getRealPath(),
256+
'@destination' => $file->getFileUri(),
257+
]);
258+
return $response;
259+
}
261260

262-
$this->lock->release($lock_id);
261+
$file->save();
263262

264-
$response->setFileEntity($file);
265-
return $response;
263+
$response->setFileEntity($file);
264+
return $response;
265+
}
266+
finally {
267+
// This will always be executed before any return statement or exception
268+
// in the try {} block.
269+
$this->lock->release($lock_id);
270+
}
266271
}
267272

268273
/**

tests/src/Kernel/Framework/UploadFileServiceTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace Drupal\Tests\graphql\Kernel\Framework;
44

5+
use Drupal\Core\Lock\LockBackendInterface;
6+
use Drupal\graphql\GraphQL\Utility\FileUpload;
57
use Drupal\Tests\graphql\Kernel\GraphQLTestBase;
8+
use Prophecy\Argument;
69
use Symfony\Component\HttpFoundation\File\UploadedFile;
710

811
/**
@@ -175,6 +178,41 @@ public function testExtensionValidation() {
175178
);
176179
}
177180

181+
/**
182+
* Tests that the file lock is released on validation errors.
183+
*/
184+
public function testLockReleased() {
185+
// Mock the lock system to check that the lock is released.
186+
$lock = $this->prophesize(LockBackendInterface::class);
187+
$lock->acquire(Argument::any())->willReturn(TRUE);
188+
// This is our only assertion - that the lock release method is called.
189+
$lock->release(Argument::any())->shouldBeCalled();
190+
191+
$upload_service = new FileUpload(
192+
\Drupal::service('entity_type.manager'),
193+
\Drupal::service('current_user'),
194+
\Drupal::service('file.mime_type.guesser'),
195+
\Drupal::service('file_system'),
196+
\Drupal::service('logger.channel.graphql'),
197+
\Drupal::service('token'),
198+
$lock->reveal(),
199+
\Drupal::service('config.factory')
200+
);
201+
202+
// Create a file with 4 bytes.
203+
file_put_contents($this->file, 'test');
204+
205+
// Create a Symfony dummy uploaded file in test mode.
206+
$uploadFile = $this->getUploadedFile(UPLOAD_ERR_OK, 4);
207+
208+
$upload_service->saveFileUpload($uploadFile, [
209+
'uri_scheme' => 'public',
210+
'file_directory' => 'test',
211+
// Only allow 1 byte.
212+
'max_filesize' => 1,
213+
]);
214+
}
215+
178216
/**
179217
* Helper method to prepare the UploadedFile depending on core version.
180218
*

0 commit comments

Comments
 (0)