Skip to content
Snippets Groups Projects
Commit fd33f04f authored by Groß-Vogt, Tobias's avatar Groß-Vogt, Tobias
Browse files

update of local data handling

parent 0b60e08d
Branches
Tags
No related merge requests found
Pipeline #229833 passed
...@@ -97,30 +97,27 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer ...@@ -97,30 +97,27 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer
public function onEvent(Event $event) public function onEvent(Event $event)
{ {
if ($event instanceof LocalDataPreEvent) { if ($event instanceof LocalDataPreEvent) {
$queryParametersOut = []; foreach ($event->getPendingQueryParametersIn() as $localDataAttributeName => $localDataAttributeValue) {
if (($attributeMapEntry = $this->attributeMapping[$localDataAttributeName] ?? null) !== null) {
// matriculationNumber:0011675 if (!$this->isGranted($localDataAttributeName)) {
foreach ($event->getQueryParameters() as $queryParameterName => $queryParameterValue) { throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName));
if (($attributeMapEntry = $this->attributeMapping[$queryParameterName] ?? null) !== null) { }
$sourceAttributeName = $attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY][0]; $sourceAttributeName = $attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY][0];
$queryParametersOut[$sourceAttributeName] = $queryParameterValue; $event->addQueryParameterOut($sourceAttributeName, $localDataAttributeValue);
$event->acknowledgeQueryParameterIn($localDataAttributeName);
} }
} }
$event->setQueryParameters($queryParametersOut); $this->onPreEvent($event);
$this->onPre($event);
} elseif ($event instanceof LocalDataPostEvent) { } elseif ($event instanceof LocalDataPostEvent) {
$sourceData = $event->getSourceData(); foreach ($event->getPendingRequestedAttributes() as $localDataAttributeName) {
if (($attributeMapEntry = $this->attributeMapping[$localDataAttributeName] ?? null) !== null) {
foreach ($this->attributeMapping as $localDataAttributeName => $attributeMapEntry) {
if ($event->isLocalDataAttributeRequested($localDataAttributeName)) {
if (!$this->isGranted($localDataAttributeName)) { if (!$this->isGranted($localDataAttributeName)) {
throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName)); throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName));
} }
$attributeValue = null; $attributeValue = null;
foreach ($attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY] as $sourceAttributeName) { foreach ($attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY] as $sourceAttributeName) {
if (($value = $sourceData[$sourceAttributeName] ?? null) !== null) { if (($value = $event->getSourceData()[$sourceAttributeName] ?? null) !== null) {
$attributeValue = $value; $attributeValue = $value;
break; break;
} }
...@@ -134,7 +131,7 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer ...@@ -134,7 +131,7 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer
} }
} }
} }
$this->onPost($event); $this->onPostEvent($event);
} }
} }
...@@ -182,11 +179,11 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer ...@@ -182,11 +179,11 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer
throw new \RuntimeException(sprintf('child classes must implement the \'%s\' method', __METHOD__)); throw new \RuntimeException(sprintf('child classes must implement the \'%s\' method', __METHOD__));
} }
protected function onPre(LocalDataPreEvent $preEvent) protected function onPreEvent(LocalDataPreEvent $preEvent)
{ {
} }
protected function onPost(LocalDataPostEvent $postEvent) protected function onPostEvent(LocalDataPostEvent $postEvent)
{ {
} }
} }
...@@ -83,15 +83,20 @@ class LocalDataEventDispatcher ...@@ -83,15 +83,20 @@ class LocalDataEventDispatcher
public function dispatch(Event $event, string $eventName = null): void public function dispatch(Event $event, string $eventName = null): void
{ {
if ($event instanceof LocalDataPreEvent) { if ($event instanceof LocalDataPreEvent) {
$event->setQueryParameters($this->queryParameters); $event->initQueryParametersIn($this->queryParameters);
$this->eventDispatcher->dispatch($event, $eventName); $this->eventDispatcher->dispatch($event, $eventName);
$pendingAttributes = $event->getPendingQueryParametersIn();
if (count($pendingAttributes) !== 0) {
throw ApiError::withDetails(Response::HTTP_BAD_REQUEST, sprintf("the following local query attributes were not acknowledged for resource '%s': %s", $this->uniqueEntityName, implode(', ', $pendingAttributes)));
}
} elseif ($event instanceof LocalDataPostEvent) { } elseif ($event instanceof LocalDataPostEvent) {
$event->setRequestedAttributes($this->requestedAttributes); $event->setRequestedAttributes($this->requestedAttributes);
$this->eventDispatcher->dispatch($event, $eventName); $this->eventDispatcher->dispatch($event, $eventName);
$remainingLocalDataAttributes = $event->getRemainingRequestedAttributes(); $pendingAttributes = $event->getPendingRequestedAttributes();
if (!empty($remainingLocalDataAttributes)) { if (count($pendingAttributes) !== 0) {
throw ApiError::withDetails(Response::HTTP_BAD_REQUEST, sprintf("the following requested local data attributes could not be provided for resource '%s': %s", $this->uniqueEntityName, implode(', ', $remainingLocalDataAttributes))); throw ApiError::withDetails(Response::HTTP_BAD_REQUEST, sprintf("the following requested local data attributes could not be provided for resource '%s': %s", $this->uniqueEntityName, implode(', ', $pendingAttributes)));
} }
} else { } else {
$this->eventDispatcher->dispatch($event, $eventName); $this->eventDispatcher->dispatch($event, $eventName);
......
...@@ -53,7 +53,7 @@ class LocalDataPostEvent extends Event implements LoggerAwareInterface ...@@ -53,7 +53,7 @@ class LocalDataPostEvent extends Event implements LoggerAwareInterface
* *
* @retrun string[] * @retrun string[]
*/ */
public function getRemainingRequestedAttributes(): array public function getPendingRequestedAttributes(): array
{ {
return $this->requestedAttributes; return $this->requestedAttributes;
} }
...@@ -103,11 +103,11 @@ class LocalDataPostEvent extends Event implements LoggerAwareInterface ...@@ -103,11 +103,11 @@ class LocalDataPostEvent extends Event implements LoggerAwareInterface
* *
* @throws ApiError if attribute $key is not in the set of requested attributes * @throws ApiError if attribute $key is not in the set of requested attributes
*/ */
private function setLocalDataAttributeInternal(string $key, $value, bool $warnfNotFound): void private function setLocalDataAttributeInternal(string $key, $value, bool $warnIfNotFound): void
{ {
$arrayKey = array_search($key, $this->requestedAttributes, true); $arrayKey = array_search($key, $this->requestedAttributes, true);
if ($arrayKey === false) { if ($arrayKey === false) {
if ($warnfNotFound) { if ($warnIfNotFound) {
if ($this->logger !== null) { if ($this->logger !== null) {
$this->logger->warning(sprintf("trying to set local data attribute '%s', which was not requested for entity '%s'", $key, LocalDataEventDispatcher::getUniqueEntityName(get_class($this->entity)))); $this->logger->warning(sprintf("trying to set local data attribute '%s', which was not requested for entity '%s'", $key, LocalDataEventDispatcher::getUniqueEntityName(get_class($this->entity))));
} }
......
...@@ -8,23 +8,40 @@ use Symfony\Contracts\EventDispatcher\Event; ...@@ -8,23 +8,40 @@ use Symfony\Contracts\EventDispatcher\Event;
class LocalDataPreEvent extends Event class LocalDataPreEvent extends Event
{ {
public const NAME = 'dbp.relay.relay_core.local_data_aware_event.pre'; /** @var string[] */
private $queryParametersIn;
/** @var array */ /** @var string[] */
private $queryParameters; private $queryParametersOut;
public function __construct() public function __construct()
{ {
$this->queryParameters = []; $this->queryParametersIn = [];
$this->queryParametersOut = [];
} }
public function setQueryParameters(array $queryParameters): void public function initQueryParametersIn(array $queryParametersIn): void
{ {
$this->queryParameters = $queryParameters; $this->queryParametersIn = $queryParametersIn;
} }
public function getQueryParameters(): array public function getPendingQueryParametersIn(): array
{ {
return $this->queryParameters; return $this->queryParametersIn;
}
public function acknowledgeQueryParameterIn(string $queryParameterName): void
{
unset($this->queryParametersIn[$queryParameterName]);
}
public function addQueryParameterOut(string $queryParameterName, string $queryParameterValue): void
{
$this->queryParametersOut[$queryParameterName] = $queryParameterValue;
}
public function getQueryParameterOut(): array
{
return $this->queryParametersOut;
} }
} }
...@@ -19,14 +19,14 @@ class LocalDataTest extends TestCase ...@@ -19,14 +19,14 @@ class LocalDataTest extends TestCase
/** @var LocalDataEventDispatcher */ /** @var LocalDataEventDispatcher */
private $localDataEventDispatcher; private $localDataEventDispatcher;
/** @var TestEntityLocalDataPostEventSubscriber */ /** @var TestEntityLocalDataEventSubscriber */
private $localDataEventSubscriber; private $localDataEventSubscriber;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
$localDataEventSubscriber = new TestEntityLocalDataPostEventSubscriber(); $localDataEventSubscriber = new TestEntityLocalDataEventSubscriber();
$localDataEventSubscriber->_injectServices(new TestUserSession('testuser'), new AuthorizationDataMuxer(new AuthorizationDataProviderProvider([]), new EventDispatcher())); $localDataEventSubscriber->_injectServices(new TestUserSession('testuser'), new AuthorizationDataMuxer(new AuthorizationDataProviderProvider([]), new EventDispatcher()));
$localDataEventSubscriber->setConfig(self::createConfig()); $localDataEventSubscriber->setConfig(self::createConfig());
...@@ -96,10 +96,66 @@ class LocalDataTest extends TestCase ...@@ -96,10 +96,66 @@ class LocalDataTest extends TestCase
} }
} }
public function testLocalDataQuery()
{
// 'attribute_1' has a configured source attribute 'src_attribute_1'.
// Post-condition: options contain the mapped attribute 'src_attribute_1' as a key with the given value 'value_1'.
$localDataAttributeName = 'attribute_1';
$options = [];
$options[LocalData::QUERY_PARAMETER_NAME] = $localDataAttributeName.':value_1';
$this->localDataEventDispatcher->onNewOperation($options);
$preEvent = new TestEntityPreEvent();
$this->localDataEventDispatcher->dispatch($preEvent);
$filters = $preEvent->getQueryParameterOut();
$this->assertArrayHasKey('src_attribute_1', $filters);
$this->assertEquals('value_1', $filters['src_attribute_1']);
}
public function testLocalDataQueryAttributeUnacknowledged()
{
// 'attribute_4' has no configured source attribute.
// Throw bad request error because no event subscriber acknowledged local query parameter 'attribute_4'.
$localDataAttributeName = 'attribute_4';
$options = [];
$options[LocalData::QUERY_PARAMETER_NAME] = $localDataAttributeName.':value_4';
try {
$this->localDataEventDispatcher->onNewOperation($options);
$preEvent = new TestEntityPreEvent();
$this->localDataEventDispatcher->dispatch($preEvent);
} catch (ApiError $exception) {
$this->assertEquals(Response::HTTP_BAD_REQUEST, $exception->getStatusCode());
}
}
public function testLocalDataQueryAccessDenied()
{
// authorization expression of attribute evaluates to false -> deny access
$localDataAttributeName = 'attribute_3';
$options = [];
$options[LocalData::QUERY_PARAMETER_NAME] = $localDataAttributeName.':value_1';
try {
$this->localDataEventDispatcher->onNewOperation($options);
$preEvent = new TestEntityPreEvent();
$this->localDataEventDispatcher->dispatch($preEvent);
} catch (ApiError $exception) {
$this->assertEquals(Response::HTTP_UNAUTHORIZED, $exception->getStatusCode());
}
}
private function getTestEntity(string $includeLocal, array $sourceData): TestEntity private function getTestEntity(string $includeLocal, array $sourceData): TestEntity
{ {
$testEntity = new TestEntity(); $testEntity = new TestEntity();
$options = [LocalData::INCLUDE_PARAMETER_NAME => $includeLocal];
$options = [];
$options[LocalData::INCLUDE_PARAMETER_NAME] = $includeLocal;
$this->localDataEventDispatcher->onNewOperation($options); $this->localDataEventDispatcher->onNewOperation($options);
$this->localDataEventDispatcher->dispatch(new TestEntityPostEvent($testEntity, $sourceData)); $this->localDataEventDispatcher->dispatch(new TestEntityPostEvent($testEntity, $sourceData));
......
...@@ -6,10 +6,13 @@ namespace Dbp\Relay\CoreBundle\Tests\LocalData; ...@@ -6,10 +6,13 @@ namespace Dbp\Relay\CoreBundle\Tests\LocalData;
use Dbp\Relay\CoreBundle\LocalData\AbstractLocalDataPostEventSubscriber; use Dbp\Relay\CoreBundle\LocalData\AbstractLocalDataPostEventSubscriber;
class TestEntityLocalDataPostEventSubscriber extends AbstractLocalDataPostEventSubscriber class TestEntityLocalDataEventSubscriber extends AbstractLocalDataPostEventSubscriber
{ {
public static function getSubscribedEventName(): string public static function getSubscribedEventNames(): array
{ {
return TestEntityPostEvent::class; return [
TestEntityPostEvent::class,
TestEntityPreEvent::class,
];
} }
} }
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Tests\LocalData;
use Dbp\Relay\CoreBundle\LocalData\LocalDataPreEvent;
class TestEntityPreEvent extends LocalDataPreEvent
{
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment