Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBX-6856: Added mime types limitation for ezimage field type #299

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10745,11 +10745,6 @@ parameters:
count: 1
path: src/lib/FieldType/Validator/FloatValueValidator.php

-
message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\ImageValidator\\:\\:innerValidate\\(\\) has no return type specified\\.$#"
count: 1
path: src/lib/FieldType/Validator/ImageValidator.php

-
message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\ImageValidator\\:\\:innerValidate\\(\\) has parameter \\$filePath with no type specified\\.$#"
count: 1
Expand Down Expand Up @@ -30640,16 +30635,6 @@ parameters:
count: 1
path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php

-
message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php

-
message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value\\.$#"
count: 1
path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php

-
message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
Expand Down Expand Up @@ -30700,11 +30685,6 @@ parameters:
count: 1
path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php

-
message: "#^Parameter \\#2 \\$newImageValue of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:updateImage\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value, array given\\.$#"
count: 2
path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php

-
message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:\\$loadedImagePath has no type specified\\.$#"
count: 1
Expand Down
57 changes: 53 additions & 4 deletions src/lib/FieldType/Image/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,29 @@ class Type extends FieldType implements TranslationContainerInterface
],
];

/** @var array<string, array<array<mixed>|scalar>> */
protected $settingsSchema = [
'mimeTypes' => [
'type' => 'choice',
'default' => [],
],
];

/** @var array<string> */
private array $mimeTypes;

/** @var \Ibexa\Core\FieldType\Validator[] */
private $validators;

/**
* @param \Ibexa\Core\FieldType\Validator[] $validators
* @param array<string> $mimeTypes
* @param array<\Ibexa\Core\FieldType\Validator> $validators
*/
public function __construct(array $validators)
{
public function __construct(
array $mimeTypes,
array $validators
) {
$this->mimeTypes = $mimeTypes;
$this->validators = $validators;
}

Expand Down Expand Up @@ -166,7 +181,7 @@ public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue)
}

foreach ($this->validators as $externalValidator) {
if (!$externalValidator->validate($fieldValue)) {
if (!$externalValidator->validate($fieldValue, $fieldDefinition)) {
$errors = array_merge($errors, $externalValidator->getMessage());
}
}
Expand Down Expand Up @@ -207,6 +222,40 @@ public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue)
return $errors;
}

public function validateFieldSettings($fieldSettings): array
{
$validationErrors = [];

foreach ($fieldSettings as $name => $value) {
if (!isset($this->settingsSchema[$name])) {
$validationErrors[] = new ValidationError(
"Setting '%setting%' is unknown",
null,
[
'%setting%' => $name,
],
"[$name]"
);
}

if (
$name === 'mimeTypes'
&& !empty(array_diff($value, $this->mimeTypes))
) {
$validationErrors[] = new ValidationError(
"Setting '%setting%' contains not supported mime types",
null,
[
'%setting%' => $name,
],
"[$name]"
);
}
}

return $validationErrors;
}

/**
* Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct.
*
Expand Down
3 changes: 2 additions & 1 deletion src/lib/FieldType/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace Ibexa\Core\FieldType;

use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException as PropertyNotFound;
use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;

/**
* Base field type validator validator.
Expand Down Expand Up @@ -98,7 +99,7 @@ abstract public function validateConstraints($constraints);
*
* @return bool
*/
abstract public function validate(Value $value);
abstract public function validate(Value $value, ?FieldDefinition $fieldDefinition = null);

