diff --git a/src/LocalData/AbstractLocalDataEventSubscriber.php b/src/LocalData/AbstractLocalDataEventSubscriber.php
index 20a7b600cc9e00db070520f28d34ddefef11a9ff..e35e035e936c97809c081582984814521e9f772b 100644
--- a/src/LocalData/AbstractLocalDataEventSubscriber.php
+++ b/src/LocalData/AbstractLocalDataEventSubscriber.php
@@ -97,30 +97,27 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer
     public function onEvent(Event $event)
     {
         if ($event instanceof LocalDataPreEvent) {
-            $queryParametersOut = [];
-
-            // matriculationNumber:0011675
-            foreach ($event->getQueryParameters() as $queryParameterName => $queryParameterValue) {
-                if (($attributeMapEntry = $this->attributeMapping[$queryParameterName] ?? null) !== null) {
+            foreach ($event->getPendingQueryParametersIn() as $localDataAttributeName => $localDataAttributeValue) {
+                if (($attributeMapEntry = $this->attributeMapping[$localDataAttributeName] ?? null) !== null) {
+                    if (!$this->isGranted($localDataAttributeName)) {
+                        throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName));
+                    }
                     $sourceAttributeName = $attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY][0];
-                    $queryParametersOut[$sourceAttributeName] = $queryParameterValue;
+                    $event->addQueryParameterOut($sourceAttributeName, $localDataAttributeValue);
+                    $event->acknowledgeQueryParameterIn($localDataAttributeName);
                 }
             }
 
-            $event->setQueryParameters($queryParametersOut);
-            $this->onPre($event);
+            $this->onPreEvent($event);
         } elseif ($event instanceof LocalDataPostEvent) {
-            $sourceData = $event->getSourceData();
-
-            foreach ($this->attributeMapping as $localDataAttributeName => $attributeMapEntry) {
-                if ($event->isLocalDataAttributeRequested($localDataAttributeName)) {
+            foreach ($event->getPendingRequestedAttributes() as $localDataAttributeName) {
+                if (($attributeMapEntry = $this->attributeMapping[$localDataAttributeName] ?? null) !== null) {
                     if (!$this->isGranted($localDataAttributeName)) {
                         throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName));
                     }
-
                     $attributeValue = null;
                     foreach ($attributeMapEntry[self::SOURCE_ATTRIBUTES_KEY] as $sourceAttributeName) {
-                        if (($value = $sourceData[$sourceAttributeName] ?? null) !== null) {
+                        if (($value = $event->getSourceData()[$sourceAttributeName] ?? null) !== null) {
                             $attributeValue = $value;
                             break;
                         }
@@ -134,7 +131,7 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer
                     }
                 }
             }
-            $this->onPost($event);
+            $this->onPostEvent($event);
         }
     }
 
@@ -182,11 +179,11 @@ abstract class AbstractLocalDataEventSubscriber extends AbstractAuthorizationSer
         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)
     {
     }
 }
diff --git a/src/LocalData/LocalDataEventDispatcher.php b/src/LocalData/LocalDataEventDispatcher.php
index 0e60a3dc06d5dd4ad80eab1c85e9b7335383d78f..c606aade965c66cb5696046c6359a37669d65695 100644
--- a/src/LocalData/LocalDataEventDispatcher.php
+++ b/src/LocalData/LocalDataEventDispatcher.php
@@ -83,15 +83,20 @@ class LocalDataEventDispatcher
     public function dispatch(Event $event, string $eventName = null): void
     {
         if ($event instanceof LocalDataPreEvent) {
-            $event->setQueryParameters($this->queryParameters);
+            $event->initQueryParametersIn($this->queryParameters);
             $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) {
             $event->setRequestedAttributes($this->requestedAttributes);
             $this->eventDispatcher->dispatch($event, $eventName);
 
-            $remainingLocalDataAttributes = $event->getRemainingRequestedAttributes();
-            if (!empty($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(', ', $remainingLocalDataAttributes)));
+            $pendingAttributes = $event->getPendingRequestedAttributes();
+            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(', ', $pendingAttributes)));
             }
         } else {
             $this->eventDispatcher->dispatch($event, $eventName);
diff --git a/src/LocalData/LocalDataPostEvent.php b/src/LocalData/LocalDataPostEvent.php
index a8b8c9dc1615b38f8296a3f5d61c0d6c2a27b131..c0f2a5f4e8398407c80619be5bdc9813ffe5806f 100644
--- a/src/LocalData/LocalDataPostEvent.php
+++ b/src/LocalData/LocalDataPostEvent.php
@@ -53,7 +53,7 @@ class LocalDataPostEvent extends Event implements LoggerAwareInterface
      *
      * @retrun string[]
      */
-    public function getRemainingRequestedAttributes(): array
+    public function getPendingRequestedAttributes(): array
     {
         return $this->requestedAttributes;
     }
@@ -103,11 +103,11 @@ class LocalDataPostEvent extends Event implements LoggerAwareInterface
      *
      * @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);
         if ($arrayKey === false) {
-            if ($warnfNotFound) {
+            if ($warnIfNotFound) {
                 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))));
                 }
