diff options
Diffstat (limited to 'core/modules/jsonapi')
-rw-r--r-- | core/modules/jsonapi/jsonapi.api.php | 17 | ||||
-rw-r--r-- | core/modules/jsonapi/jsonapi.module | 16 | ||||
-rw-r--r-- | core/modules/jsonapi/src/Access/TemporaryQueryGuard.php | 31 | ||||
-rw-r--r-- | core/modules/jsonapi/src/Hook/JsonapiHooks.php | 59 | ||||
-rw-r--r-- | core/modules/jsonapi/src/JsonApiFilter.php | 77 |
5 files changed, 148 insertions, 52 deletions
diff --git a/core/modules/jsonapi/jsonapi.api.php b/core/modules/jsonapi/jsonapi.api.php index 5b2f2002d25c..ca8c5ae993fb 100644 --- a/core/modules/jsonapi/jsonapi.api.php +++ b/core/modules/jsonapi/jsonapi.api.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Access\AccessResult; +use Drupal\jsonapi\JsonApiFilter; /** * @defgroup jsonapi_architecture JSON:API Architecture @@ -263,10 +264,10 @@ use Drupal\Core\Access\AccessResult; * viewable. * - AccessResult::neutral() if the implementation has no opinion. * The supported subsets for which an access result may be returned are: - * - JSONAPI_FILTER_AMONG_ALL: all entities of the given type. - * - JSONAPI_FILTER_AMONG_PUBLISHED: all published entities of the given type. - * - JSONAPI_FILTER_AMONG_ENABLED: all enabled entities of the given type. - * - JSONAPI_FILTER_AMONG_OWN: all entities of the given type owned by the + * - JsonApiFilter::AMONG_ALL: all entities of the given type. + * - JsonApiFilter::AMONG_PUBLISHED: all published entities of the given type. + * - JsonApiFilter::AMONG_ENABLED: all enabled entities of the given type. + * - JsonApiFilter::AMONG_OWN: all entities of the given type owned by the * user for whom access is being checked. * See the documentation of the above constants for more information about * each subset. @@ -278,7 +279,7 @@ function hook_jsonapi_entity_filter_access(EntityTypeInterface $entity_type, Acc // by all entities of that type to users with that permission. if ($admin_permission = $entity_type->getAdminPermission()) { return ([ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, $admin_permission), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, $admin_permission), ]); } } @@ -305,9 +306,9 @@ function hook_jsonapi_entity_filter_access(EntityTypeInterface $entity_type, Acc */ function hook_jsonapi_ENTITY_TYPE_filter_access(EntityTypeInterface $entity_type, AccountInterface $account): array { return ([ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer llamas'), - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'view all published llamas'), - JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermissions($account, ['view own published llamas', 'view own unpublished llamas'], 'AND'), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer llamas'), + JsonApiFilter::AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'view all published llamas'), + JsonApiFilter::AMONG_OWN => AccessResult::allowedIfHasPermissions($account, ['view own published llamas', 'view own unpublished llamas'], 'AND'), ]); } diff --git a/core/modules/jsonapi/jsonapi.module b/core/modules/jsonapi/jsonapi.module index 69414af650b9..c512575305a4 100644 --- a/core/modules/jsonapi/jsonapi.module +++ b/core/modules/jsonapi/jsonapi.module @@ -11,6 +11,10 @@ * regardless of whether they are published or enabled, and regardless of * their owner. * + * @deprecated in drupal:11.3.0 and is removed from drupal:13.0.0. Use + * \Drupal\jsonapi\JsonApiFilter::AMONG_ALL instead. + * + * @see https://www.drupal.org/node/3495601 * @see hook_jsonapi_entity_filter_access() * @see hook_jsonapi_ENTITY_TYPE_filter_access() */ @@ -25,6 +29,10 @@ const JSONAPI_FILTER_AMONG_ALL = 'filter_among_all'; * This is used when an entity type has a "published" entity key and there's a * query condition for the value of that equaling 1. * + * @deprecated in drupal:11.3.0 and is removed from drupal:13.0.0. Use + * \Drupal\jsonapi\JsonApiFilter::AMONG_PUBLISHED instead. + * + * @see https://www.drupal.org/node/3495601 * @see hook_jsonapi_entity_filter_access() * @see hook_jsonapi_ENTITY_TYPE_filter_access() */ @@ -42,6 +50,10 @@ const JSONAPI_FILTER_AMONG_PUBLISHED = 'filter_among_published'; * For the User entity type, which does not have a "status" entity key, the * "status" field is used. * + * @deprecated in drupal:11.3.0 and is removed from drupal:13.0.0. Use + * \Drupal\jsonapi\JsonApiFilter::AMONG_ENABLED instead. + * + * @see https://www.drupal.org/node/3495601 * @see hook_jsonapi_entity_filter_access() * @see hook_jsonapi_ENTITY_TYPE_filter_access() */ @@ -64,6 +76,10 @@ const JSONAPI_FILTER_AMONG_ENABLED = 'filter_among_enabled'; * - The entity type has an "owner" entity key. * - There's a filter/query condition for the value equal to the user's ID. * + * @deprecated in drupal:11.3.0 and is removed from drupal:13.0.0. Use + * \Drupal\jsonapi\JsonApiFilter::AMONG_OWN instead. + * + * @see https://www.drupal.org/node/3495601 * @see hook_jsonapi_entity_filter_access() * @see hook_jsonapi_ENTITY_TYPE_filter_access() */ diff --git a/core/modules/jsonapi/src/Access/TemporaryQueryGuard.php b/core/modules/jsonapi/src/Access/TemporaryQueryGuard.php index d59dca4ec03a..2888fbbec77c 100644 --- a/core/modules/jsonapi/src/Access/TemporaryQueryGuard.php +++ b/core/modules/jsonapi/src/Access/TemporaryQueryGuard.php @@ -12,6 +12,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\DataReferenceDefinitionInterface; +use Drupal\jsonapi\JsonApiFilter; use Drupal\jsonapi\Query\EntityCondition; use Drupal\jsonapi\Query\EntityConditionGroup; use Drupal\jsonapi\Query\Filter; @@ -323,12 +324,12 @@ class TemporaryQueryGuard { } /** - * Gets an access condition for the allowed JSONAPI_FILTER_AMONG_* subsets. + * Gets an access condition for the allowed JsonApiFilter::AMONG_* subsets. * - * If access is allowed for the JSONAPI_FILTER_AMONG_ALL subset, then no + * If access is allowed for the JsonApiFilter::AMONG_ALL subset, then no * conditions are returned. Otherwise, if access is allowed for - * JSONAPI_FILTER_AMONG_PUBLISHED, JSONAPI_FILTER_AMONG_ENABLED, or - * JSONAPI_FILTER_AMONG_OWN, then a condition group is returned for the union + * JsonApiFilter::AMONG_PUBLISHED, JsonApiFilter::AMONG_ENABLED, or + * JsonApiFilter::AMONG_OWN, then a condition group is returned for the union * of allowed subsets. If no subsets are allowed, then static::alwaysFalse() * is returned. * @@ -344,12 +345,12 @@ class TemporaryQueryGuard { * secure an entity query. */ protected static function getAccessConditionForKnownSubsets(EntityTypeInterface $entity_type, AccountInterface $account, CacheableMetadata $cacheability) { - // Get the combined access results for each JSONAPI_FILTER_AMONG_* subset. + // Get the combined access results for each JsonApiFilter::AMONG_* subset. $access_results = static::getAccessResultsFromEntityFilterHook($entity_type, $account); // No conditions are needed if access is allowed for all entities. - $cacheability->addCacheableDependency($access_results[JSONAPI_FILTER_AMONG_ALL]); - if ($access_results[JSONAPI_FILTER_AMONG_ALL]->isAllowed()) { + $cacheability->addCacheableDependency($access_results[JsonApiFilter::AMONG_ALL]); + if ($access_results[JsonApiFilter::AMONG_ALL]->isAllowed()) { return NULL; } @@ -363,7 +364,7 @@ class TemporaryQueryGuard { // The "published" subset. $published_field_name = $entity_type->getKey('published'); if ($published_field_name) { - $access_result = $access_results[JSONAPI_FILTER_AMONG_PUBLISHED]; + $access_result = $access_results[JsonApiFilter::AMONG_PUBLISHED]; $cacheability->addCacheableDependency($access_result); if ($access_result->isAllowed()) { $conditions[] = new EntityCondition($published_field_name, 1); @@ -375,7 +376,7 @@ class TemporaryQueryGuard { // @todo Remove ternary when the 'status' key is added to the User entity type. $status_field_name = $entity_type->id() === 'user' ? 'status' : $entity_type->getKey('status'); if ($status_field_name) { - $access_result = $access_results[JSONAPI_FILTER_AMONG_ENABLED]; + $access_result = $access_results[JsonApiFilter::AMONG_ENABLED]; $cacheability->addCacheableDependency($access_result); if ($access_result->isAllowed()) { $conditions[] = new EntityCondition($status_field_name, 1); @@ -387,7 +388,7 @@ class TemporaryQueryGuard { // @todo Remove ternary when the 'uid' key is added to the User entity type. $owner_field_name = $entity_type->id() === 'user' ? 'uid' : $entity_type->getKey('owner'); if ($owner_field_name) { - $access_result = $access_results[JSONAPI_FILTER_AMONG_OWN]; + $access_result = $access_results[JsonApiFilter::AMONG_OWN]; $cacheability->addCacheableDependency($access_result); if ($access_result->isAllowed()) { $cacheability->addCacheContexts(['user']); @@ -415,7 +416,7 @@ class TemporaryQueryGuard { } /** - * Gets the combined access result for each JSONAPI_FILTER_AMONG_* subset. + * Gets the combined access result for each JsonApiFilter::AMONG_* subset. * * This invokes hook_jsonapi_entity_filter_access() and * hook_jsonapi_ENTITY_TYPE_filter_access() and combines the results from all @@ -433,10 +434,10 @@ class TemporaryQueryGuard { protected static function getAccessResultsFromEntityFilterHook(EntityTypeInterface $entity_type, AccountInterface $account) { /** @var \Drupal\Core\Access\AccessResultInterface[] $combined_access_results */ $combined_access_results = [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::neutral(), - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::neutral(), - JSONAPI_FILTER_AMONG_ENABLED => AccessResult::neutral(), - JSONAPI_FILTER_AMONG_OWN => AccessResult::neutral(), + JsonApiFilter::AMONG_ALL => AccessResult::neutral(), + JsonApiFilter::AMONG_PUBLISHED => AccessResult::neutral(), + JsonApiFilter::AMONG_ENABLED => AccessResult::neutral(), + JsonApiFilter::AMONG_OWN => AccessResult::neutral(), ]; // Invoke hook_jsonapi_entity_filter_access() and diff --git a/core/modules/jsonapi/src/Hook/JsonapiHooks.php b/core/modules/jsonapi/src/Hook/JsonapiHooks.php index 7db5b77b0e80..d5f6d2540fcd 100644 --- a/core/modules/jsonapi/src/Hook/JsonapiHooks.php +++ b/core/modules/jsonapi/src/Hook/JsonapiHooks.php @@ -7,6 +7,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\jsonapi\JsonApiFilter; use Drupal\jsonapi\Routing\Routes; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Hook\Attribute\Hook; @@ -107,7 +108,7 @@ class JsonapiHooks { // AccessResult::forbidden() from its implementation of this hook. if ($admin_permission = $entity_type->getAdminPermission()) { return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, $admin_permission), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, $admin_permission), ]; } return []; @@ -122,8 +123,8 @@ class JsonapiHooks { // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for // (isReusable()), so this does not have to. return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'access block library'), - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowed(), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'access block library'), + JsonApiFilter::AMONG_PUBLISHED => AccessResult::allowed(), ]; } @@ -136,8 +137,8 @@ class JsonapiHooks { // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for // (access to the commented entity), so this does not have to. return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer comments'), - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'access comments'), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer comments'), + JsonApiFilter::AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'access comments'), ]; } @@ -148,7 +149,7 @@ class JsonapiHooks { public function jsonapiEntityTestFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account): array { // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess() return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'view test entity'), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'view test entity'), ]; } @@ -161,7 +162,7 @@ class JsonapiHooks { // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for // (public OR owner), so this does not have to. return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'access content'), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'access content'), ]; } @@ -172,7 +173,7 @@ class JsonapiHooks { public function jsonapiMediaFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account): array { // @see \Drupal\media\MediaAccessControlHandler::checkAccess() return [ - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'view media'), + JsonApiFilter::AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'view media'), ]; } @@ -184,30 +185,30 @@ class JsonapiHooks { // @see \Drupal\node\NodeAccessControlHandler::access() if ($account->hasPermission('bypass node access')) { return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowed()->cachePerPermissions(), + JsonApiFilter::AMONG_ALL => AccessResult::allowed()->cachePerPermissions(), ]; } if (!$account->hasPermission('access content')) { $forbidden = AccessResult::forbidden("The 'access content' permission is required.")->cachePerPermissions(); return [ - JSONAPI_FILTER_AMONG_ALL => $forbidden, - JSONAPI_FILTER_AMONG_OWN => $forbidden, - JSONAPI_FILTER_AMONG_PUBLISHED => $forbidden, + JsonApiFilter::AMONG_ALL => $forbidden, + JsonApiFilter::AMONG_OWN => $forbidden, + JsonApiFilter::AMONG_PUBLISHED => $forbidden, // For legacy reasons, the Node entity type has a "status" key, so // forbid this subset as well, even though it has no semantic meaning. - JSONAPI_FILTER_AMONG_ENABLED => $forbidden, + JsonApiFilter::AMONG_ENABLED => $forbidden, ]; } return [ - // @see \Drupal\node\NodeAccessControlHandler::checkAccess() - JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own unpublished content'), - // @see \Drupal\node\NodeGrantDatabaseStorage::access() - // Note that: - // - This is just for the default grant. Other node access conditions - // are added via the 'node_access' query tag. - // - Permissions were checked earlier in this function, so we must - // vary the cache by them. - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowed()->cachePerPermissions(), + // @see \Drupal\node\NodeAccessControlHandler::checkAccess() + JsonApiFilter::AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own unpublished content'), + // @see \Drupal\node\NodeGrantDatabaseStorage::access() + // Note that: + // - This is just for the default grant. Other node access conditions + // are added via the 'node_access' query tag. + // - Permissions were checked earlier in this function, so we must + // vary the cache by them. + JsonApiFilter::AMONG_PUBLISHED => AccessResult::allowed()->cachePerPermissions(), ]; } @@ -221,7 +222,7 @@ class JsonapiHooks { // "shortcut_set = $shortcut_set_storage->getDisplayedToUser($current_user)" // so this does not have to. return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer shortcuts')->orIf(AccessResult::allowedIfHasPermissions($account, [ + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer shortcuts')->orIf(AccessResult::allowedIfHasPermissions($account, [ 'access shortcuts', 'customize shortcut links', ])), @@ -235,8 +236,8 @@ class JsonapiHooks { public function jsonapiTaxonomyTermFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account): array { // @see \Drupal\taxonomy\TermAccessControlHandler::checkAccess() return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer taxonomy'), - JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'access content'), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer taxonomy'), + JsonApiFilter::AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'access content'), ]; } @@ -249,8 +250,8 @@ class JsonapiHooks { // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for // (!isAnonymous()), so this does not have to. return [ - JSONAPI_FILTER_AMONG_OWN => AccessResult::allowed(), - JSONAPI_FILTER_AMONG_ENABLED => AccessResult::allowedIfHasPermission($account, 'access user profiles'), + JsonApiFilter::AMONG_OWN => AccessResult::allowed(), + JsonApiFilter::AMONG_ENABLED => AccessResult::allowedIfHasPermission($account, 'access user profiles'), ]; } @@ -261,8 +262,8 @@ class JsonapiHooks { public function jsonapiWorkspaceFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account): array { // @see \Drupal\workspaces\WorkspaceAccessControlHandler::checkAccess() return [ - JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'view any workspace'), - JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own workspace'), + JsonApiFilter::AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'view any workspace'), + JsonApiFilter::AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own workspace'), ]; } diff --git a/core/modules/jsonapi/src/JsonApiFilter.php b/core/modules/jsonapi/src/JsonApiFilter.php new file mode 100644 index 000000000000..c9ac90be7af7 --- /dev/null +++ b/core/modules/jsonapi/src/JsonApiFilter.php @@ -0,0 +1,77 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\jsonapi; + +/** + * JsonApi filter options. + */ +final class JsonApiFilter { + + /** + * Array key for denoting type-based filtering access. + * + * Array key for denoting access to filter among all entities of a given type, + * regardless of whether they are published or enabled, and regardless of + * their owner. + * + * @see hook_jsonapi_entity_filter_access() + * @see hook_jsonapi_ENTITY_TYPE_filter_access() + */ + const AMONG_ALL = 'filter_among_all'; + + /** + * Array key for denoting type-based published-only filtering access. + * + * Array key for denoting access to filter among all published entities of a + * given type, regardless of their owner. + * + * This is used when an entity type has a "published" entity key and there's a + * query condition for the value of that equaling 1. + * + * @see hook_jsonapi_entity_filter_access() + * @see hook_jsonapi_ENTITY_TYPE_filter_access() + */ + const AMONG_PUBLISHED = 'filter_among_published'; + + /** + * Array key for denoting type-based enabled-only filtering access. + * + * Array key for denoting access to filter among all enabled entities of a + * given type, regardless of their owner. + * + * This is used when an entity type has a "status" entity key and there's a + * query condition for the value of that equaling 1. + * + * For the User entity type, which does not have a "status" entity key, the + * "status" field is used. + * + * @see hook_jsonapi_entity_filter_access() + * @see hook_jsonapi_ENTITY_TYPE_filter_access() + */ + const AMONG_ENABLED = 'filter_among_enabled'; + + /** + * Array key for denoting type-based owned-only filtering access. + * + * Array key for denoting access to filter among all entities of a given type, + * regardless of whether they are published or enabled, so long as they are + * owned by the user for whom access is being checked. + * + * When filtering among User entities, this is used when access is being + * checked for an authenticated user and there's a query condition + * limiting the result set to just that user's entity object. + * + * When filtering among entities of another type, this is used when all of the + * following conditions are met: + * - Access is being checked for an authenticated user. + * - The entity type has an "owner" entity key. + * - There's a filter/query condition for the value equal to the user's ID. + * + * @see hook_jsonapi_entity_filter_access() + * @see hook_jsonapi_ENTITY_TYPE_filter_access() + */ + const AMONG_OWN = 'filter_among_own'; + +} |