diff --git a/src/Authorization/AbstractAuthorizationService.php b/src/Authorization/AbstractAuthorizationService.php
index 5daddb85610fc936d88d0669cb975acb3bb2529a..0e7270da9b7d62c4256268ef0b3de4e7d2f54dd3 100644
--- a/src/Authorization/AbstractAuthorizationService.php
+++ b/src/Authorization/AbstractAuthorizationService.php
@@ -6,12 +6,13 @@ namespace Dbp\Relay\CoreBundle\Authorization;
 
 use Dbp\Relay\CoreBundle\API\UserSessionInterface;
 use Dbp\Relay\CoreBundle\Exception\ApiError;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 use Symfony\Component\HttpFoundation\Response;
 
 abstract class AbstractAuthorizationService
 {
-    public const RIGHTS_CONFIG_ATTRIBUTE = AuthorizationExpressionChecker::RIGHTS_CONFIG_ATTRIBUTE;
-    public const ATTRIBUTES_CONFIG_ATTRIBUTE = AuthorizationExpressionChecker::ATTRIBUTES_CONFIG_ATTRIBUTE;
+    private const AUTHORIZATION_ROOT_CONFIG_NODE = 'authorization';
 
     /** @var AuthorizationExpressionChecker */
     private $userAuthorizationChecker;
@@ -19,6 +20,7 @@ abstract class AbstractAuthorizationService
     /** @var AuthorizationUser */
     private $currentAuthorizationUser;
 
+    /** @var array|null */
     private $config;
 
     /**
@@ -27,13 +29,13 @@ abstract class AbstractAuthorizationService
     public function _injectServices(UserSessionInterface $userSession, AuthorizationDataMuxer $mux)
     {
         $this->userAuthorizationChecker = new AuthorizationExpressionChecker($mux);
-        $this->currentAuthorizationUser = new AuthorizationUser($userSession->getUserIdentifier(), $this->userAuthorizationChecker);
+        $this->currentAuthorizationUser = new AuthorizationUser($userSession, $this->userAuthorizationChecker);
         $this->updateConfig();
     }
 
     public function setConfig(array $config)
     {
-        $this->config = $config;
+        $this->config = $config[self::AUTHORIZATION_ROOT_CONFIG_NODE] ?? [];
         $this->updateConfig();
     }
 
@@ -76,8 +78,6 @@ abstract class AbstractAuthorizationService
 
     private function getAttributeInternal(string $attributeName, $defaultValue = null)
     {
-        $this->userAuthorizationChecker->init();
-
         return $this->userAuthorizationChecker->evalAttributeExpression($this->currentAuthorizationUser, $attributeName, $defaultValue);
     }
 
@@ -86,8 +86,45 @@ abstract class AbstractAuthorizationService
      */
     private function isGrantedInternal(string $rightName, $subject = null): bool
     {
-        $this->userAuthorizationChecker->init();
-
         return $this->userAuthorizationChecker->isGranted($this->currentAuthorizationUser, $rightName, $subject);
     }
+
+    /**
+     * @param array $rights     the mapping between right names and right default expressions
+     * @param array $attributes the mapping between attribute names and attribute default expressions
+     */
+    public static function getAuthorizationConfigNodeDefinition(array $rights = [], array $attributes = []): NodeDefinition
+    {
+        $treeBuilder = new TreeBuilder(self::AUTHORIZATION_ROOT_CONFIG_NODE);
+
+        $rightsNodeChildBuilder = $treeBuilder->getRootNode()->children()->arrayNode(AuthorizationExpressionChecker::RIGHTS_CONFIG_NODE)
+            ->addDefaultsIfNotSet()
+            ->children();
+        foreach ($rights as $rightName => $defaultExpression) {
+            $rightsNodeChildBuilder->scalarNode($rightName)
+                ->defaultValue($defaultExpression ?? 'false')
+                ->end();
+        }
+
+        $attributesNodeChildBuilder = $treeBuilder->getRootNode()->children()->arrayNode(AuthorizationExpressionChecker::ATTRIBUTES_CONFIG_NODE)
+            ->addDefaultsIfNotSet()
+            ->children();
+        foreach ($attributes as $attributeName => $defaultExpression) {
+            $attributesNodeChildBuilder->scalarNode($attributeName)
+                ->defaultValue($defaultExpression ?? 'null')
+                ->end();
+        }
+
+        return $treeBuilder->getRootNode();
+    }
+
+    public static function createConfig(array $rightExpressions = [], array $attributeExpressions = []): array
+    {
+        return [
+            AbstractAuthorizationService::AUTHORIZATION_ROOT_CONFIG_NODE => [
+                AuthorizationExpressionChecker::RIGHTS_CONFIG_NODE => $rightExpressions,
+                AuthorizationExpressionChecker::ATTRIBUTES_CONFIG_NODE => $attributeExpressions,
+            ],
+        ];
+    }
 }
