Commit 581dac09 authored by webchick's avatar webchick

Issue #1696660 by fubhy, xjm, fago, webflo: Added an entity access API for single entity access.

parent 8ca44a65
......@@ -304,6 +304,27 @@ function entity_page_label(EntityInterface $entity, $langcode = NULL) {
return $entity->label($langcode);
}
/**
* Returns the entity access controller for the given entity type.
*
* @param string $entity_type
* The type of the entity.
*
* @return \Drupal\Core\Entity\EntityAccessControllerInterface
* An entity access controller instance.
*
* @see \Drupal\Core\Entity\EntityManager
*/
function entity_access_controller($entity_type) {
$controllers = &drupal_static(__FUNCTION__, array());
if (!isset($controllers[$entity_type])) {
$type_info = entity_get_info($entity_type);
$class = $type_info['access_controller_class'];
$controllers[$entity_type] = new $class($entity_type);
}
return $controllers[$entity_type];
}
/**
* Returns an entity form controller for the given operation.
*
......
......@@ -251,8 +251,9 @@ public function getIterator() {
/**
* Implements AccessibleInterface::access().
*/
public function access(\Drupal\user\User $account = NULL) {
// TODO: Implement access() method.
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
$method = $operation . 'Access';
return entity_access_controller($this->entityType)->$method($this, LANGUAGE_DEFAULT, $account);
}
/**
......
<?php
/**
* @file
* Contains Drupal\Core\Entity\EntityAccessController.
*/
namespace Drupal\Core\Entity;
use Drupal\user\Plugin\Core\Entity\User;
/**
* Defines a base class for entity access controllers.
*
* Defaults to FALSE (access denied) for 'view', 'create', 'update' and 'delete'
* access checks.
*/
class EntityAccessController implements EntityAccessControllerInterface {
/**
* Implements EntityAccessControllerInterface::viewAccess().
*/
public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return FALSE;
}
/**
* Implements EntityAccessControllerInterface::createAccess().
*/
public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return FALSE;
}
/**
* Implements EntityAccessControllerInterface::updateAccess().
*/
public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return FALSE;
}
/**
* Implements EntityAccessControllerInterface::deleteAccess().
*/
public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return FALSE;
}
}
<?php
/**
* @file
* Contains Drupal\Core\Entity\EntityAccessControllerInterface.
*/
namespace Drupal\Core\Entity;
// @todo Don't depend on module level code.
use Drupal\user\Plugin\Core\Entity\User;
/**
* Defines a common interface for entity access controller classes.
*/
interface EntityAccessControllerInterface {
/**
* Checks 'view' access for a given entity or entity translation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which to check 'view' access.
* @param string $langcode
* (optional) The language code for which to check access. Defaults to
* LANGUAGE_DEFAULT.
* @param \Drupal\user\Plugin\Core\Entity\User $account
* (optional) The user for which to check access, or NULL to check access
* for the current user. Defaults to NULL.
*
* @return bool
* TRUE if access was granted, FALSE otherwise.
*/
public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
/**
* Checks 'create' access for a given entity or entity translation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which to check 'create' access.
* @param string $langcode
* (optional) The language code for which to check access. Defaults to
* LANGUAGE_DEFAULT.
* @param \Drupal\user\Plugin\Core\Entity\User $account
* (optional) The user for which to check access, or NULL to check access
* for the current user. Defaults to NULL.
*
* @return bool
* TRUE if access was granted, FALSE otherwise.
*/
public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
/**
* Checks 'update' access for a given entity or entity translation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to check 'update' access.
* @param string $langcode
* (optional) The language code for which to check access. Defaults to
* LANGUAGE_DEFAULT.
* @param \Drupal\user\Plugin\Core\Entity\User $account
* (optional) The user for which to check access, or NULL to check access
* for the current user. Defaults to NULL.
*
* @return bool
* TRUE if access was granted, FALSE otherwise.
*/
public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
/**
* Checks 'delete' access for a given entity or entity translation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which to check 'delete' access.
* @param string $langcode
* (optional) The language code for which to check access. Defaults to
* LANGUAGE_DEFAULT.
* @param \Drupal\user\Plugin\Core\Entity\User $account
* (optional) The user for which to check access, or NULL to check access
* for the current user. Defaults to NULL.
*
* @return bool
* TRUE if access was granted, FALSE otherwise.
*/
public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
}
......@@ -60,6 +60,10 @@
* Drupal\Core\Entity\EntityListController.
* - render_controller_class: The name of the class that is used to render the
* entities. Defaults to Drupal\Core\Entity\EntityRenderController.
* - access_controller_class: The name of the class that is used for access
* checks. The class must implement
* Drupal\Core\Entity\EntityAccessControllerInterface. Defaults to
* Drupal\Core\Entity\EntityAccessController.
* - translation_controller_class: (optional) The name of the translation
* controller class that should be used to handle the translation process.
* See Drupal\translation_entity\EntityTranslationControllerInterface for more
......@@ -212,6 +216,7 @@ class EntityManager extends PluginManagerBase {
),
'list_controller_class' => 'Drupal\Core\Entity\EntityListController',
'render_controller_class' => 'Drupal\Core\Entity\EntityRenderController',
'access_controller_class' => 'Drupal\Core\Entity\EntityAccessController',
'static_cache' => TRUE,
'translation' => array(),
'bundles' => array(),
......
......@@ -233,8 +233,9 @@ public function isEmpty() {
/**
* Implements AccessibleInterface::access().
*/
public function access(\Drupal\user\User $account = NULL) {
// @todo implement
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
$method = $operation . 'Access';
return entity_access_controller($this->parent->entityType())->$method($this->parent, $this->langcode, $account);
}
/**
......
......@@ -10,7 +10,7 @@
use Drupal\Core\Entity\Field\FieldInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\Type\TypedData;
use Drupal\user\User;
use Drupal\user\Plugin\Core\Entity\User;
use ArrayIterator;
use IteratorAggregate;
use InvalidArgumentException;
......@@ -296,7 +296,7 @@ public function __clone() {
/**
* Implements AccessibleInterface::access().
*/
public function access(User $account = NULL) {
public function access($operation = 'view', User $account = NULL) {
// TODO: Implement access() method. Use item access.
}
}
......@@ -15,12 +15,23 @@ interface AccessibleInterface {
/**
* Checks data value access.
*
* @param \Drupal\user\User $account
* (optional) The user account to check access for. Defaults to the current
* user.
* @param string $operation
* (optional) The operation to be performed. Supported values are:
* - view
* - create
* - update
* - delete
* Defaults to 'view'.
* @param \Drupal\user\Plugin\Core\Entity\User $account
* (optional) The user for which to check access, or NULL to check access
* for the current user. Defaults to NULL.
*
* @return bool
* TRUE if the given user has access; otherwise FALSE.
* TRUE if the given user has access for the given operation, FALSE
* otherwise.
*
* @todo Don't depend on module level code.
*/
public function access(\Drupal\user\User $account = NULL);
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL);
}
<?php
/**
* @file
* Contains Drupal\system\Tests\Entity\EntityAccessTest.
*/
namespace Drupal\system\Tests\Entity;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\AccessibleInterface;
use Drupal\simpletest\WebTestBase;
use Drupal\user\Plugin\Core\Entity\User;
/**
* Tests the entity access controller.
*/
class EntityAccessTest extends WebTestBase {
public static function getInfo() {
return array(
'name' => 'Entity access',
'description' => 'Tests entity access.',
'group' => 'Entity API',
);
}
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('entity_test');
/**
* Asserts entity access correctly grants or denies access.
*/
function assertEntityAccess($ops, AccessibleInterface $object, User $account = NULL) {
foreach ($ops as $op => $result) {
$message = format_string("Entity access returns @result with operation '@op'.", array(
'@result' => isset($result) ? 'null' : ($result ? 'true' : 'false'),
'@op' => $op,
));
$this->assertEqual($result, $object->access($op, $account), $message);
}
}
/**
* Ensures entity access is properly working.
*/
function testEntityAccess() {
// Set up a non-admin user that is allowed to view test entities.
$user = $this->drupalCreateUser(array('view test entity'));
$this->drupalLogin($user);
$entity = entity_create('entity_test', array(
'name' => 'test',
));
$entity->save();
// The current user is allowed to view, create, update and delete entities.
$this->assertEntityAccess(array(
'create' => TRUE,
'update' => TRUE,
'delete' => TRUE,
'view' => TRUE,
), $entity);
// The custom user is not allowed to view test entities.
$custom_user = $this->drupalCreateUser();
$this->assertEntityAccess(array(
'create' => TRUE,
'update' => TRUE,
'delete' => TRUE,
'view' => FALSE,
), $entity, $custom_user);
}
/**
* Ensures that the default controller is used as a fallback.
*/
function testEntityAccessDefaultController() {
// Remove the access controller definition from the test entity.
state()->set('entity_test.default_access_controller', TRUE);
// Check that the default access controller is used for entities that don't
// have a specific access controller defined.
$controller = entity_access_controller('entity_test');
$this->assertTrue($controller instanceof \Drupal\Core\Entity\EntityAccessController, 'The default entity controller is used for the entity_test entity type.');
$entity = entity_create('entity_test', array());
$this->assertEntityAccess(array(
'create' => FALSE,
'update' => FALSE,
'delete' => FALSE,
'view' => FALSE,
), $entity);
}
/**
* Ensures entity access for entity translations is properly working.
*/
function testEntityTranslationAccess() {
// Enable translations for the test entity type.
variable_set('entity_test_translation', TRUE);
module_enable(array('locale'));
// Set up a non-admin user that is allowed to view test entity translations.
$user = $this->drupalCreateUser(array('view test entity translations'));
$this->drupalLogin($user);
// Create two test languages.
foreach (array('foo', 'bar') as $langcode) {
$language = new Language(array(
'langcode' => $langcode,
'name' => $this->randomString(),
));
language_save($language);
}
$entity = entity_create('entity_test', array(
'name' => 'test',
'langcode' => 'foo',
));
$entity->save();
$translation = $entity->getTranslation('bar');
$this->assertEntityAccess(array(
'view' => TRUE,
), $translation);
}
}
......@@ -16,6 +16,10 @@ function entity_test_entity_info_alter(&$info) {
if (variable_get('entity_test_translation')) {
$info['entity_test']['translation']['entity_test'] = TRUE;
}
// Optionally unset the access controller to test the fallback.
if (state()->get('entity_test.default_access_controller')) {
unset($info['entity_test']['access_controller_class']);
}
}
/**
......@@ -27,6 +31,12 @@ function entity_test_permission() {
'title' => t('Administer entity_test content'),
'description' => t('Manage entity_test content'),
),
'view test entity' => array(
'title' => t('View test entities'),
),
'view test entity translations' => array(
'title' => t('View translations of test entities'),
),
);
return $permissions;
}
......
<?php
/**
* @file
* Contains Drupal\entity_test\EntityTestAccessController.
*/
namespace Drupal\entity_test;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityAccessControllerInterface;
use Drupal\user\Plugin\Core\Entity\User;
/**
* Defines the access controller for the test entity type.
*/
class EntityTestAccessController implements EntityAccessControllerInterface {
/**
* Implements EntityAccessControllerInterface::view().
*/
public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
if ($langcode != LANGUAGE_DEFAULT) {
return user_access('view test entity translations', $account);
}
return user_access('view test entity', $account);
}
/**
* Implements EntityAccessControllerInterface::create().
*/
public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return TRUE;
}
/**
* Implements EntityAccessControllerInterface::update().
*/
public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return TRUE;
}
/**
* Implements EntityAccessControllerInterface::delete().
*/
public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return TRUE;
}
}
......@@ -19,6 +19,7 @@
* label = @Translation("Test entity"),
* module = "entity_test",
* controller_class = "Drupal\entity_test\EntityTestStorageController",
* access_controller_class = "Drupal\entity_test\EntityTestAccessController",
* form_controller_class = {
* "default" = "Drupal\entity_test\EntityTestFormController"
* },
......
......@@ -991,7 +991,7 @@ public function language() {
/**
* Implements \Drupal\Core\TypedData\AccessibleInterface::access().
*/
public function access(\Drupal\user\User $account = NULL) {
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
return $this->__call(__FUNCTION__, func_get_args());
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment