Protect uploads if folder for EditableFileField was removed

This commit is contained in:
Sabina Talipova 2022-05-13 16:25:51 +12:00
parent 0f12e1ecfc
commit 92aded3016
6 changed files with 156 additions and 15 deletions

View File

@ -28,6 +28,7 @@ use SilverStripe\UserForms\Model\EditableFormField\EditableFileField;
use SilverStripe\UserForms\Model\Submission\SubmittedForm; use SilverStripe\UserForms\Model\Submission\SubmittedForm;
use SilverStripe\UserForms\Model\Submission\SubmittedFileField; use SilverStripe\UserForms\Model\Submission\SubmittedFileField;
use SilverStripe\UserForms\Model\UserDefinedForm; use SilverStripe\UserForms\Model\UserDefinedForm;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\ArrayData; use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements; use SilverStripe\View\Requirements;
use SilverStripe\View\SSViewer; use SilverStripe\View\SSViewer;
@ -53,6 +54,8 @@ class UserDefinedFormController extends PageController
/** @var string The name of the folder where form submissions will be placed by default */ /** @var string The name of the folder where form submissions will be placed by default */
private static $form_submissions_folder = 'Form-submissions'; private static $form_submissions_folder = 'Form-submissions';
private static string $file_upload_stage = Versioned::DRAFT;
protected function init() protected function init()
{ {
parent::init(); parent::init();
@ -262,25 +265,39 @@ JS
if (!empty($data[$field->Name])) { if (!empty($data[$field->Name])) {
if (in_array(EditableFileField::class, $field->getClassAncestry() ?? [])) { if (in_array(EditableFileField::class, $field->getClassAncestry() ?? [])) {
if (!empty($_FILES[$field->Name]['name'])) { if (!empty($_FILES[$field->Name]['name'])) {
$foldername = $field->getFormField()->getFolderName(); if (!$field->getFolderExists()) {
$field->createProtectedFolder();
}
$file = Versioned::withVersionedMode(function () use ($field, $form) {
$stage = Injector::inst()->get(self::class)->config()->get('file_upload_stage');
Versioned::set_stage($stage);
// create the file from post data $foldername = $field->getFormField()->getFolderName();
$upload = Upload::create(); // create the file from post data
try { $upload = Upload::create();
$upload->loadIntoFile($_FILES[$field->Name], null, $foldername); try {
} catch (ValidationException $e) { $upload->loadIntoFile($_FILES[$field->Name], null, $foldername);
$validationResult = $e->getResult(); } catch (ValidationException $e) {
foreach ($validationResult->getMessages() as $message) { $validationResult = $e->getResult();
$form->sessionMessage($message['message'], ValidationResult::TYPE_ERROR); foreach ($validationResult->getMessages() as $message) {
$form->sessionMessage($message['message'], ValidationResult::TYPE_ERROR);
}
Controller::curr()->redirectBack();
return null;
} }
Controller::curr()->redirectBack(); /** @var AssetContainer|File $file */
$file = $upload->getFile();
$file->ShowInSearch = 0;
$file->UserFormUpload = UserFormFileExtension::USER_FORM_UPLOAD_TRUE;
$file->write();
return $file;
});
if (is_null($file)) {
return; return;
} }
/** @var AssetContainer|File $file */
$file = $upload->getFile();
$file->ShowInSearch = 0;
$file->UserFormUpload = UserFormFileExtension::USER_FORM_UPLOAD_TRUE;
$file->write();
// generate image thumbnail to show in asset-admin // generate image thumbnail to show in asset-admin
// you can run userforms without asset-admin, so need to ensure asset-admin is installed // you can run userforms without asset-admin, so need to ensure asset-admin is installed

View File

@ -194,7 +194,18 @@ class EditableFileField extends EditableFormField
return $result; return $result;
} }
public function getFolderExists(): bool
{
return $this->Folder()->ID !== 0;
}
public function createProtectedFolder(): void
{
$folderName = sprintf('page-%d/upload-field-%d', $this->ParentID, $this->ID);
$folder = UserDefinedFormAdmin::getFormSubmissionFolder($folderName);
$this->FolderID = $folder->ID;
$this->write();
}
public function getFormField() public function getFormField()
{ {

View File

@ -24,6 +24,7 @@ use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
use SilverStripe\UserForms\Model\Recipient\EmailRecipient; use SilverStripe\UserForms\Model\Recipient\EmailRecipient;
use SilverStripe\UserForms\Model\Submission\SubmittedFormField; use SilverStripe\UserForms\Model\Submission\SubmittedFormField;
use SilverStripe\UserForms\Model\UserDefinedForm; use SilverStripe\UserForms\Model\UserDefinedForm;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\ArrayData; use SilverStripe\View\ArrayData;
use SilverStripe\View\SSViewer; use SilverStripe\View\SSViewer;
use function filesize; use function filesize;
@ -467,4 +468,47 @@ class UserDefinedFormControllerTest extends FunctionalTest
$nodata = $this->findEmail('nodata@example.com', 'no-reply@example.com', 'Email Subject'); $nodata = $this->findEmail('nodata@example.com', 'no-reply@example.com', 'Email Subject');
$this->assertEmpty($nodata['AttachedFiles'], 'Recipients with HideFormData do not receive attachment'); $this->assertEmpty($nodata['AttachedFiles'], 'Recipients with HideFormData do not receive attachment');
} }
public function testMissingFolderCreated()
{
Config::modify()->set(Upload_Validator::class, 'use_is_uploaded_file', false);
$userForm = $this->setupFormFrontend('upload-form-without-folder');
$controller = UserDefinedFormController::create($userForm);
$field = $this->objFromFixture(EditableFileField::class, 'file-field-3');
$path = realpath(__DIR__ . '/fixtures/testfile.jpg');
$data = [
$field->Name => [
'name' => 'testfile.jpg',
'type' => 'image/jpeg',
'tmp_name' => $path,
'error' => 0,
'size' => filesize($path ?? ''),
]
];
$_FILES[$field->Name] = $data[$field->Name];
$controller->getRequest()->setSession(new Session([]));
$folderExistBefore = $field->getFolderExists();
$stageBefore = Versioned::get_stage();
$controller->process($data, $controller->Form());
$field = EditableFileField::get_by_id($field->ID);
$filter = [
'ParentID' => $field->Folder()->ID,
'Name' => 'testfile.jpg',
];
$fileDraftCount = Versioned::get_by_stage(File::class, Versioned::DRAFT)->filter($filter)->count();
$fileLiveCount = Versioned::get_by_stage(File::class, Versioned::LIVE)->filter($filter)->count();
$folderExistAfter = $field->getFolderExists();
$this->assertFalse($folderExistBefore);
$this->assertTrue($folderExistAfter);
$this->assertEquals($stageBefore, Versioned::get_stage());
$this->assertEquals(1, $fileDraftCount);
$this->assertEquals(0, $fileLiveCount);
}
} }

View File

@ -168,6 +168,51 @@ class EditableFormFieldTest extends FunctionalTest
$this->assertNotContains('jpg', $formField->getValidator()->getAllowedExtensions()); $this->assertNotContains('jpg', $formField->getValidator()->getAllowedExtensions());
} }
/**
* Test that if folder is not set or folder was removed,
* then getFormField() saves file in protected folder
*/
public function testCreateProtectedFolder()
{
$fileField1 = $this->objFromFixture(EditableFileField::class, 'file-field-without-folder');
$fileField2 = $this->objFromFixture(EditableFileField::class, 'file-field-with-folder');
$fileField1->createProtectedFolder();
$formField1 = $fileField1->getFormField();
$formField2 = $fileField2->getFormField();
$canViewParent1 = $fileField1->Folder()->Parent()->Parent()->CanViewType;
$canViewParent2 = $fileField2->Folder()->Parent()->CanViewType;
$this->assertEquals('OnlyTheseUsers', $canViewParent1);
$this->assertEquals('Inherit', $canViewParent2);
$this->assertTrue(
(bool) preg_match(
sprintf(
'/^Form-submissions\/page-%d\/upload-field-%d/',
$fileField1->ParentID,
$fileField1->ID
),
$formField1->folderName,
)
);
$this->assertEquals('folder1/folder1-1/', $formField2->folderName);
}
/**
* Verify that folder is related to a field exist
*/
public function testGetFolderExists()
{
$fileField1 = $this->objFromFixture(EditableFileField::class, 'file-field-without-folder');
$fileField2 = $this->objFromFixture(EditableFileField::class, 'file-field-with-folder');
$this->assertFalse($fileField1->getFolderExists());
$this->assertTrue($fileField2->getFolderExists());
}
/** /**
* Verify that unique names are automatically generated for each formfield * Verify that unique names are automatically generated for each formfield
*/ */

View File

@ -2,6 +2,13 @@ SilverStripe\Security\Group:
admin: admin:
Title: Administrators Title: Administrators
SilverStripe\Assets\Folder:
user-form-folder-parent:
Title: folder1
user-form-folder-child:
Title: folder1-1
Parent: =>SilverStripe\Assets\Folder.user-form-folder-parent
SilverStripe\UserForms\Model\EditableFormField\EditableTextField: SilverStripe\UserForms\Model\EditableFormField\EditableTextField:
basic-text: basic-text:
Name: basic_text_name Name: basic_text_name
@ -289,6 +296,14 @@ SilverStripe\UserForms\Model\EditableFormField\EditableFileField:
file-field: file-field:
Name: file-uploader Name: file-uploader
Title: Set file Title: Set file
file-field-without-folder:
Name: file-uploader-without-folder
Title: Set file
FolderID: 0
file-field-with-folder:
Name: file-uploader-with-folder
Title: Set file
FolderID: =>SilverStripe\Assets\Folder.user-form-folder-child
SilverStripe\UserForms\Model\UserDefinedForm: SilverStripe\UserForms\Model\UserDefinedForm:
basic-form-page: basic-form-page:

View File

@ -275,6 +275,10 @@ SilverStripe\UserForms\Model\EditableFormField\EditableFileField:
file-field-2: file-field-2:
Name: FileUploadField Name: FileUploadField
Title: File Upload Field Title: File Upload Field
file-field-3:
Name: FileUploadField
Title: File Upload Field Without Folder
Folder: ''
SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroupEnd: SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroupEnd:
group1end: group1end:
@ -495,6 +499,11 @@ SilverStripe\UserForms\Model\UserDefinedForm:
- =>SilverStripe\UserForms\Model\Recipient\EmailRecipient.upload-recipient - =>SilverStripe\UserForms\Model\Recipient\EmailRecipient.upload-recipient
- =>SilverStripe\UserForms\Model\Recipient\EmailRecipient.upload-no-data - =>SilverStripe\UserForms\Model\Recipient\EmailRecipient.upload-no-data
upload-form-without-folder:
Title: 'Form with upload field without folder'
Fields:
- =>SilverStripe\UserForms\Model\EditableFormField\EditableFileField.file-field-3
SilverStripe\UserForms\Model\EditableCustomRule: SilverStripe\UserForms\Model\EditableCustomRule:
rule1: rule1:
Display: Show Display: Show