Skip to content

Commit 74ef5ed

Browse files
committed
Return correct IRI from the /me endpoint if a Get operation exists
1 parent badf1fe commit 74ef5ed

4 files changed

Lines changed: 56 additions & 9 deletions

File tree

features/user/me.feature

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@ Feature: Add a /me endpoint
77
Given I add "Accept" header equal to "application/ld+json"
88
And I add "Content-Type" header equal to "application/ld+json"
99

10+
@loginUser
11+
Scenario: I can retrieve the current logged in user object
12+
When I send a "GET" request to the resource "login_user"
13+
Then the response status code should be 200
14+
And the JSON should be valid according to the schema file "user.schema.json"
15+
And the JSON node "@id" should be equal to the IRI of the resource "login_user"
16+
And the JSON node "_metadata.mercureSubscribeTopics[0]" should be equal to "http://example.com/_/component_groups/{id}{._format}"
17+
1018
@loginUser
1119
Scenario: I can retrieve the current logged in user object
1220
When I send a "GET" request to "/me"
1321
Then the response status code should be 200
1422
And the JSON should be valid according to the schema file "user.schema.json"
23+
And the JSON node "@id" should be equal to the IRI of the resource "login_user"
1524
And the JSON node "_metadata.mercureSubscribeTopics[0]" should be equal to "http://example.com/_/component_groups/{id}{._format}"
1625

1726
Scenario: I can retrieve the current logged in user object

src/ApiPlatform/Api/IriConverter.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@
1313

1414
namespace Silverback\ApiComponentsBundle\ApiPlatform\Api;
1515

16+
use ApiPlatform\Metadata\Exception\OperationNotFoundException;
17+
use ApiPlatform\Metadata\Exception\ResourceClassNotFoundException;
18+
use ApiPlatform\Metadata\Get;
1619
use ApiPlatform\Metadata\IriConverterInterface;
1720
use ApiPlatform\Metadata\Operation;
21+
use ApiPlatform\Metadata\Operations;
22+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
1823
use ApiPlatform\Metadata\UrlGeneratorInterface;
1924
use Silverback\ApiComponentsBundle\Entity\Core\Route;
2025

@@ -23,7 +28,7 @@
2328
*/
2429
class IriConverter implements IriConverterInterface
2530
{
26-
public function __construct(private IriConverterInterface $decorated)
31+
public function __construct(private IriConverterInterface $decorated, private ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory)
2732
{
2833
}
2934

@@ -33,8 +38,39 @@ public function getResourceFromIri(string $iri, array $context = [], ?Operation
3338
}
3439

3540
// We want relations when they are found, to use the IRI with the path
41+
42+
/**
43+
* @throws ResourceClassNotFoundException
44+
* @throws \Exception
45+
*/
3646
public function getIriFromResource($resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = []): ?string
3747
{
48+
if ($operation?->getName() === 'me') {
49+
// we do not want to return the /me IRI if a Get endpoint is configured. The IRI should be canonical to the user's ID etc.
50+
51+
// get the API metadata of the class
52+
// find the Get operation - ApiPlatform\Metadata\Get
53+
// use this uriTemplate instead, overwrite $operation and the operation in context
54+
$resourceIterator = $this->resourceMetadataCollectionFactory->create($operation->getClass())->getIterator();
55+
while($resourceIterator->valid()) {
56+
$current = $resourceIterator->current();
57+
/**
58+
* @var Operations $resourceOperations
59+
*/
60+
$resourceOperations = $current->getOperations();
61+
$operationIterator = $resourceOperations->getIterator();
62+
while($operationIterator->valid()) {
63+
$checkOperation = $operationIterator->current();
64+
if ($checkOperation instanceof Get) {
65+
$operation = $checkOperation;
66+
$context['operation'] = $checkOperation;
67+
break 2;
68+
}
69+
$operationIterator->next();
70+
}
71+
$resourceIterator->next();
72+
}
73+
}
3874
$originalIri = $this->decorated->getIriFromResource($resource, $referenceType, $operation, $context);
3975

4076
if (!$resource instanceof Route || !($path = $resource->getPath())) {

src/Resources/config/services.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,6 +1421,7 @@
14211421
->decorate('api_platform.iri_converter')
14221422
->args([
14231423
new Reference(IriConverter::class . '.inner'),
1424+
new Reference('api_platform.metadata.resource.metadata_collection_factory'),
14241425
]);
14251426
$services->alias('silverback.iri_converter', IriConverter::class);
14261427
$services->alias(IriConverterInterface::class, IriConverter::class);

tests/Functional/TestBundle/Entity/User.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,22 @@
1818
use ApiPlatform\Metadata\Get;
1919
use ApiPlatform\Metadata\GetCollection;
2020
use ApiPlatform\Metadata\Patch;
21+
use ApiPlatform\Metadata\Post;
2122
use ApiPlatform\Metadata\Put;
2223
use Doctrine\ORM\Mapping as ORM;
2324
use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
2425

25-
const USER_SECURITY = "is_granted('ROLE_SUPER_ADMIN') or object == user";
26-
2726
/**
2827
* @author Daniel West <daniel@silverback.is>
2928
*/
30-
#[ApiResource]
31-
#[Get(security: USER_SECURITY)]
32-
#[Put(security: USER_SECURITY)]
33-
#[Patch(security: USER_SECURITY)]
34-
#[Delete(security: USER_SECURITY)]
35-
#[GetCollection(order: ['createdAt' => 'DESC'], security: "is_granted('ROLE_SUPER_ADMIN')")]
29+
#[ApiResource(operations: [
30+
new GetCollection( order: ['createdAt' => 'DESC'], security: "is_granted('ROLE_SUPER_ADMIN')"),
31+
new Post( security: "is_granted('ROLE_SUPER_ADMIN')" ),
32+
new Get( security: "is_granted('ROLE_SUPER_ADMIN') or object.getId() == user.getId()" ),
33+
new Put( security: "is_granted('ROLE_SUPER_ADMIN') or object.getId() == user.getId()" ),
34+
new Patch( security: "is_granted('ROLE_SUPER_ADMIN') or object.getId() == user.getId()" ),
35+
new Delete( security: "is_granted('ROLE_SUPER_ADMIN')" )
36+
])]
3637
#[ORM\Entity]
3738
#[ORM\Table(name: '`user`')]
3839
class User extends AbstractUser

0 commit comments

Comments
 (0)