foldershare-8.x-1.2/src/Entity/FolderShareAccessControlHandler.php
src/Entity/FolderShareAccessControlHandler.php
<?php
namespace Drupal\foldershare\Entity;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\foldershare\Constants;
use Drupal\foldershare\FolderShareInterface;
/**
* Provides access control for operations on files and folders.
*
* Access to a folder and its files is controlled by three mechanisms:
*
* - Permission-based access control.
* - Root item-based access control.
* - Module settings to enable/disable sharing for the entire site.
*
* <B>Permission-based access control</B>
* The module uses the standard Drupal role-based permissions mechanism and
* these module-specific permissions:
*
* - "view foldershare" enables users to view and download content.
*
* - "author foldershare" enables users to create, delete, and modify content.
*
* - "share foldershare" enables users to share root items with other users.
*
* - "share public foldershare" enables users to share root items with the
* public (anonymous users).
*
* - "administer foldershare" enables users to modify anyone's content,
* change content ownership, or modify content share settings.
*
* Additionally, users designated as site administrators always have
* full access to all content regardless of permissions.
*
* <B>Root item-based access control</B>
* Root items have access control lists that designate specific users
* that may view and author content. These access control lists act as a
* boolean AND with permission access controls so that a user must have
* both the site-wide generic view permission AND a root item's view
* access grant in order to view content.
*
* Content owners are always included in the access control lists of their
* own content and therefore always have full view and author access.
*
* The generic "anonymous" user may be granted access as well. This
* publishes content for public access.
*
* Access grants are always on the root item of a folder tree, and they
* apply to all content within that folder tree.
*
* <B>Warning:</B> This class is strictly internal to the FolderShare
* module. The class's existance, name, and content may change from
* release to release without any promise of backwards compatability.
*
* @ingroup foldershare
*
* @see \Drupal\foldershare\Entity\FolderShare
*/
class FolderShareAccessControlHandler extends EntityAccessControlHandler {
/*---------------------------------------------------------------------
*
* Field view and edit access.
*
*---------------------------------------------------------------------*/
/**
* Fields that should never be viewed.
*
* These are internal fields that don't have a meaningful presentation
* and should NEVER be shown on a page.
*
* Note that we *do not* exclude the module's internal parentid and
* rootid fields. While these are not something users should normally
* view directly, there is nothing wrong with doing so.
*/
const FIELDS_VIEW_NEVER = [
'langcode',
'uuid',
];
/**
* Fields that should never be edited directly.
*
* These are internal fields with values set programmatically. They
* should NEVER be edited directly by a user. There are, however,
* entity API calls that can set these while maintaining the integrity
* of the data model.
*
* The 'id' and 'uuid' are assigned by Drupal and need to remain unchanged
* for the life of the entity.
*
* The 'uid' is the owner of the content and should not be edited
* directly.
*
* The 'parentid and 'rootid are internal fields used to connect folders
* into a folder hierarchy. They are only changed when content is moved
* from place to place in the hierarchy.
*
* The 'size' is computed automatically and should never be edited.
*
* The 'kind' field indicates whether the entity is a file or folder
* and should never be edited.
*
* The 'file' field gives the entity ID of an underlying File object,
* if any, and obviously should never be edited.
*
* The 'grantauthoruids' and 'grantviewuids' store the user IDs of users
* granted specific access to view or author content in a root folder.
* None of these should be edited.
*/
const FIELDS_EDIT_NEVER = [
'id',
'uid',
'uuid',
'created',
'changed',
'langcode',
'parentid',
'rootid',
'size',
'kind',
'mime',
'file',
'image',
'media',
'grantauthoruids',
'grantviewuids',
'systemhidden',
'systemdisabled',
];
/*---------------------------------------------------------------------
*
* Construct.
*
*---------------------------------------------------------------------*/
/**
* Constructs and initializes an access control handler object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entityType
* The entity type definition.
*/
public function __construct(EntityTypeInterface $entityType) {
parent::__construct($entityType);
// File and folder names (labels) may be viewed by users granted
// permission using the generic view operation.
$this->viewLabelOperation = FALSE;
}
/**
* {@inheritdoc}
*/
public static function createInstance(EntityTypeInterface $entityType) {
return new static($entityType);
}
/*---------------------------------------------------------------------
*
* Check access.
*
* Implements EntityAccessControlHandler and overrides selected methods.
*
* These are the primary entrance points for this class.
*
*---------------------------------------------------------------------*/
/**
* Checks if a user has permission for an operation on an item.
*
* The following operations are supported:
*
* - 'chown'. Change ownership of the file or folder and all of its contents.
*
* - 'delete'. Delete the file or folder, and all of its subfolders
* and files.
*
* - 'share'. Change access grants to share/unshare a root item, and
* its folder tree, for view or author access by other users.
*
* - 'update'. Edit the file's or folder's fields.
*
* - 'view'. View or copy the file's or folder's fields.
*
* <B>Hooks</B>
* Via the parent class, this method invokes the "hook_foldershare_access"
* hook after checking permissions and access grants. This hook is not
* invoked for administrators, which always have access.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which access checking is required.
* @param string $operation
* The name of the operation being considered.
* @param \Drupal\Core\Session\AccountInterface $account
* (optional) The user for which to check access. Defaults to the
* current user.
* @param bool $returnAsObject
* (optional) When TRUE, an access object is returned. When FALSE
* (default), a boolean is returned.
*
* @return bool|\Drupal\Core\Access\AccessResultInterface
* The result of the access control check.
*
* @see ::checkAccess()
*/
public function access(
EntityInterface $entity,
$operation,
AccountInterface $account = NULL,
$returnAsObject = FALSE) {
// This method, and those it calls, implement four steps for access
// control:
//
// 1. If the user is a site or content administrator, they are granted
// immediate access for all content and all operations.
//
// 2. If the user does not have the specific module permission required
// for an operation (e.g. view, author, share), they are denied
// access.
//
// 3. If the user does not own the content and sharing is disabled for
// the module, they are denied access.
//
// 4. If the user does not own the content and they are not granted
// specific access by the content's root item, they are denied
// access.
//
// Otherwise the user is granted access.
//
// This method handles step (1) only. It then forwards to the parent
// class, which forwards to our checkAccess() method below which
// handles steps (2), (3), and (4).
$account = $this->prepareUser($account);
//
// Allow site & content administrators everything.
// -----------------------------------------------
// Drupal insures that users marked as site administrators always have
// all permissions. The check below, then, will always return TRUE for
// site admins.
//
// Content administrators have this module's ADMINISTER_PERMISSION,
// which grants them access to all content for all operations.
$perm = $this->entityType->getAdminPermission();
if (empty($perm) === TRUE) {
$perm = Constants::ADMINISTER_PERMISSION;
}
$access = AccessResult::allowedIfHasPermission($account, $perm);
if ($access->isAllowed() === TRUE) {
// The user is either a site admin or they have the module's
// content admin permission. Grant full access.
$access->cachePerPermissions();
return $returnAsObject === TRUE ? $access : $access->isAllowed();
}
//
// Check entity-based ACLs.
// ------------------------
// At this point, the user is NOT a site administrator and they do not
// have module content admin permission. Access to the content is
// determined now by a mix of permissions, module settings, and content
// access controls.
//
// Forward to the parent class, which implements cache checks and
// module hooks to override access controls. Afterwards the parent
// calls our checkAccess() method to handle permissions and root item
// access control lists.
$access = parent::access($entity, $operation, $account, TRUE);
$access->cachePerPermissions();
return $returnAsObject === TRUE ? $access : $access->isAllowed();
}
/**
* Checks if a given user has permission to create a new entity.
*
* This method handles access checks for create operations.
* Access is granted for creating root items if:
*
* - The user has the administer permission OR
* - The user has the author permission.
*
* Note that this method is NOT passed an entity, so it cannot check
* if creation is allowed within an entity. To do that, use access()
* on the entity and pass 'create' as the operation.
*
* @param \Drupal\Core\Session\AccountInterface $account
* (optional, default = NULL = current user) The user for which to
* check access.
* @param array $context
* (optional, default = []) An array of key-value pairs to pass additional
* context to this method. Ignored by this method.
* @param string|null $bundle
* (optional, default = NULL) An optional bundle of the entity, for
* entities that support bundles, and NULL otherwise. Ignored by this
* method since file/folder entities do not support bundles.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The result of the access control check.
*
* @see ::checkAccess()
*/
protected function checkCreateAccess(
AccountInterface $account = NULL,
array $context = [],
$bundle = NULL) {
$account = $this->prepareUser($account);
//
// Allow site & content administrators everything.
// -----------------------------------------------
// Drupal insures that users marked as site administrators always have
// all permissions. The check below, then, will always return TRUE for
// site admins.
//
// Content administrators have this module's ADMINISTER_PERMISSION,
// which grants them access to everything.
$perm = $this->entityType->getAdminPermission();
if (empty($perm) === TRUE) {
$perm = Constants::ADMINISTER_PERMISSION;
}
$access = AccessResult::allowedIfHasPermission($account, $perm);
if ($access->isAllowed() === TRUE) {
// The user is either a site admin or they have the module's
// content admin permission. Access granted.
$access->cachePerPermissions()
->cachePerUser();
return $access;
}
//
// Allow author users.
// -------------------
// Require AUTHOR_PERMISSION.
$perm = Constants::AUTHOR_PERMISSION;
$access = AccessResult::allowedIfHasPermission($account, $perm);
if ($access->isAllowed() === FALSE) {
// Access denied.
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser();
}
return AccessResult::allowed()
->cachePerPermissions()
->cachePerUser();
}
/**
* Checks if a user has permission to view or edit a field.
*
* If the entity allows access (based on permissions and root item
* access grants), then most fields may be viewed and some may be
* edited. Some internal fields may be viewed by administrators,
* but not edited directly.
*
* The table below indicates which fields are restricted.
* The fields themselves are defined in the FolderShare class.
*
* | Field | Allow for view | Allow for edit |
* | ---------------- | -------------- | -------------- |
* | id | yes | no |
* | uuid | yes | no |
* | uid | yes | no |
* | langcode | no | yes |
* | created | yes | no |
* | changed | yes | no |
* | size | yes | no |
* | name | yes | yes |
* | description | yes | yes |
* | parentid | yes | no |
* | rootid | yes | no |
* | kind | yes | no |
* | file | yes | no |
* | grantauthoruids | yes | no |
* | grantviewuids | yes | no |
* | systemhidden | no | no |
* | systemdisabled | no | no |
*
* Any additional fields created by third-parties or by the field_ui
* module can be viewed and edited.
*
* @param string $operation
* The name of the operation being considered, which is always
* 'view' or 'edit' (defined by Drupal core).
* @param \Drupal\Core\Field\FieldDefinitionInterface $field
* The field being checked.
* @param \Drupal\Core\Session\AccountInterface $account
* (optional) The user for which to check access. Defaults to
* the current user.
* @param \Drupal\Core\Field\FieldItemListInterface $items
* (optional) The list of field values for which to check access.
* Ignored by this method.
*
* @return\Drupal\Core\Access\AccessResultInterface
* The result of the access control check.
*/
protected function checkFieldAccess(
$operation,
FieldDefinitionInterface $field,
AccountInterface $account = NULL,
FieldItemListInterface $items = NULL) {
// This method is called for every field of every row when showing
// lists of files and folders. It is therefore important that it run as
// quickly as possible for view operations.
$fieldName = $field->getName();
switch ($operation) {
case 'view':
// Any field in FIELDS_VIEW_NEVER is NEVER viewable by any type
// of user, including admins.
if (in_array($fieldName, self::FIELDS_VIEW_NEVER, TRUE) === TRUE) {
// Access denied.
return AccessResult::forbidden();
}
// Access granted.
return AccessResult::allowed();
case 'edit':
// Any field in FIELDS_EDIT_NEVER is NEVER editable directly by
// any type of user, including admins.
if (in_array($fieldName, self::FIELDS_EDIT_NEVER, TRUE) === TRUE) {
// Access denied.
return AccessResult::forbidden();
}
// Access granted.
return AccessResult::allowed();
default:
// Unknown operation. Access denied.
return AccessResult::forbidden();
}
}
/**
* Checks if a user can do an operation on an item.
*
* This is the principal access control method for this class. It
* checks permissions and per-root item access control lists to decide
* if access is granted or denied.
*
* The following operations are supported:
*
* - 'create'. Create a new item within a folder.
*
* - 'delete'. Delete the file or folder, and all its subfolders and files.
*
* - 'share'. Change access grants to share/unshare
* this item's root item for view or author access by other users.
*
* - 'update'. Edit the file's or folder's fields or add children to
* a folder.
*
* - 'view'. View and copy the file's or folder's fields.
*
* Each of these operations require an appropriate mix of module
* permissions, file/folder ownership, and/or access grants:
*
* - Create requires that the user have module author permission AND
* either own the file/folder OR have been granted author access
* on the root item.
*
* - Delete requires that the user have module author permission AND
* either own the file/folder OR have been granted author access
* on the root item.
*
* - Share requires that the user have module share permission AND
* own the file/folder's root item, where access grants are stored.
*
* - Update requires that the user have module author permission AND
* either own the file/folder OR have been granted author access
* on the root item.
*
* - View requires that the user have module view permission AND
* either own the file/folder OR have been granted view access
* on the root item.
*
* - View tree requires that the user have module view permission AND
* either own the entire folder tree OR have been granted view access
* on the root item.
*
* Note that update and delete only check the file/folder for access and
* NOT all subfolders. This means that a user that is allowed to write to
* a file/folder, can delete that file/folder and all of its contents,
* even if they do not have write permission on all of that content.
* Ownership of a file/folder is always an override of the per-file/folder
* access controls.
*
* @param \Drupal\Core\Entity\EntityInterface $item
* The entity for which access checking is required.
* @param string $operation
* The name of the operation being considered.
* @param \Drupal\Core\Session\AccountInterface $user
* (optional) The user for which to check access. Defaults to
* the current user.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The result of the access control check.
*/
protected function checkAccess(
EntityInterface $item,
$operation,
AccountInterface $user = NULL) {
//
// Validate.
// ---------
// If the given entity is missing or invalid, we cannot check access.
if ($item === NULL) {
// No entity. Access denied.
return AccessResult::forbidden();
}
$entityTypeId = $item->getEntityTypeId();
if ($entityTypeId !== FolderShare::ENTITY_TYPE_ID) {
// Invalid entity. Access denied.
return AccessResult::forbidden();
}
// Get current user info.
$user = $this->prepareUser($user);
$userId = (int) $user->id();
//
// Allow site & content administrators everything.
// -----------------------------------------------
// Drupal insures that users marked as site administrators always have
// all permissions. The check below, then, will always return TRUE for
// site admins.
//
// Content administrators have this module's ADMINISTER_PERMISSION,
// which grants them access to everything.
$perm = $this->entityType->getAdminPermission();
if (empty($perm) === TRUE) {
$perm = Constants::ADMINISTER_PERMISSION;
}
$access = AccessResult::allowedIfHasPermission($user, $perm);
if ($access->isAllowed() === TRUE) {
// The user is either a site admin or they have the module's
// content admin permission. Access granted.
$access->cachePerPermissions()
->cachePerUser();
return $access;
}
//
// Check for disabled and hidden.
// ------------------------------
// Disabled and hidden items are never accessible.
if ($item->isSystemHidden() === TRUE) {
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
if ($item->isSystemDisabled() === TRUE) {
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
//
// Check access.
// -------------
// For each known operation, check permissions and per-root item access
// control grants.
switch ($operation) {
case 'view':
case 'update':
case 'delete':
case 'create':
// View, update, delete, or create-within content.
//
// Allowed if:
// - User has view/update/delete permission.
// - And one of:
// - User owns the content.
// - Owner has sharing permission AND
// user has been granted appropriate view or author access.
if ($operation === 'view') {
// View.
$userPermissionNeeded = Constants::VIEW_PERMISSION;
$userGrantNeeded = 'view';
}
elseif ($operation === 'create') {
// Create. If the item is not a folder, then forbidden.
if ($item->isFolder() === FALSE) {
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
$userPermissionNeeded = Constants::AUTHOR_PERMISSION;
$userGrantNeeded = 'author';
}
else {
// Update and delete.
$userPermissionNeeded = Constants::AUTHOR_PERMISSION;
$userGrantNeeded = 'author';
}
// Check that the current user has permission.
$access = AccessResult::allowedIfHasPermission(
$user,
$userPermissionNeeded);
if ($access->isAllowed() === FALSE) {
// Access denied. The user does not have the required permissions.
return $access->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
// Check if the root item is hidden or disabled.
$rootItem = $item->getRootItem();
if ($rootItem->isSystemHidden() === TRUE ||
$rootItem->isSystemDisabled() === TRUE) {
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item)
->addCacheableDependency($rootItem);
}
// Check if the root item's owner has explicitly granted shared
// access to the current user.
if ($rootItem->isAccessGranted($userId, $userGrantNeeded) === FALSE) {
// Access denied. The item is not owned by the current user and
// the owner of the item's root has not granted the user access.
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item)
->addCacheableDependency($rootItem);
}
// Success!
return $access->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item)
->addCacheableDependency($rootItem);
case 'share':
// Share content.
//
// Allowed if:
// - User has share permission.
// - User owns the root item.
//
// Never allowed for anonymous.
if ($user->isAnonymous() === TRUE) {
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
$access = AccessResult::allowedIfHasPermission(
$user,
Constants::SHARE_PERMISSION);
if ($access->isAllowed() === FALSE) {
$access = AccessResult::allowedIfHasPermission(
$user,
Constants::SHARE_PUBLIC_PERMISSION);
if ($access->isAllowed() === FALSE) {
// Access denied. The user does not have the required permissions.
return $access->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
}
// Check if the root item is hidden or disabled.
$rootItem = $item->getRootItem();
if ($rootItem->isSystemHidden() === TRUE ||
$rootItem->isSystemDisabled() === TRUE) {
return AccessResult::forbidden()->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item)
->addCacheableDependency($rootItem);
}
// Check if the item is owned by the current user.
$rootOwnerId = (int) $rootItem->getOwnerId();
if ($rootOwnerId !== $userId) {
// Access denied. The current user does not own the item, so they
// cannot change its sharing grants.
return AccessResult::forbidden()->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item)
->addCacheableDependency($rootItem);
}
// Success!
return $access->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item)
->addCacheableDependency($rootItem);
case 'chown':
// Only site or module content administrators may change ownership
// on content. Above we already checked for admins and granted access
// to them. So if we are here, the user is not an admin and access
// denied.
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
default:
// Unknown operation. Access denied.
return AccessResult::forbidden()
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($item);
}
}
/**
* Checks if a user has permission for an operation.
*
* The following operations are supported:
*
* - 'chown'
* - 'delete'
* - 'share'
* - 'update'
* - 'view'
*
* Unlike access(), this function only checks permissions, and not
* entity-based access controls. It therefore returns the *potential*
* for access, but not an absolute yes/no on access because that
* would require checking entity access controls too for a specific
* file or folder.
*
* @param string $operation
* The name of the operation being considered.
* @param \Drupal\Core\Session\AccountInterface $account
* (optional) The user for which to check access. Defaults to
* the current user.
*
* @return bool
* The result of the access control check.
*/
public static function mayAccess(
string $operation,
AccountInterface $account = NULL) {
//
// Validate.
// ---------
// Get the account to check.
if ($account === NULL) {
$account = \Drupal::currentUser();
}
//
// Allow site & content administrators everything.
// -----------------------------------------------
// Drupal insures that users marked as site administrators always have
// all permissions. The check below, then, will always return TRUE for
// site admins.
//
// Content administrators have this module's ADMINISTER_PERMISSION,
// which grants them access to everything.
if ($account->hasPermission(Constants::ADMINISTER_PERMISSION) === TRUE) {
// The user is either a site admin or they have the module's
// content admin permission. Access granted.
return TRUE;
}
//
// Check permissions.
// ------------------
// For each known operation, check permissions only.
switch ($operation) {
case 'view':
// View content.
return $account->hasPermission(Constants::VIEW_PERMISSION);
case 'update':
case 'delete':
case 'create':
// Change or delete content.
return $account->hasPermission(Constants::AUTHOR_PERMISSION);
case 'share':
// Alter sharing of content.
if ($account->isAnonymous() === TRUE) {
// Anonymous NEVER has share permission.
return FALSE;
}
return $account->hasPermission(Constants::SHARE_PERMISSION) ||
$account->hasPermission(Constants::SHARE_PUBLIC_PERMISSION);
case 'chown':
// Only administrators can change ownership, which was already
// checked above. Access denied.
return FALSE;
default:
// Unrecognized operation. Access denied.
return FALSE;
}
}
/**
* Returns a list of operations and their permissions.
*
* While the class's access methods may be called repeatedly to
* check for permission on each operation, this function consolidates
* those calls and checks permissions all at once for all known operations.
* The returned associative array has operation names as keys, and
* TRUE/FALSE values that indicate if the operation is allowed.
*
* Operations include:
* - 'chown'.
* - 'create'.
* - 'delete'.
* - 'share'.
* - 'update'.
* - 'view'.
*
* The $entity argument names an entity for which access is checked.
* If this is NULL, access checks are for operations on the root item
* list for which there is no parent entity.
*
* @param \Drupal\foldershare\Entity\FolderShare $entity
* (optional, default = NULL = user root list) The entity for which
* access grants are checked. If this is NULL, getRootAccessSummary()
* is called for FolderShareInterface::USER_ROOT_LIST.
* @param \Drupal\Core\Session\AccountInterface $account
* (optional, default = NULL = current user) The user for which to check
* access.
*
* @return boolean[]
* The returned associative array has operation names as keys, and
* TRUE/FALSE values that indicate if the user has permission for
* the associated operation.
*/
public static function getAccessSummary(
FolderShare $entity = NULL,
AccountInterface $account = NULL) {
if ($account === NULL) {
$account = \Drupal::currentUser();
}
if ($entity === NULL) {
if ($account->isAnonymous() === TRUE) {
return self::getRootAccessSummary(
FolderShareInterface::PUBLIC_ROOT_LIST,
$account);
}
return self::getRootAccessSummary(
FolderShareInterface::USER_ROOT_LIST,
$account);
}
// Check if the user can perform a specific operation. This should be
// read as "can the user OPERATOR the item?". For example,
// "can the user DELETE the item?"
//
// The 'create' operator should be read as "can the user CREATE within
// the item?".
$a = [
'chown' => $entity->access('chown', $account, FALSE),
'create' => $entity->access('create', $account, FALSE),
'delete' => $entity->access('delete', $account, FALSE),
'share' => $entity->access('share', $account, FALSE),
'update' => $entity->access('update', $account, FALSE),
'view' => $entity->access('view', $account, FALSE),
];
return $a;
}
/**
* Returns a list of rootlist operations and their permissions.
*
* While the class's access methods may be called repeatedly to
* check for permission on each operation, this function consolidates
* those calls and checks permissions all at once for all known operations.
* The returned associative array has operation names as keys, and
* TRUE/FALSE values that indicate if the operation is allowed.
*
* Operations include:
* - 'chown'.
* - 'create'.
* - 'delete'.
* - 'share'.
* - 'update'.
* - 'view'.
*
* The $rootId argument names a root list for which access is checked.
*
* @param int $rootId
* (optional, default = FolderShareInterface::USER_ROOT_LIST) The ID
* for a root list for which access grants are checked. Values are
* expected to be one of the well-known root lists:
* FolderShareInterface::USER_ROOT_LIST,
* FolderShareInterface::ALL_ROOT_LIST, or
* FolderShareInterface::PUBLIC_ROOT_LIST. All other values return
* no permissions.
* @param \Drupal\Core\Session\AccountInterface $account
* (optional, default = NULL = current user) The user for which to check
* access.
*
* @return boolean[]
* The returned associative array has operation names as keys, and
* TRUE/FALSE values that indicate if the user has permission for
* the associated operation.
*/
public static function getRootAccessSummary(
int $rootId = FolderShareInterface::USER_ROOT_LIST,
AccountInterface $account = NULL) {
//
// Validate.
// ---------
// Get the account to check.
if ($account === NULL) {
$account = \Drupal::currentUser();
}
// Make sure the parent ID is for a root list.
switch ($rootId) {
case FolderShareInterface::ALL_ROOT_LIST:
// Treat as the user's root list.
if ($account->isAnonymous() === TRUE) {
$rootId = FolderShareInterface::PUBLIC_ROOT_LIST;
}
else {
$rootId = FolderShareInterface::USER_ROOT_LIST;
}
break;
case FolderShareInterface::USER_ROOT_LIST:
case FolderShareInterface::PUBLIC_ROOT_LIST:
break;
default:
// For any other entity ID, no permissions.
return [
'chown' => FALSE,
'create' => FALSE,
'delete' => FALSE,
'share' => FALSE,
'update' => FALSE,
'view' => FALSE,
];
}
// Get entity info.
$entityTypeManager = \Drupal::entityTypeManager();
$accessController = $entityTypeManager->getAccessControlHandler(
FolderShare::ENTITY_TYPE_ID);
//
// Allow site & content administrators everything.
// -----------------------------------------------
// Drupal insures that users marked as site administrators always have
// all permissions. The check below, then, will always return TRUE for
// site admins.
//
// Content administrators have this module's ADMINISTER_PERMISSION,
// which grants them access to everything.
if ($account->hasPermission(Constants::ADMINISTER_PERMISSION) === TRUE) {
// The user is either a site admin or they have the module's
// content admin permission.
//
// For the public root list, allow admins all access EXCEPT create.
// It doesn't make sense to create within the public list if the user
// is not anonymous. The created item would be owned by the current
// admin user, not anonymous, so it would not show up in the public
// root list being shown.
//
// There are three "right" ways to make things public:
// - Create an item in a user's own root list, then share it with the
// public.
// - Create an item as anonymous, assuming anonymous has author access.
// The created item will be owned by anonymous and show up in the
// public list.
// - Create an item in a user's own root list, then an admin changes
// the item's ownership to anonymous. The item will move to the public
// list.
//
// It is feasible that a site has granted the anonymous user admin
// permission, though that is a terrible idea. In that case, we're in
// this block of code and the user is anonymous. Allow them create
// access, but no other admin.
if ($rootId === FolderShareInterface::PUBLIC_ROOT_LIST) {
return [
'chown' => TRUE,
'create' => ($account->isAnonymous()),
'delete' => TRUE,
'share' => TRUE,
'update' => TRUE,
'view' => TRUE,
];
}
// For any other root list, admins can do everything.
return [
'chown' => TRUE,
'create' => TRUE,
'delete' => TRUE,
'share' => TRUE,
'update' => TRUE,
'view' => TRUE,
];
}
//
// Check permissions.
// ------------------
// Check if the user can perform a specific operation. This should be
// read as "can the user OPERATOR items in the root list?". For example,
// "can the user DELETE items in the root list?"
//
// The 'create' permission requires a special call back to the access
// controller, while the others are based on permissions.
//
// From the earlier comments, note again that there are three "right"
// ways to make things public:
// - Create an item in a user's root list, then share with public.
// - Create an item in a user's root list, then change ownership to
// anonymous.
// - Create an item in the public root list directly as anonymous.
//
// It is not possible to create directly into the public root list unless
// the user is anonymous. In that case, the public root list is their
// home root list and they can create if they have permission.
if ($rootId === FolderShareInterface::PUBLIC_ROOT_LIST) {
if ($account->isAnonymous() === TRUE) {
$canCreate = $accessController->createAccess(NULL, $account);
}
else {
$canCreate = FALSE;
}
}
else {
$canCreate = $accessController->createAccess(NULL, $account);
}
$canView = $account->hasPermission(Constants::VIEW_PERMISSION);
$canAuthor = $account->hasPermission(Constants::AUTHOR_PERMISSION);
if ($account->isAnonymous() === TRUE) {
$canShare = FALSE;
}
else {
$canShare = $account->hasPermission(Constants::SHARE_PERMISSION) ||
$account->hasPermission(Constants::SHARE_PUBLIC_PERMISSION);
}
return [
// Non-administrators cannot change content ownership.
'chown' => FALSE,
// Other operations depend upon the user's permissions.
'create' => $canCreate,
'delete' => $canAuthor,
'share' => $canShare,
'update' => $canAuthor,
'view' => $canView,
];
}
}
