diff --git a/features/authorization/deny.feature b/features/authorization/deny.feature
index 404a8c45438..3a3bdde3f23 100644
--- a/features/authorization/deny.feature
+++ b/features/authorization/deny.feature
@@ -59,7 +59,8 @@ Feature: Authorization checking
{
"title": "Special Title",
"description": "Description",
- "owner": "dunglas"
+ "owner": "dunglas",
+ "adminOnlyProperty": "secret"
}
"""
Then the response status code should be 201
@@ -100,3 +101,18 @@ Feature: Authorization checking
}
"""
Then the response status code should be 200
+
+ Scenario: An admin retrieves a resource with an admin only viewable property
+ When I add "Accept" header equal to "application/ld+json"
+ And I add "Content-Type" header equal to "application/ld+json"
+ And I add "Authorization" header equal to "Basic YWRtaW46a2l0dGVu"
+ And I send a "GET" request to "/secured_dummies"
+ Then the response status code should be 200
+ And the response should contain "adminOnlyProperty"
+
+ Scenario: A user retrieves a resource with an admin only viewable property
+ When I add "Accept" header equal to "application/ld+json"
+ And I add "Authorization" header equal to "Basic ZHVuZ2xhczprZXZpbg=="
+ And I send a "GET" request to "/secured_dummies"
+ Then the response status code should be 200
+ And the response should not contain "adminOnlyProperty"
diff --git a/features/graphql/authorization.feature b/features/graphql/authorization.feature
index a4cc77c4ccd..165b0131c20 100644
--- a/features/graphql/authorization.feature
+++ b/features/graphql/authorization.feature
@@ -91,7 +91,7 @@ Feature: Authorization checking
When I send the following GraphQL request:
"""
mutation {
- createSecuredDummy(input: {owner: "me", title: "Hi", description: "Desc", clientMutationId: "auth"}) {
+ createSecuredDummy(input: {owner: "me", title: "Hi", description: "Desc", adminOnlyProperty: "secret", clientMutationId: "auth"}) {
securedDummy {
title
owner
@@ -112,7 +112,7 @@ Feature: Authorization checking
And I send the following GraphQL request:
"""
mutation {
- createSecuredDummy(input: {owner: "someone", title: "Hi", description: "Desc"}) {
+ createSecuredDummy(input: {owner: "someone", title: "Hi", description: "Desc", adminOnlyProperty: "secret"}) {
securedDummy {
id
title
@@ -131,7 +131,7 @@ Feature: Authorization checking
And I send the following GraphQL request:
"""
mutation {
- createSecuredDummy(input: {owner: "dunglas", title: "Hi", description: "Desc"}) {
+ createSecuredDummy(input: {owner: "dunglas", title: "Hi", description: "Desc", adminOnlyProperty: "secret"}) {
securedDummy {
id
title
diff --git a/src/Annotation/ApiProperty.php b/src/Annotation/ApiProperty.php
index 2ec25e3545a..1c21ec1a05d 100644
--- a/src/Annotation/ApiProperty.php
+++ b/src/Annotation/ApiProperty.php
@@ -29,6 +29,7 @@
* @Attribute("openapiContext", type="array"),
* @Attribute("jsonldContext", type="array"),
* @Attribute("push", type="bool"),
+ * @Attribute("security", type="string"),
* @Attribute("swaggerContext", type="array")
* )
*/
@@ -118,6 +119,13 @@ final class ApiProperty
*/
private $push;
+ /**
+ * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
+ *
+ * @var string
+ */
+ private $security;
+
/**
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
*
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/api.xml b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
index f34847662f7..057c887b9af 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/api.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
@@ -115,7 +115,7 @@
null
- false
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/hal.xml b/src/Bridge/Symfony/Bundle/Resources/config/hal.xml
index 2828d13eec5..a42c976c90b 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/hal.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/hal.xml
@@ -40,7 +40,7 @@
- false
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml b/src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml
index 410a327b43d..7d232706bc1 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml
@@ -41,7 +41,7 @@
- false
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml b/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml
index b40b0ed1c2d..3912ad1e1b4 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml
@@ -27,7 +27,7 @@
- false
+
diff --git a/src/JsonApi/Serializer/ItemNormalizer.php b/src/JsonApi/Serializer/ItemNormalizer.php
index ca869d67f0b..ade96fe723c 100644
--- a/src/JsonApi/Serializer/ItemNormalizer.php
+++ b/src/JsonApi/Serializer/ItemNormalizer.php
@@ -20,6 +20,7 @@
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
+use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
use ApiPlatform\Core\Serializer\CacheKeyTrait;
use ApiPlatform\Core\Serializer\ContextTrait;
@@ -49,9 +50,9 @@ final class ItemNormalizer extends AbstractItemNormalizer
private $componentsCache = [];
- public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ?PropertyAccessorInterface $propertyAccessor, ?NameConverterInterface $nameConverter, ResourceMetadataFactoryInterface $resourceMetadataFactory, array $defaultContext = [], iterable $dataTransformers = [])
+ public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ?PropertyAccessorInterface $propertyAccessor, ?NameConverterInterface $nameConverter, ResourceMetadataFactoryInterface $resourceMetadataFactory, array $defaultContext = [], iterable $dataTransformers = [], ResourceAccessCheckerInterface $resourceAccessChecker = null)
{
- parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, null, null, false, $defaultContext, $dataTransformers, $resourceMetadataFactory);
+ parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, null, null, false, $defaultContext, $dataTransformers, $resourceMetadataFactory, $resourceAccessChecker);
}
/**
diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php
index aca10e346d7..197050fdd55 100644
--- a/src/JsonLd/Serializer/ItemNormalizer.php
+++ b/src/JsonLd/Serializer/ItemNormalizer.php
@@ -19,6 +19,7 @@
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
+use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
use ApiPlatform\Core\Serializer\ContextTrait;
use ApiPlatform\Core\Util\ClassInfoTrait;
@@ -43,9 +44,9 @@ final class ItemNormalizer extends AbstractItemNormalizer
private $contextBuilder;
- public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ContextBuilderInterface $contextBuilder, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, array $defaultContext = [], iterable $dataTransformers = [])
+ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ContextBuilderInterface $contextBuilder, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, array $defaultContext = [], iterable $dataTransformers = [], ResourceAccessCheckerInterface $resourceAccessChecker = null)
{
- parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, null, false, $defaultContext, $dataTransformers, $resourceMetadataFactory);
+ parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, null, false, $defaultContext, $dataTransformers, $resourceMetadataFactory, $resourceAccessChecker);
$this->contextBuilder = $contextBuilder;
}
diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php
index 9046325f1d1..61b2f6a55bb 100644
--- a/src/Serializer/AbstractItemNormalizer.php
+++ b/src/Serializer/AbstractItemNormalizer.php
@@ -24,6 +24,7 @@
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
+use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use ApiPlatform\Core\Util\ClassInfoTrait;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -55,13 +56,14 @@ abstract class AbstractItemNormalizer extends AbstractObjectNormalizer
protected $propertyMetadataFactory;
protected $iriConverter;
protected $resourceClassResolver;
+ protected $resourceAccessChecker;
protected $propertyAccessor;
protected $itemDataProvider;
protected $allowPlainIdentifiers;
protected $dataTransformers = [];
protected $localCache = [];
- public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, array $defaultContext = [], iterable $dataTransformers = [], ResourceMetadataFactoryInterface $resourceMetadataFactory = null)
+ public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, array $defaultContext = [], iterable $dataTransformers = [], ResourceMetadataFactoryInterface $resourceMetadataFactory = null, ResourceAccessCheckerInterface $resourceAccessChecker = null)
{
if (!isset($defaultContext['circular_reference_handler'])) {
$defaultContext['circular_reference_handler'] = function ($object) {
@@ -83,6 +85,7 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
$this->allowPlainIdentifiers = $allowPlainIdentifiers;
$this->dataTransformers = $dataTransformers;
$this->resourceMetadataFactory = $resourceMetadataFactory;
+ $this->resourceAccessChecker = $resourceAccessChecker;
}
/**
@@ -349,6 +352,25 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu
return $allowedAttributes;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
+ {
+ if (!parent::isAllowedAttribute($classOrObject, $attribute, $format, $context)) {
+ return false;
+ }
+
+ $options = $this->getFactoryOptions($context);
+ $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $options);
+ $security = $propertyMetadata->getAttribute('security');
+ if ($this->resourceAccessChecker && $security) {
+ return $this->resourceAccessChecker->isGranted($attribute, $security);
+ }
+
+ return true;
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Serializer/ItemNormalizer.php b/src/Serializer/ItemNormalizer.php
index f593dafddbb..293b7955766 100644
--- a/src/Serializer/ItemNormalizer.php
+++ b/src/Serializer/ItemNormalizer.php
@@ -20,6 +20,7 @@
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
+use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -36,9 +37,9 @@ class ItemNormalizer extends AbstractItemNormalizer
{
private $logger;
- public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, LoggerInterface $logger = null, iterable $dataTransformers = [], ResourceMetadataFactoryInterface $resourceMetadataFactory = null)
+ public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, LoggerInterface $logger = null, iterable $dataTransformers = [], ResourceMetadataFactoryInterface $resourceMetadataFactory = null, ResourceAccessCheckerInterface $resourceAccessChecker = null)
{
- parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, $itemDataProvider, $allowPlainIdentifiers, [], $dataTransformers, $resourceMetadataFactory);
+ parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, $itemDataProvider, $allowPlainIdentifiers, [], $dataTransformers, $resourceMetadataFactory, $resourceAccessChecker);
$this->logger = $logger ?: new NullLogger();
}
diff --git a/tests/Annotation/ApiPropertyTest.php b/tests/Annotation/ApiPropertyTest.php
index ea08ce657d0..aa550d42e4f 100644
--- a/tests/Annotation/ApiPropertyTest.php
+++ b/tests/Annotation/ApiPropertyTest.php
@@ -52,6 +52,7 @@ public function testConstruct()
'fetchable' => true,
'fetchEager' => false,
'jsonldContext' => ['foo' => 'bar'],
+ 'security' => 'is_granted(\'ROLE_ADMIN\')',
'swaggerContext' => ['foo' => 'baz'],
'openapiContext' => ['foo' => 'baz'],
'push' => true,
@@ -62,6 +63,7 @@ public function testConstruct()
'fetchable' => false,
'fetch_eager' => false,
'jsonld_context' => ['foo' => 'bar'],
+ 'security' => 'is_granted(\'ROLE_ADMIN\')',
'swagger_context' => ['foo' => 'baz'],
'openapi_context' => ['foo' => 'baz'],
'push' => true,
diff --git a/tests/Fixtures/TestBundle/Document/SecuredDummy.php b/tests/Fixtures/TestBundle/Document/SecuredDummy.php
index bc932469381..6820d13b0f5 100644
--- a/tests/Fixtures/TestBundle/Document/SecuredDummy.php
+++ b/tests/Fixtures/TestBundle/Document/SecuredDummy.php
@@ -13,6 +13,7 @@
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Document;
+use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Validator\Constraints as Assert;
@@ -26,7 +27,7 @@
* @ApiResource(
* attributes={"security"="is_granted('ROLE_USER')"},
* collectionOperations={
- * "get",
+ * "get"={"security"="is_granted('ROLE_USER') or is_granted('ROLE_ADMIN')"},
* "get_from_data_provider_generator"={
* "method"="GET",
* "path"="custom_data_provider_generator",
@@ -72,6 +73,14 @@ class SecuredDummy
*/
private $description = '';
+ /**
+ * @var string The dummy secret property, only readable/writable by specific users
+ *
+ * @ODM\Field
+ * @ApiProperty(security="is_granted('ROLE_ADMIN')")
+ */
+ private $adminOnlyProperty = '';
+
/**
* @var string The owner
*
@@ -105,6 +114,16 @@ public function setDescription(string $description)
$this->description = $description;
}
+ public function getAdminOnlyProperty(): ?string
+ {
+ return $this->adminOnlyProperty;
+ }
+
+ public function setAdminOnlyProperty(?string $adminOnlyProperty)
+ {
+ $this->adminOnlyProperty = $adminOnlyProperty;
+ }
+
public function getOwner(): string
{
return $this->owner;
diff --git a/tests/Fixtures/TestBundle/Entity/SecuredDummy.php b/tests/Fixtures/TestBundle/Entity/SecuredDummy.php
index f01d909c33c..19ffc2cc816 100644
--- a/tests/Fixtures/TestBundle/Entity/SecuredDummy.php
+++ b/tests/Fixtures/TestBundle/Entity/SecuredDummy.php
@@ -13,6 +13,7 @@
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity;
+use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
@@ -25,7 +26,7 @@
* @ApiResource(
* attributes={"security"="is_granted('ROLE_USER')"},
* collectionOperations={
- * "get",
+ * "get"={"security"="is_granted('ROLE_USER') or is_granted('ROLE_ADMIN')"},
* "get_from_data_provider_generator"={
* "method"="GET",
* "path"="custom_data_provider_generator",
@@ -73,6 +74,14 @@ class SecuredDummy
*/
private $description = '';
+ /**
+ * @var string The dummy secret property, only readable/writable by specific users
+ *
+ * @ORM\Column
+ * @ApiProperty(security="is_granted('ROLE_ADMIN')")
+ */
+ private $adminOnlyProperty = '';
+
/**
* @var string The owner
*
@@ -106,6 +115,16 @@ public function setDescription(string $description)
$this->description = $description;
}
+ public function getAdminOnlyProperty(): ?string
+ {
+ return $this->adminOnlyProperty;
+ }
+
+ public function setAdminOnlyProperty(?string $adminOnlyProperty)
+ {
+ $this->adminOnlyProperty = $adminOnlyProperty;
+ }
+
public function getOwner(): string
{
return $this->owner;
diff --git a/tests/Serializer/AbstractItemNormalizerTest.php b/tests/Serializer/AbstractItemNormalizerTest.php
index e10fbf07063..b09592cb865 100644
--- a/tests/Serializer/AbstractItemNormalizerTest.php
+++ b/tests/Serializer/AbstractItemNormalizerTest.php
@@ -25,6 +25,7 @@
use ApiPlatform\Core\Metadata\Property\PropertyNameCollection;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
+use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\InputDto;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
@@ -33,6 +34,7 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritance;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceChild;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\RelatedDummy;
+use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\SecuredDummy;
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
@@ -110,7 +112,7 @@ public function testSupportNormalizationAndSupportDenormalization()
[],
[],
null,
- false,
+ null,
]);
$this->assertTrue($normalizer->supportsNormalization($dummy));
@@ -177,7 +179,7 @@ public function testNormalize()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -196,6 +198,65 @@ public function testNormalize()
]));
}
+ public function testNormalizeWithSecuredProperty()
+ {
+ $dummy = new SecuredDummy();
+ $dummy->setTitle('myPublicTitle');
+ $dummy->setAdminOnlyProperty('secret');
+
+ $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
+ $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
+
+ $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
+ $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true));
+ $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, null, null, null, null, null, null, null, ['security' => 'is_granted(\'ROLE_ADMIN\')']));
+
+ $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/secured_dummies/1');
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
+ $propertyAccessorProphecy->getValue($dummy, 'title')->willReturn('myPublicTitle');
+ $propertyAccessorProphecy->getValue($dummy, 'adminOnlyProperty')->willReturn('secret');
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(SecuredDummy::class);
+
+ $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
+ $resourceAccessChecker->isGranted('adminOnlyProperty', 'is_granted(\'ROLE_ADMIN\')')->willReturn(false);
+
+ $serializerProphecy = $this->prophesize(SerializerInterface::class);
+ $serializerProphecy->willImplement(NormalizerInterface::class);
+ $serializerProphecy->normalize('myPublicTitle', null, Argument::type('array'))->willReturn('myPublicTitle');
+
+ $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
+ $propertyNameCollectionFactoryProphecy->reveal(),
+ $propertyMetadataFactoryProphecy->reveal(),
+ $iriConverterProphecy->reveal(),
+ $resourceClassResolverProphecy->reveal(),
+ $propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ null,
+ false,
+ [],
+ [],
+ null,
+ $resourceAccessChecker->reveal(),
+ ]);
+ $normalizer->setSerializer($serializerProphecy->reveal());
+
+ if (!interface_exists(AdvancedNameConverterInterface::class)) {
+ $normalizer->setIgnoredAttributes(['alias']);
+ }
+
+ $expected = [
+ 'title' => 'myPublicTitle',
+ ];
+ $this->assertEquals($expected, $normalizer->normalize($dummy, null, [
+ 'resources' => [],
+ ]));
+ }
+
public function testNormalizeReadableLinks()
{
$relatedDummy = new RelatedDummy();
@@ -253,7 +314,7 @@ public function testNormalizeReadableLinks()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -315,7 +376,7 @@ public function testDenormalize()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -365,7 +426,7 @@ public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass
$serializerProphecy->willImplement(DenormalizerInterface::class);
$serializerProphecy->denormalize($data, DummyForAdditionalFieldsInput::class, 'json', $cleanedContext)->willReturn($dummyInputDto);
- $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, null, false, [], [$inputDataTransformerProphecy->reveal()], null) extends AbstractItemNormalizer {
+ $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, null, false, [], [$inputDataTransformerProphecy->reveal()], null, null) extends AbstractItemNormalizer {
};
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -424,7 +485,7 @@ public function testDenormalizeWritableLinks()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -486,7 +547,7 @@ public function testBadRelationType()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -544,7 +605,7 @@ public function testInnerDocumentNotAllowed()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -589,7 +650,7 @@ public function testBadType()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -631,7 +692,7 @@ public function testTypeChecksCanBeDisabled()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -677,7 +738,7 @@ public function testJsonAllowIntAsFloat()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -752,7 +813,7 @@ public function testDenormalizeBadKeyType()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -794,7 +855,7 @@ public function testNullable()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -854,7 +915,7 @@ public function testChildInheritedProperty(): void
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -906,7 +967,7 @@ public function testDenormalizeRelationWithPlainId()
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -969,7 +1030,7 @@ public function testDenormalizeRelationWithPlainIdNotFound()
[],
[],
null,
- true,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -1027,7 +1088,7 @@ public function testDoNotDenormalizeRelationWithPlainIdWhenPlainIdentifiersAreNo
[],
[],
null,
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -1114,7 +1175,7 @@ public function testNormalizationWithDataTransformer()
[],
[$dataTransformerProphecy->reveal(), $secondDataTransformerProphecy->reveal()],
$resourceMetadataFactoryProphecy->reveal(),
- false,
+ null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());