diff -u b/src/WebformAccessRulesManager.php b/src/WebformAccessRulesManager.php --- b/src/WebformAccessRulesManager.php +++ b/src/WebformAccessRulesManager.php @@ -132,37 +132,4 @@ /** - * {@inheritdoc} - */ - public function queryTagWebformSubmissionAccessAlter(AlterableInterface $query, AccountInterface $account, $op, array $webform_submission_tables) { - foreach ($this->entityTypeManager->getStorage('webform')->loadMultiple() as $webform) { - $access_rules = $this->getAccessRules($webform); - if ($this->checkAccessRules($op, $account, $access_rules) || $this->checkAccessRules($op . '_any', $account, $access_rules)) { - foreach ($webform_submission_tables as $table) { - $table['condition']->condition($table['alias'] . '.webform_id', $webform->id()); - } - } - elseif ($this->checkAccessRules($op . '_own', $account, $access_rules)) { - foreach ($webform_submission_tables as $table) { - /** @var \Drupal\Core\Database\Query\SelectInterface $query */ - $condition = $query->andConditionGroup(); - $condition->condition($table['alias'] . '.uid', $account->id()); - $condition->condition($table['alias'] . '.webform_id', $webform->id()); - $table['condition']->condition($condition); - } - } - - if ($this->cachePerUser($access_rules)) { - // Report to the top cache contexts. - $request = \Drupal::requestStack()->getCurrentRequest(); - $renderer = \Drupal::service('renderer'); - if ($request->isMethodCacheable() && $renderer->hasRenderContext()) { - $build = ['#cache' => ['contexts' => ['user']]]; - $renderer->render($build); - } - } - } - } - - /** * Retrieve a list of access rules from a webform. * @@ -178,7 +145,7 @@ * - permissions: (array) Array of permissions that should grant access to * this operation */ - protected function getAccessRules(WebformInterface $webform) { + public function getAccessRules(WebformInterface $webform) { return $webform->getAccessRules() + $this->getDefaultAccessRules(); } @@ -199,7 +166,7 @@ * @return bool * TRUE if access is allowed and FALSE is access is denied. */ - protected function checkAccessRules($operation, AccountInterface $account, array $access_rules) { + public function checkAccessRules($operation, AccountInterface $account, array $access_rules) { // Check administer access rule and grant full access to user. if ($this->checkAccessRule($access_rules['administer'], $account)) { return TRUE; @@ -254,7 +221,7 @@ * @return bool * TRUE if access rules should be cached per user. */ - protected function cachePerUser(array $access_rules) { + public function cachePerUser(array $access_rules) { foreach ($access_rules as $access_rule) { if (!empty($access_rule['users'])) { return TRUE; diff -u b/src/WebformAccessRulesManagerInterface.php b/src/WebformAccessRulesManagerInterface.php --- b/src/WebformAccessRulesManagerInterface.php +++ b/src/WebformAccessRulesManagerInterface.php @@ -40,6 +40,10 @@ */ public function checkWebformSubmissionAccess($operation, AccountInterface $account, WebformSubmissionInterface $webform_submission); + /****************************************************************************/ + // Get access rules methods. + /****************************************************************************/ + /** * Returns the webform default access rules. * @@ -50,4 +54,40 @@ /** + * Retrieve a list of access rules from a webform. + * + * @param \Drupal\webform\WebformInterface $webform + * Webform whose access rules to retrieve. + * + * @return array + * Associative array of access rules contained in the provided webform. Keys + * are operation names whereas values are sub arrays with the following + * structure: + * - roles: (array) Array of roles that should have access to this operation + * - users: (array) Array of UIDs that should have access to this operation + * - permissions: (array) Array of permissions that should grant access to + * this operation + */ + public function getAccessRules(WebformInterface $webform); + + /****************************************************************************/ + // Check access rules methods. + /****************************************************************************/ + + /** + * Check access for a given operation and set of access rules. + * + * @param string $operation + * Operation that is being requested. + * @param \Drupal\Core\Session\AccountInterface $account + * Account that is requesting access to the operation. + * @param array $access_rules + * A set of access rules to check against. + * + * @return bool + * TRUE if access is allowed and FALSE is access is denied. + */ + public function checkAccessRules($operation, AccountInterface $account, array $access_rules); + + /** * Collect metadata on known access rules. * @@ -68,23 +108,13 @@ /** - * Alter a DB query to enforce access rules. + * Determine if access rules should be cached per user. * - * @param \Drupal\Core\Database\Query\AlterableInterface $query - * DB query to alter. - * @param \Drupal\Core\Session\AccountInterface $account - * Account on whose behalf the query is executed - * @param string $op - * Operation to enforce. Most of the time it would be 'view', but it can be - * just about any operation in theory. - * @param array $webform_submission_tables - * Array of {webform_submission} tables contained within the $query. Each - * sub-array represents a single encounter of {webform_submission} table in - * the $query and has the following structure: - * - alias: (string) Alias of this occurrence of {webform_submission} table. - * - condition: (ConditionInterface) A condition group where necessary extra - * conditions should be added for this occurrence of {webform_submission} - * table. + * @param array $access_rules + * A set of access rules. + * + * @return bool + * TRUE if access rules should be cached per user. */ - public function queryTagWebformSubmissionAccessAlter(AlterableInterface $query, AccountInterface $account, $op, array $webform_submission_tables); + public function cachePerUser(array $access_rules); } diff -u b/webform.module b/webform.module --- b/webform.module +++ b/webform.module @@ -27,6 +27,7 @@ use Drupal\webform\Utility\WebformElementHelper; use Drupal\webform\Utility\WebformDialogHelper; use Drupal\webform\Utility\WebformOptionsHelper; +use Drupal\webform\Utility\WebformRendererHelper; use Drupal\webform\WebformInterface; use Drupal\webform\WebformSubmissionForm; @@ -714,84 +715,4 @@ /** - * Implements hook_query_TAG_alter(). - * - * This hook implementation adds webform submission access checks for the - * account stored in the 'account' meta-data (or current user if not provided), - * for an operation stored in the 'op' meta-data (or 'view' if not provided). - */ -function webform_query_webform_submission_access_alter(AlterableInterface $query) { - /** @var \Drupal\Core\Database\Query\SelectInterface $query */ - $op = $query->getMetaData('op') ?: 'view'; - $account = $query->getMetaData('account') ?: \Drupal::currentUser(); - - $entity_type = \Drupal::entityTypeManager()->getDefinition('webform_submission'); - - // Get webform submission tables which are used to build the alter query. - $webform_submission_tables = []; - foreach ($query->getTables() as $table) { - if (is_string($table['table']) && $table['table'] === $entity_type->getBaseTable()) { - $webform_submission_tables[] = [ - 'alias' => $table['alias'], - 'condition' => $query->orConditionGroup(), - ]; - } - } - - // If there are no webform submission tables then nothing needs to be altered. - if (empty($webform_submission_tables)) { - return; - } - - // If the user has administer access then exit. - if ($account->hasPermission('administer webform submission') - || $account->hasPermission('administer webform')) { - return; - } - - // Apply operation specific any and own permissions. - if (in_array($op, ['view', 'edit', 'delete'])) { - $permission_any = "$op any webform submission"; - $permission_own = "$op own webform submission"; - - // Make sure renderer is aware of the user.permissions cache contexts. - $request = \Drupal::requestStack()->getCurrentRequest(); - $renderer = \Drupal::service('renderer'); - if ($request->isMethodCacheable() && $renderer->hasRenderContext()) { - $build = ['#cache' => ['contexts' => ['user.permissions']]]; - $renderer->render($build); - } - - // If the user has any permission the query does not have to be altered. - if ($account->hasPermission($permission_any)) { - return; - } - - // If the user has own permission, then add the account id to all - // webform submission tables conditions. - if ($account->hasPermission($permission_own)) { - foreach ($webform_submission_tables as $table) { - $table['condition']->condition($table['alias'] . '.uid', $account->id()); - } - } - } - - // Alter query based on access rules. - /** @var \Drupal\webform\WebformAccessRulesManagerInterface $access_rules_manager */ - $access_rules_manager = \Drupal::service('webform.access_rules_manager'); - $access_rules_manager->queryTagWebformSubmissionAccessAlter($query, $account, $op, $webform_submission_tables); - - // Apply webform submission table conditions to query. - foreach ($webform_submission_tables as $table) { - // If a webform submission table does not have any conditions, - // we have to block access to the table. - if (count($table['condition']->conditions()) === 1) { - $table['condition']->where('1 = 0'); - } - - $query->condition($table['condition']); - } -} - -/** * Implements hook_webform_access_rules(). */ @@ -987,6 +908,10 @@ throw $exception; } +/******************************************************************************/ +// Query alter functions. +/******************************************************************************/ + /** * Implements hook_query_alter(). * @@ -1016,0 +942,103 @@ + +/** + * Implements hook_query_TAG_alter(). + * + * This hook implementation adds webform submission access checks for the + * account stored in the 'account' meta-data (or current user if not provided), + * for an operation stored in the 'op' meta-data (or 'view' if not provided). + */ +function webform_query_webform_submission_access_alter(AlterableInterface $query) { + /** @var \Drupal\Core\Database\Query\SelectInterface $query */ + $op = $query->getMetaData('op') ?: 'view'; + $account = $query->getMetaData('account') ?: \Drupal::currentUser(); + + $entity_type = \Drupal::entityTypeManager()->getDefinition('webform_submission'); + + // Get webform submission tables which are used to build the alter query. + $webform_submission_tables = []; + foreach ($query->getTables() as $table) { + if (is_string($table['table']) && $table['table'] === $entity_type->getBaseTable()) { + $webform_submission_tables[] = [ + 'alias' => $table['alias'], + 'condition' => $query->orConditionGroup(), + ]; + } + } + + // If there are no webform submission tables then nothing needs to be altered. + if (empty($webform_submission_tables)) { + return; + } + + // If the user has administer access then exit. + if ($account->hasPermission('administer webform submission') + || $account->hasPermission('administer webform')) { + return; + } + + // Apply operation specific any and own permissions. + if (in_array($op, ['view', 'edit', 'delete'])) { + $permission_any = "$op any webform submission"; + $permission_own = "$op own webform submission"; + + // Add user.permissions to renderer cache contexts. + WebformRendererHelper::addCacheContext(['user.permissions']); + + // If the user has any permission the query does not have to be altered. + if ($account->hasPermission($permission_any)) { + return; + } + + // If the user has own permission, then add the account id to all + // webform submission tables conditions. + if ($account->hasPermission($permission_own)) { + foreach ($webform_submission_tables as $table) { + $table['condition']->condition($table['alias'] . '.uid', $account->id()); + } + } + } + + // Alter query based on access rules. + /** @var \Drupal\webform\WebformAccessRulesManagerInterface $access_rules_manager */ + $access_rules_manager = \Drupal::service('webform.access_rules_manager'); + /** @var \Drupal\webform\WebformInterface[] $webforms */ + $webforms = Webform::loadMultiple(); + foreach ($webforms as $webform) { + $access_rules = $access_rules_manager->getAccessRules($webform); + // Check basic and any access rules and add webform id to all + // webform submission tables conditions. + if ($access_rules_manager->checkAccessRules($op, $account, $access_rules) + || $access_rules_manager->checkAccessRules($op . '_any', $account, $access_rules)) { + foreach ($webform_submission_tables as $table) { + $table['condition']->condition($table['alias'] . '.webform_id', $webform->id()); + } + } + // If the user has own access rules, then add the account id to all + // webform submission tables conditions. + elseif ($access_rules_manager->checkAccessRules($op . '_own', $account, $access_rules)) { + foreach ($webform_submission_tables as $table) { + /** @var \Drupal\Core\Database\Query\SelectInterface $query */ + $condition = $query->andConditionGroup(); + $condition->condition($table['alias'] . '.uid', $account->id()); + $condition->condition($table['alias'] . '.webform_id', $webform->id()); + $table['condition']->condition($condition); + } + } + + // For user specific access rules, add user to renderer cache contexts. + if ($access_rules_manager->cachePerUser($access_rules)) { + WebformRendererHelper::addCacheContext(['user']); + } + } + + // Apply webform submission table conditions to query. + foreach ($webform_submission_tables as $table) { + // If a webform submission table does not have any conditions, + // we have to block access to the table. + if (count($table['condition']->conditions()) === 1) { + $table['condition']->where('1 = 0'); + } + + $query->condition($table['condition']); + } +} only in patch2: unchanged: --- /dev/null +++ b/src/Utility/WebformRendererHelper.php @@ -0,0 +1,26 @@ +getCurrentRequest(); + $renderer = \Drupal::service('renderer'); + if ($request->isMethodCacheable() && $renderer->hasRenderContext()) { + $build = ['#cache' => ['contexts' => $contexts]]; + $renderer->render($build); + } + } + +}