Skip to content

Commit

Permalink
feat(api-platform): handle scope on endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
tleon committed Sep 6, 2023
1 parent 70bc0e4 commit a298f72
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 11 deletions.
2 changes: 1 addition & 1 deletion app/config/security_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ security:
oauth2:
memory:
users:
my_client_id: { password: 'prestashop' }
my_client_id: { password: 'prestashop', roles: [ 'ROLE_HOOK_READ' ] }

encoders:
Symfony\Component\Security\Core\User\User: plaintext
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/

declare(strict_types=1);

namespace PrestaShopBundle\ApiPlatform\Decorator;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;

/** Decorate Api platform's ResourceMetadataCollectionFactory, so we can alter the results it returns */
class AttributesResourceMetadataCollectionFactoryDecorator implements ResourceMetadataCollectionFactoryInterface
{
private ResourceMetadataCollectionFactoryInterface $innerFactory;

public function __construct(ResourceMetadataCollectionFactoryInterface $innerFactory)
{
$this->innerFactory = $innerFactory;
}

public function create(string $resourceClass): ResourceMetadataCollection
{
// We call the original method since we only want to alter the result of this method.
$resourceMetadataCollection = $this->innerFactory->create($resourceClass);

/** @var ApiResource $resourceMetadata */
foreach ($resourceMetadataCollection as $resourceMetadata) {
$operations = $resourceMetadata->getOperations();
/** @var Operation $operation */
foreach ($operations as $key => $operation) {
$extraProperties = $operation->getExtraProperties();
if (array_key_exists('scopes', $extraProperties)) {
$scopesToAdd = $extraProperties['scopes'];
$modifiedOperationWithSecurity = $operation->withSecurity(
$this->translateScopeToSecurity($scopesToAdd, $operation->getSecurity())
);
// We unset the extra property scopes so that the element is not treated again.
unset($extraProperties['scopes']);
$modifiedOperation = $modifiedOperationWithSecurity->withExtraProperties($extraProperties);
// We remove the original element and replace it with our clone.
$operations->remove($key);
$operations->add($key, $modifiedOperation);
}
}
}

return $resourceMetadataCollection;
}

private function translateScopeToSecurity(array $scopes, ?string $existingSecurity): string
{
$security = '';
$arrayLength = count($scopes);
foreach ($scopes as $key => $scope) {
$security .= 'is_granted("ROLE_' . strtoupper($scope) . '")';
if ($key !== $arrayLength - 1) {
$security .= ' OR ';
}
}

return empty($existingSecurity) ? $security : sprintf('(%s) OR (%s)', $existingSecurity, $security);
}
}
19 changes: 9 additions & 10 deletions src/PrestaShopBundle/ApiPlatform/Resources/Hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,16 @@
[
'name' => 'Authorization',
'in' => 'scopes',
'description' => 'read:hook-status <br> write:hook-status ',
'description' => 'hook_read <br> hook_write ',
],
],
],
exceptionToStatus: [HookNotFoundException::class => 404],
normalizationContext: [
'groups' => [
'hook-status:read',
],
],
provider: QueryProvider::class,
extraProperties: ['query' => GetHookStatus::class]
extraProperties: [
'query' => GetHookStatus::class,
'scopes' => ['hook_read'],
]
),
new Put(
uriTemplate: '/hook-status',
Expand All @@ -84,17 +82,18 @@
requirements: ['id' => '\d+'],
exceptionToStatus: [HookNotFoundException::class => 404],
provider: QueryProvider::class,
extraProperties: ['query' => GetHook::class]
extraProperties: [
'query' => GetHook::class,
'scopes' => ['hook_read'],
]
),
],
)]
class Hook
{
#[ApiProperty(identifier: true)]
#[Groups(['hook-status:read'])]
public int $id;

#[Groups(['hook-status:read'])]
public bool $active;

public string $name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ services:

prestashop.adapter.hook.manager:
alias: PrestaShop\PrestaShop\Adapter\HookManager

prestashop.attributes.ressource.metadata.collection.factory:
class: PrestaShopBundle\ApiPlatform\Decorator\AttributesResourceMetadataCollectionFactoryDecorator
decorates: api_platform.metadata.resource.metadata_collection_factory
arguments:
$innerFactory: '@prestashop.attributes.ressource.metadata.collection.factory.inner'
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public function getNewToken(
$token = new AccessToken();
$token->setClient($clientEntity);
$token->setUserIdentifier($userIdentifier);
foreach ($scopes as $scope) {
$token->addScope($scope);
}

return $token;
}
Expand Down

0 comments on commit a298f72

Please sign in to comment.