/**
* Returns array of messages on performed validations.
Expand Down
3 changes: 2 additions & 1 deletion src/lib/FieldType/Validator/EmailAddressValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
use Ibexa\Core\FieldType\Value as BaseValue;
Expand Down Expand Up @@ -84,7 +85,7 @@ public function validateConstraints($constraints)
*
* @return bool
*/
public function validate(BaseValue $value)
public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null)
{
$pattern = '/^((\"[^\"\f\n\r\t\v\b]+\")|([A-Za-z0-9_\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[A-Za-z0-9_\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]{2,}))$/';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
Expand Down Expand Up @@ -45,7 +46,7 @@ public function validateConstraints($constraints)
/**
* {@inheritdoc}
*/
public function validate(BaseValue $value)
public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null)
{
if (
pathinfo($value->fileName, PATHINFO_BASENAME) !== $value->fileName ||
Expand Down
3 changes: 2 additions & 1 deletion src/lib/FieldType/Validator/FileSizeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
use Ibexa\Core\FieldType\Value as BaseValue;
Expand Down Expand Up @@ -66,7 +67,7 @@ public function validateConstraints($constraints)
*
* @return bool
*/
public function validate(BaseValue $value)
public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null)
{
$isValid = true;

Expand Down
3 changes: 2 additions & 1 deletion src/lib/FieldType/Validator/FloatValueValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
use Ibexa\Core\FieldType\Value as BaseValue;
Expand Down Expand Up @@ -82,7 +83,7 @@ public function validateConstraints($constraints)
*
* @return bool
*/
public function validate(BaseValue $value)
public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null)
{
$isValid = true;

Expand Down
41 changes: 35 additions & 6 deletions src/lib/FieldType/Validator/ImageValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
use Ibexa\Core\FieldType\Value;
Expand All @@ -15,34 +16,42 @@ class ImageValidator extends Validator
/**
* {@inheritdoc}
*/
public function validateConstraints($constraints)
public function validateConstraints($constraints, ?FieldDefinition $fieldDefinition = null)
{
return [];
}

/**
* {@inheritdoc}
*/
public function validate(Value $value)
public function validate(Value $value, ?FieldDefinition $fieldDefinition = null)
{
$mimeTypes = [];
if (null !== $fieldDefinition) {
$mimeTypes = $fieldDefinition->getFieldSettings()['mimeTypes'] ?? [];
}

$isValid = true;
if (isset($value->inputUri) && !$this->innerValidate($value->inputUri)) {
if (isset($value->inputUri) && !$this->innerValidate($value->inputUri, $mimeTypes)) {
$isValid = false;
}

// BC: Check if file is a valid image if the value of 'id' matches a local file
if (isset($value->id) && file_exists($value->id) && !$this->innerValidate($value->id)) {
if (isset($value->id) && file_exists($value->id) && !$this->innerValidate($value->id, $mimeTypes)) {
$isValid = false;
}

return $isValid;
}

private function innerValidate($filePath)
/**
* @param array<string> $mimeTypes
*/
private function innerValidate($filePath, array $mimeTypes): bool
{
// silence `getimagesize` error as extension-wise valid image files might produce it anyway
// note that file extension checking is done using other validation which should be called before this one
if (!@getimagesize($filePath)) {
if (!$imageData = @getimagesize($filePath)) {
$this->errors[] = new ValidationError(
'A valid image file is required.',
null,
Expand All @@ -53,6 +62,26 @@ private function innerValidate($filePath)
return false;
}

// If array with mime types is empty, it means that all mime types are allowed
if (empty($mimeTypes)) {
return true;
}

$mimeType = $imageData['mime'];
if (!in_array($mimeType, $mimeTypes, true)) {
$this->errors[] = new ValidationError(
'The mime type of the file is invalid (%mimeType%). Allowed mime types are %mimeTypes%.',
null,
[
'%mimeType%' => $mimeType,
'%mimeTypes%' => implode(', ', $mimeTypes),
],
'id'
);

return false;
}

return true;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/FieldType/Validator/IntegerValueValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
use Ibexa\Core\FieldType\Value as BaseValue;
Expand Down Expand Up @@ -85,7 +86,7 @@ public function validateConstraints($constraints)
*
* @return bool
*/
public function validate(BaseValue $value)
public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null)
{
$isValid = true;

Expand Down
3 changes: 2 additions & 1 deletion src/lib/FieldType/Validator/StringLengthValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Core\FieldType\Validator;

use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\ValidationError;
use Ibexa\Core\FieldType\Validator;
use Ibexa\Core\FieldType\Value as BaseValue;
Expand Down Expand Up @@ -104,7 +105,7 @@ protected function validateConstraintsOrder($constraints)
*
* @return bool
*/
public function validate(BaseValue $value)
public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null)
{
$isValid = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField
$storageDef->dataFloat1 = $validators['FileSizeValidator']['maxFileSize'] ?? 0.0;
$storageDef->dataInt2 = (int)($validators['AlternativeTextValidator']['required'] ?? 0);
$storageDef->dataText1 = 'MB';

$fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings;

$storageDef->dataText5 = json_encode($fieldSettings['mimeTypes'] ?? [], JSON_THROW_ON_ERROR);
}

public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef): void
Expand All @@ -200,10 +204,27 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin
'required' => (bool)$storageDef->dataInt2,
],
],
'fieldSettings' => $this->getFieldSettings($storageDef),
]
);
}

/**
* @return array<string>
*
* @throws \JsonException
*/
private function getFieldSettings(StorageFieldDefinition $storageFieldDefinition): array
{
$mimeTypes = $storageFieldDefinition->dataText5;

return [
'mimeTypes' => !empty($mimeTypes)
? json_decode($mimeTypes, true, 512, JSON_THROW_ON_ERROR)
: [],
];
}

/**
* Parses the XML from the legacy database.
*
Expand Down
15 changes: 14 additions & 1 deletion src/lib/Resources/settings/fieldtypes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ parameters:
ZM: {Name: "Zambia", Alpha2: "ZM", Alpha3: "ZMB", IDC: "260"}
ZW: {Name: "Zimbabwe", Alpha2: "ZW", Alpha3: "ZWE", IDC: "263"}

ibexa.field_type.ezimage.mime_types:
- image/jpeg
- image/pjpeg
- image/png
- image/bmp
- image/gif
- image/tiff
- image/x-icon
- image/webp

services:
Ibexa\Core\FieldType\FieldType:
class: Ibexa\Core\FieldType\FieldType
Expand Down Expand Up @@ -325,7 +335,10 @@ services:
Ibexa\Core\FieldType\Image\Type:
class: Ibexa\Core\FieldType\Image\Type
arguments:
- ['@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator', '@Ibexa\Core\FieldType\Validator\ImageValidator']
$validators:
- '@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator'
- '@Ibexa\Core\FieldType\Validator\ImageValidator'
$mimeTypes: '%ibexa.field_type.ezimage.mime_types%'
parent: Ibexa\Core\FieldType\FieldType
tags:
- {name: ibexa.field_type, alias: ezimage}
Expand Down
Loading