Skip to content

Commit

Permalink
Validate PSR-compatible file upload
Browse files Browse the repository at this point in the history
Instead of converting the uploaded file object to an UploadedFile
instance from Symfony, because the latter is compatible with
Laravel's validation, let's re-implement the validation for the
three rules we were using.

The benefit: we can now avoid copying the uploaded file to a
temporary location just to do the wrapping.

In the next step, we will remove the temporary file and let the
uploader / Intervention Image handle the PSR stream directly.
  • Loading branch information
franzliedke committed Apr 10, 2020
1 parent 4c50c8d commit 1cbb2a3
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 17 deletions.
76 changes: 69 additions & 7 deletions src/User/AvatarValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,76 @@
namespace Flarum\User;

use Flarum\Foundation\AbstractValidator;
use Flarum\Foundation\ValidationException;
use Psr\Http\Message\UploadedFileInterface;
use Symfony\Component\Mime\MimeTypes;

class AvatarValidator extends AbstractValidator
{
protected $rules = [
'avatar' => [
'required',
'mimes:jpeg,png,bmp,gif',
'max:2048'
]
];
/**
* Throw an exception if a model is not valid.
*
* @param array $attributes
*/
public function assertValid(array $attributes)
{
$this->assertFileRequired($attributes['avatar']);
$this->assertFileMimes($attributes['avatar']);
$this->assertFileSize($attributes['avatar']);
}

protected function assertFileRequired(UploadedFileInterface $file)
{
if ($file->getError() !== UPLOAD_ERR_OK) {
$this->raise('required');
}
}

protected function assertFileMimes(UploadedFileInterface $file)
{
$allowedTypes = $this->getAllowedTypes();

// Block PHP files masquerading as images
$phpExtensions = ['php', 'php3', 'php4', 'php5', 'phtml'];
$fileExtension = pathinfo($file->getClientFilename(), PATHINFO_EXTENSION);

if (in_array(trim(strtolower($fileExtension)), $phpExtensions)) {
$this->raise('mimes', [':values' => implode(', ', $allowedTypes)]);
}

$guessedExtension = MimeTypes::getDefault()->getExtensions($file->getClientMediaType())[0] ?? null;

if (! in_array($guessedExtension, $allowedTypes)) {
$this->raise('mimes', [':values' => implode(', ', $allowedTypes)]);
}
}

protected function assertFileSize(UploadedFileInterface $file)
{
$maxSize = $this->getMaxSize();

if ($file->getSize() / 1024 > $maxSize) {
$this->raise('max.file', [':max' => $maxSize]);
}
}

protected function raise($error, array $parameters = [])
{
$message = $this->translator->trans(
"validation.$error",
$parameters + [':attribute' => 'avatar']
);

throw new ValidationException(['avatar' => $message]);
}

protected function getMaxSize()
{
return 2048;
}

protected function getAllowedTypes()
{
return ['jpeg', 'png', 'bmp', 'gif'];
}
}
11 changes: 1 addition & 10 deletions src/User/Command/UploadAvatarHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use Flarum\User\UserRepository;
use Illuminate\Contracts\Events\Dispatcher;
use Intervention\Image\ImageManager;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class UploadAvatarHandler
{
Expand Down Expand Up @@ -65,6 +64,7 @@ public function __construct(Dispatcher $events, UserRepository $users, Applicati
* @param UploadAvatar $command
* @return \Flarum\User\User
* @throws \Flarum\User\Exception\PermissionDeniedException
* @throws \Flarum\Foundation\ValidationException
*/
public function handle(UploadAvatar $command)
{
Expand All @@ -82,15 +82,6 @@ public function handle(UploadAvatar $command)
$file->moveTo($tmpFile);

try {
$file = new UploadedFile(
$tmpFile,
$file->getClientFilename(),
$file->getClientMediaType(),
$file->getSize(),
$file->getError(),
true
);

$this->validator->assertValid(['avatar' => $file]);

$image = (new ImageManager)->make($tmpFile);
Expand Down

0 comments on commit 1cbb2a3

Please sign in to comment.