diff --git a/src/LocalData/LocalDataPreEvent.php b/src/LocalData/LocalDataPreEvent.php
index c3ac8a5380d5569222782e6f70be4c86d3f309ee..633ca7dc2ad06026066afe599d5be286cf68e4dd 100644
--- a/src/LocalData/LocalDataPreEvent.php
+++ b/src/LocalData/LocalDataPreEvent.php
@@ -8,23 +8,40 @@ use Symfony\Contracts\EventDispatcher\Event;
 
 class LocalDataPreEvent extends Event
 {
-    public const NAME = 'dbp.relay.relay_core.local_data_aware_event.pre';
+    /** @var string[] */
+    private $queryParametersIn;
 
-    /** @var array */
-    private $queryParameters;
+    /** @var string[] */
+    private $queryParametersOut;
 
     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;
     }
 }
diff --git a/tests/LocalData/LocalDataTest.php b/tests/LocalData/LocalDataTest.php
index d41d1cef341b26714699e200d9986c92ca19c4cd..dd51cbf0009aba4b72f88c089038324b005accdf 100644
--- a/tests/LocalData/LocalDataTest.php
+++ b/tests/LocalData/LocalDataTest.php
@@ -19,14 +19,14 @@ class LocalDataTest extends TestCase
     /** @var LocalDataEventDispatcher */
     private $localDataEventDispatcher;
 
-    /** @var TestEntityLocalDataPostEventSubscriber */
+    /** @var TestEntityLocalDataEventSubscriber */
     private $localDataEventSubscriber;
 
     protected function setUp(): void
     {
         parent::setUp();
 
-        $localDataEventSubscriber = new TestEntityLocalDataPostEventSubscriber();
+        $localDataEventSubscriber = new TestEntityLocalDataEventSubscriber();
         $localDataEventSubscriber->_injectServices(new TestUserSession('testuser'), new AuthorizationDataMuxer(new AuthorizationDataProviderProvider([]), new EventDispatcher()));
         $localDataEventSubscriber->setConfig(self::createConfig());
 
@@ -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
     {
         $testEntity = new TestEntity();
-        $options = [LocalData::INCLUDE_PARAMETER_NAME => $includeLocal];
+
+        $options = [];
+        $options[LocalData::INCLUDE_PARAMETER_NAME] = $includeLocal;
+
         $this->localDataEventDispatcher->onNewOperation($options);
         $this->localDataEventDispatcher->dispatch(new TestEntityPostEvent($testEntity, $sourceData));
 
diff --git a/tests/LocalData/TestEntityLocalDataEventSubscriber.php b/tests/LocalData/TestEntityLocalDataEventSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..a85a1d84d6a154592e4147fb2097abc1753c36e8
--- /dev/null
+++ b/tests/LocalData/TestEntityLocalDataEventSubscriber.php
@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Tests\LocalData;
+
+use Dbp\Relay\CoreBundle\LocalData\AbstractLocalDataPostEventSubscriber;
+
+class TestEntityLocalDataEventSubscriber extends AbstractLocalDataPostEventSubscriber
+{
+    public static function getSubscribedEventNames(): array
+    {
+        return [
+            TestEntityPostEvent::class,
+            TestEntityPreEvent::class,
+            ];
+    }
+}
diff --git a/tests/LocalData/TestEntityLocalDataPostEventSubscriber.php b/tests/LocalData/TestEntityLocalDataPostEventSubscriber.php
deleted file mode 100644
index 878c51c408e28565d3e9abb2c660cdb63896805e..0000000000000000000000000000000000000000
--- a/tests/LocalData/TestEntityLocalDataPostEventSubscriber.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Dbp\Relay\CoreBundle\Tests\LocalData;
-
-use Dbp\Relay\CoreBundle\LocalData\AbstractLocalDataPostEventSubscriber;
-
-class TestEntityLocalDataPostEventSubscriber extends AbstractLocalDataPostEventSubscriber
-{
-    public static function getSubscribedEventName(): string
-    {
-        return TestEntityPostEvent::class;
-    }
-}
diff --git a/tests/LocalData/TestEntityPreEvent.php b/tests/LocalData/TestEntityPreEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..da717aca123c70a0b3e9cee18fa71de6c7d1d39d
--- /dev/null
+++ b/tests/LocalData/TestEntityPreEvent.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Tests\LocalData;
+
+use Dbp\Relay\CoreBundle\LocalData\LocalDataPreEvent;
+
+class TestEntityPreEvent extends LocalDataPreEvent
+{
+}