diff --git a/src/Authorization/AuthorizationExpressionChecker.php b/src/Authorization/AuthorizationExpressionChecker.php
index 545edd82523ace570baec86420d7dfd0e3741d04..e0d20eb25a8af1caf695635c7b13f1d818234fbf 100644
--- a/src/Authorization/AuthorizationExpressionChecker.php
+++ b/src/Authorization/AuthorizationExpressionChecker.php
@@ -11,8 +11,8 @@ use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionLanguage;
  */
 class AuthorizationExpressionChecker
 {
-    public const RIGHTS_CONFIG_ATTRIBUTE = 'rights';
-    public const ATTRIBUTES_CONFIG_ATTRIBUTE = 'attributes';
+    public const RIGHTS_CONFIG_NODE = 'rights';
+    public const ATTRIBUTES_CONFIG_NODE = 'attributes';
 
     private const MAX_NUM_CALLS = 16;
 
@@ -25,9 +25,6 @@ class AuthorizationExpressionChecker
     /** @var array */
     private $attributeExpressions;
 
-    /** @var int */
-    private $callCounter;
-
     /** @var AuthorizationDataMuxer */
     private $dataMux;
 
@@ -40,7 +37,6 @@ class AuthorizationExpressionChecker
     public function __construct(AuthorizationDataMuxer $dataMux)
     {
         $this->expressionLanguage = new ExpressionLanguage();
-
         $this->rightExpressions = [];
         $this->attributeExpressions = [];
         $this->dataMux = $dataMux;
@@ -50,13 +46,8 @@ class AuthorizationExpressionChecker
 
     public function setConfig(array $config)
     {
-        $this->loadExpressions($config[self::RIGHTS_CONFIG_ATTRIBUTE] ?? [], $this->rightExpressions);
-        $this->loadExpressions($config[self::ATTRIBUTES_CONFIG_ATTRIBUTE] ?? [], $this->attributeExpressions);
-    }
-
-    public function init()
-    {
-        $this->callCounter = 0;
+        $this->loadExpressions($config[self::RIGHTS_CONFIG_NODE] ?? [], $this->rightExpressions);
+        $this->loadExpressions($config[self::ATTRIBUTES_CONFIG_NODE] ?? [], $this->attributeExpressions);
     }
 
     /**
diff --git a/src/Authorization/AuthorizationUser.php b/src/Authorization/AuthorizationUser.php
index 8a6a1361706f2517bd867eaaf1d9b27b75c0fc69..bfc17f173b5730063eb78ea29a6a461a1a97e5d2 100644
--- a/src/Authorization/AuthorizationUser.php
+++ b/src/Authorization/AuthorizationUser.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Dbp\Relay\CoreBundle\Authorization;
 
+use Dbp\Relay\CoreBundle\API\UserSessionInterface;
+
 /**
  * Provides the user interface available within privilege expressions.
  */
@@ -12,20 +14,18 @@ class AuthorizationUser
     /** @var AuthorizationExpressionChecker */
     private $authorizationChecker;
 
-    /**
-     * @var string|null
-     */
-    private $identifier;
+    /** @var UserSessionInterface */
+    private $userSession;
 
-    public function __construct(?string $identifier, AuthorizationExpressionChecker $authorizationChecker)
+    public function __construct(UserSessionInterface $userSession, AuthorizationExpressionChecker $authorizationChecker)
     {
+        $this->userSession = $userSession;
         $this->authorizationChecker = $authorizationChecker;
-        $this->identifier = $identifier;
     }
 
     public function getIdentifier(): ?string
     {
-        return $this->identifier;
+        return $this->userSession->getUserIdentifier();
     }
 
     /**
diff --git a/src/Authorization/ExpressionLanguage/ExpressionLanguage.php b/src/Authorization/ExpressionLanguage/ExpressionLanguage.php
index a997ab81e79679de981405f1c2921d713ed13b33..49baa180195b76554f700e06ea49a9f0773ab75e 100644
--- a/src/Authorization/ExpressionLanguage/ExpressionLanguage.php
+++ b/src/Authorization/ExpressionLanguage/ExpressionLanguage.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage;
 
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\ArrayExpressionFunctionProvider;
 use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\FilterExpressionFunctionProvider;
 use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\MapExpressionFunctionProvider;
 use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\PhpArrayExpressionFunctionProvider;
@@ -22,6 +23,7 @@ class ExpressionLanguage extends SymfonyExpressionLanguage
             new PhpArrayExpressionFunctionProvider(),
             new PhpNumericExpressionFunctionProvider(),
             new PhpStringExpressionFunctionProvider(),
+            new ArrayExpressionFunctionProvider(),
         ], $providers);
 
         parent::__construct($cache, $providers);
diff --git a/src/LocalData/AbstractLocalDataPostEventSubscriber.php b/src/LocalData/AbstractLocalDataPostEventSubscriber.php
index 6d6115c591104235c976e0fa55f2995369af2313..0879e34f4a70f04dda2aab13732fadf692f46f98 100644
--- a/src/LocalData/AbstractLocalDataPostEventSubscriber.php
+++ b/src/LocalData/AbstractLocalDataPostEventSubscriber.php
@@ -5,17 +5,38 @@ declare(strict_types=1);
 namespace Dbp\Relay\CoreBundle\LocalData;
 
 use Dbp\Relay\CoreBundle\Authorization\AbstractAuthorizationService;
+use Dbp\Relay\CoreBundle\Exception\ApiError;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Response;
 
+/*
+ * Abstract implementation of a configurable local data provider post event subscriber.
+ * It is intended to be derived by local data aware entity post event subscribers.
+ * A mapping between source attribute and local data attribute,
+ * and default values for the attributes can be specified by means of the deriving event subscriber's bundle config.
+ * If no default value is specified, an exception is thrown in the case the mapped source attribute is not found.
+ */
 abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizationService implements EventSubscriberInterface
 {
-    public const CONFIG_NODE = 'local_data_mapping';
-    public const SOURCE_ATTRIBUTE_KEY = 'source_attribute';
-    public const LOCAL_DATA_ATTRIBUTE_KEY = 'local_data_attribute';
-    public const AUTHORIZATION_EXPRESSION_KEY = 'authorization_expression';
+    protected const ROOT_CONFIG_NODE = 'local_data_mapping';
+    protected const SOURCE_ATTRIBUTE_CONFIG_NODE = 'source_attribute';
+    protected const LOCAL_DATA_ATTRIBUTE_CONFIG_NODE = 'local_data_attribute';
+    protected const AUTHORIZATION_EXPRESSION_CONFIG_NODE = 'authorization_expression';
+    protected const DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE = 'default_value';
+    protected const DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE = 'default_values';
 
-    /** @var string[] */
+    private const SOURCE_ATTRIBUTE_KEY = 'source';
+    private const DEFAULT_VALUE_KEY = 'default';
+
+    /*
+     * WORKAROUND: could not find a way to determine whether a Symfony config array node was NOT specified since it provides an empty
+     * array in case it is not specified. So I use an array value as default which does not seem to be reproducible by the configurator.
+     */
+    private const ARRAY_VALUE_NOT_SPECIFIED = [null => null];
+
+    /** @var array */
     private $attributeMapping;
 
     public function __construct()
@@ -25,21 +46,34 @@ abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizatio
 
     public function setConfig(array $config)
     {
-        $configNode = $config[self::CONFIG_NODE] ?? [];
+        $configNode = $config[self::ROOT_CONFIG_NODE] ?? [];
+        $rightExpressions = [];
 
-        $rights = [];
         foreach ($configNode as $configMappingEntry) {
-            if (isset($this->attributeMapping[$configMappingEntry[self::LOCAL_DATA_ATTRIBUTE_KEY]])) {
-                throw new \RuntimeException(sprintf('multiple mapping entries for local data attribute %s', $configMappingEntry[self::LOCAL_DATA_ATTRIBUTE_KEY]));
+            $localDataAttributeName = $configMappingEntry[self::LOCAL_DATA_ATTRIBUTE_CONFIG_NODE];
+
+            if (isset($this->attributeMapping[$localDataAttributeName])) {
+                throw new \RuntimeException(sprintf('multiple mapping entries for local data attribute %s', $localDataAttributeName));
+            }
+
+            $attributeMapEntry = [];
+            $attributeMapEntry[self::SOURCE_ATTRIBUTE_KEY] = $configMappingEntry[self::SOURCE_ATTRIBUTE_CONFIG_NODE];
+
+            $defaultValue = $configMappingEntry[self::DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE] ??
+                ((($defaultArray = $configMappingEntry[self::DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE]) !== self::ARRAY_VALUE_NOT_SPECIFIED) ? $defaultArray : null);
+            if ($defaultValue !== null) {
+                $attributeMapEntry[self::DEFAULT_VALUE_KEY] = $defaultValue;
             }
-            $this->attributeMapping[$configMappingEntry[self::LOCAL_DATA_ATTRIBUTE_KEY]] = $configMappingEntry[self::SOURCE_ATTRIBUTE_KEY];
+
+            $this->attributeMapping[$localDataAttributeName] = $attributeMapEntry;
+
             // the name of the local data attribute is used as name for the right to view that attribute
             // the attribute is visible false by default
-            $rights[$configMappingEntry[self::LOCAL_DATA_ATTRIBUTE_KEY]] = $configMappingEntry[self::AUTHORIZATION_EXPRESSION_KEY] ?? 'false';
+            $rightExpressions[$localDataAttributeName] = $configMappingEntry[self::AUTHORIZATION_EXPRESSION_CONFIG_NODE] ?? 'false';
         }
 
-        if (!empty($rights)) {
-            parent::setConfig([AbstractAuthorizationService::RIGHTS_CONFIG_ATTRIBUTE => $rights]);
+        if (!empty($rightExpressions)) {
+            parent::setConfig(parent::createConfig($rightExpressions));
         }
     }
 
@@ -57,29 +91,51 @@ abstract class AbstractLocalDataPostEventSubscriber extends AbstractAuthorizatio
     {
         $sourceData = $postEvent->getSourceData();
 
-        foreach ($this->attributeMapping as $localDataAttributeName => $sourceAttributeName) {
-            if ($this->isGranted($localDataAttributeName)) {
-                if (($sourceAttributeValue = $sourceData[$sourceAttributeName] ?? null) !== null) {
-                    $postEvent->trySetLocalDataAttribute($localDataAttributeName, $sourceAttributeValue);
+        foreach ($this->attributeMapping as $localDataAttributeName => $attributeMapEntry) {
+            if ($postEvent->isLocalDataAttributeRequested($localDataAttributeName)) {
+                if (!$this->isGranted($localDataAttributeName)) {
+                    throw ApiError::withDetails(Response::HTTP_UNAUTHORIZED, sprintf('access to local data attribute \'%s\' denied', $localDataAttributeName));
+                }
+
+                $sourceAttributeName = $attributeMapEntry[self::SOURCE_ATTRIBUTE_KEY];
+                $attributeValue = $sourceData[$sourceAttributeName] ?? $attributeMapEntry[self::DEFAULT_VALUE_KEY] ?? null;
+                if ($attributeValue !== null) {
+                    $postEvent->setLocalDataAttribute($localDataAttributeName, $attributeValue);
                 } else {
-                    throw new \RuntimeException(sprintf('attribute \'%s\' not available in source data', $sourceAttributeName));
+                    throw ApiError::withDetails(Response::HTTP_INTERNAL_SERVER_ERROR, sprintf('attribute \'%s\' not available in source data', $sourceAttributeName));
                 }
             }
         }
     }
 
-    public static function getConfigNode()
+    /**
+     * @deprecated Use getLocalDataMappingConfigNodeDefinition instead
+     */
+    public static function getConfigNode(): NodeDefinition
     {
-        $treeBuilder = new TreeBuilder(self::CONFIG_NODE);
+        return self::getLocalDataMappingConfigNodeDefinition();
+    }
+
+    public static function getLocalDataMappingConfigNodeDefinition(): NodeDefinition
+    {
+        $treeBuilder = new TreeBuilder(self::ROOT_CONFIG_NODE);
 
         return $treeBuilder->getRootNode()
             ->arrayPrototype()
                 ->children()
-                    ->scalarNode(self::SOURCE_ATTRIBUTE_KEY)->end()
-                    ->scalarNode(self::LOCAL_DATA_ATTRIBUTE_KEY)->end()
-                    ->scalarNode(self::AUTHORIZATION_EXPRESSION_KEY)
+                    ->scalarNode(self::SOURCE_ATTRIBUTE_CONFIG_NODE)->end()
+                    ->scalarNode(self::LOCAL_DATA_ATTRIBUTE_CONFIG_NODE)->end()
+                    ->scalarNode(self::AUTHORIZATION_EXPRESSION_CONFIG_NODE)
                         ->defaultValue('false')
                     ->end()
+                    ->scalarNode(self::DEFAULT_VALUE_ATTRIBUTE_CONFIG_NODE)
+                        ->info('The default value for scalar (non-array) attributes. If none is specified, an exception is thrown in the case the source attribute is not found.')
+                    ->end()
+                    ->arrayNode(self::DEFAULT_VALUES_ATTRIBUTE_CONFIG_NODE)
+                        ->defaultValue(self::ARRAY_VALUE_NOT_SPECIFIED)
+                        ->info('The default value for array type attributes. If none is specified, an exception is thrown in the case the source attribute is not found.')
+                        ->scalarPrototype()->end()
+                    ->end()
                 ->end()
             ->end()
         ;