Issue #2471154 by amateescu, googletorp, Denchev, mariancalinro, Xano, celdia: Anonymous user label can't be viewed and auth user labels are only accessible with 'access user profiles' permission

8.1.x
Nathaniel Catchpole 2016-02-22 19:50:34 +09:00
parent fd16d6eb22
commit 5972523090
8 changed files with 130 additions and 9 deletions

View File

@ -290,7 +290,7 @@ class EntityAutocomplete extends Textfield {
/**
* Converts an array of entity objects into a string of entity labels.
*
* This method is also responsible for checking the 'view' access on the
* This method is also responsible for checking the 'view label' access on the
* passed-in entities.
*
* @param \Drupal\Core\Entity\EntityInterface[] $entities
@ -302,7 +302,9 @@ class EntityAutocomplete extends Textfield {
public static function getEntityLabels(array $entities) {
$entity_labels = array();
foreach ($entities as $entity) {
$label = ($entity->access('view')) ? $entity->label() : t('- Restricted access -');
// Use the special view label, since some entities allow the label to be
// viewed, even if the entity is not allowed to be viewed.
$label = ($entity->access('view label')) ? $entity->label() : t('- Restricted access -');
// Take into account "autocreated" entities.
if (!$entity->isNew()) {

View File

@ -39,6 +39,16 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
*/
protected $entityType;
/**
* Allows to grant access to just the labels.
*
* By default, the "view label" operation falls back to "view". Set this to
* TRUE to allow returning different access when just listing entity labels.
*
* @var bool
*/
protected $viewLabelOperation = FALSE;
/**
* Constructs an access control handler instance.
*
@ -57,6 +67,10 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
$account = $this->prepareUser($account);
$langcode = $entity->language()->getId();
if ($operation === 'view label' && $this->viewLabelOperation == FALSE) {
$operation = 'view';
}
if (($return = $this->getCache($entity->uuid(), $operation, $langcode, $account)) !== NULL) {
// Cache hit, no work necessary.
return $return_as_object ? $return : $return->isAllowed();
@ -124,7 +138,8 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which to check access.
* @param string $operation
* The entity operation. Usually one of 'view', 'update' or 'delete'.
* The entity operation. Usually one of 'view', 'view label', 'update' or
* 'delete'.
* @param \Drupal\Core\Session\AccountInterface $account
* The user for which to check access.
*

View File

@ -27,7 +27,7 @@ interface EntityAccessControlHandlerInterface {
* The entity for which to check access.
* @param string $operation
* The operation access should be checked for.
* Usually one of "view", "update" or "delete".
* Usually one of "view", "view label", "update" or "delete".
* @param \Drupal\Core\Session\AccountInterface $account
* (optional) The user session for which to check access, or NULL to check
* access for the current user. Defaults to NULL.

View File

@ -10,6 +10,7 @@ namespace Drupal\link\Tests;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Url;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\link\LinkItemInterface;
use Drupal\simpletest\WebTestBase;
@ -99,6 +100,12 @@ class LinkFieldTest extends WebTestBase {
// Create a node to test the link widget.
$node = $this->drupalCreateNode();
// Create an entity with restricted view access.
$entity_test_no_label_access = EntityTest::create([
'name' => 'forbid_access',
]);
$entity_test_no_label_access->save();
// Define some valid URLs (keys are the entered values, values are the
// strings displayed to the user).
$valid_external_entries = array(
@ -132,7 +139,7 @@ class LinkFieldTest extends WebTestBase {
// Entity URI displayed as ER autocomplete value when displayed in a form.
'entity:node/1' => $node->label() . ' (1)',
// URI for an entity that exists, but is not accessible by the user.
'entity:user/1' => '- Restricted access - (1)',
'entity:entity_test/' . $entity_test_no_label_access->id() => '- Restricted access - (' . $entity_test_no_label_access->id() . ')',
// URI for an entity that doesn't exist, but with a valid ID.
'entity:user/999999' => 'entity:user/999999',
);

View File

@ -8,6 +8,7 @@
namespace Drupal\node\Tests;
use Drupal\node\NodeInterface;
use Drupal\user\Entity\User;
/**
* Create a node and test node edit functionality.
@ -205,10 +206,17 @@ class NodeEditFormTest extends NodeTestBase {
// won't do.
$this->assertTrue($uid === 0 || $uid === '0', 'Node authored by anonymous user.');
// Go back to the edit form and check that the correct value is displayed
// in the author widget.
$this->drupalGet('node/' . $node->id() . '/edit');
$anonymous_user = User::getAnonymousUser();
$expected = $anonymous_user->label() . ' (' . $anonymous_user->id() . ')';
$this->assertFieldByName($form_element_name, $expected, 'Authored by field displays the correct value for the anonymous user.');
// Change the authored by field to another user's name (that is not
// logged in).
$edit[$form_element_name] = $this->webUser->getUsername();
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
$this->nodeStorage->resetCache(array($node->id()));
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($node->getOwnerId(), $this->webUser->id(), 'Node authored by normal user.');

View File

@ -11,9 +11,12 @@ use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestDefaultAccess;
use Drupal\entity_test\Entity\EntityTestLabel;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\user\Entity\User;
/**
* Tests the entity access control handler.
@ -36,13 +39,70 @@ class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
}
}
/**
* Ensures user labels are accessible for everyone.
*/
public function testUserLabelAccess() {
// Set up a non-admin user.
\Drupal::currentUser()->setAccount($this->createUser(['uid' => 2]));
$anonymous_user = User::getAnonymousUser();
$user = $this->createUser();
// The current user is allowed to view the anonymous user label.
$this->assertEntityAccess(array(
'create' => FALSE,
'update' => FALSE,
'delete' => FALSE,
'view' => FALSE,
'view label' => TRUE,
), $anonymous_user);
// The current user is allowed to view user labels.
$this->assertEntityAccess(array(
'create' => FALSE,
'update' => FALSE,
'delete' => FALSE,
'view' => FALSE,
'view label' => TRUE,
), $user);
// Switch to a anonymous user account.
$account_switcher = \Drupal::service('account_switcher');
$account_switcher->switchTo(new AnonymousUserSession());
// The anonymous user is allowed to view the anonymous user label.
$this->assertEntityAccess(array(
'create' => FALSE,
'update' => FALSE,
'delete' => FALSE,
'view' => FALSE,
'view label' => TRUE,
), $anonymous_user);
// The anonymous user is allowed to view user labels.
$this->assertEntityAccess(array(
'create' => FALSE,
'update' => FALSE,
'delete' => FALSE,
'view' => FALSE,
'view label' => TRUE,
), $user);
// Restore user account.
$account_switcher->switchBack();
}
/**
* Ensures entity access is properly working.
*/
function testEntityAccess() {
// Set up a non-admin user that is allowed to view test entities.
\Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity')));
$entity = EntityTest::create(array(
// Use the 'entity_test_label' entity type in order to test the 'view label'
// access operation.
$entity = EntityTestLabel::create(array(
'name' => 'test',
));
@ -52,15 +112,18 @@ class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
'update' => FALSE,
'delete' => FALSE,
'view' => TRUE,
'view label' => TRUE,
), $entity);
// The custom user is not allowed to perform any operation on test entities.
// The custom user is not allowed to perform any operation on test entities,
// except for viewing their label.
$custom_user = $this->createUser();
$this->assertEntityAccess(array(
'create' => FALSE,
'update' => FALSE,
'delete' => FALSE,
'view' => FALSE,
'view label' => TRUE,
), $entity, $custom_user);
}

View File

@ -11,6 +11,7 @@ use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Session\AccountInterface;
use Drupal\entity_test\Entity\EntityTestLabel;
/**
* Defines the access control handler for the test entity type.
@ -25,6 +26,13 @@ use Drupal\Core\Session\AccountInterface;
*/
class EntityTestAccessControlHandler extends EntityAccessControlHandler {
/**
* Allows to grant access to just the labels.
*
* @var bool
*/
protected $viewLabelOperation = TRUE;
/**
* {@inheritdoc}
*/
@ -37,7 +45,11 @@ class EntityTestAccessControlHandler extends EntityAccessControlHandler {
return AccessResult::forbidden();
}
if ($operation === 'view') {
if ($operation === 'view label' && $entity instanceof EntityTestLabel) {
// Viewing the label of the 'entity_test_label' entity type is allowed.
return AccessResult::allowed();
}
elseif (in_array($operation, array('view', 'view label'))) {
if (!$entity->isDefaultTranslation()) {
return AccessResult::allowedIfHasPermission($account, 'view test entity translations');
}

View File

@ -21,12 +21,26 @@ use Drupal\Core\Session\AccountInterface;
*/
class UserAccessControlHandler extends EntityAccessControlHandler {
/**
* Allow access to user label.
*
* @var bool
*/
protected $viewLabelOperation = TRUE;
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
/** @var \Drupal\user\UserInterface $entity*/
// We don't treat the user label as privileged information, so this check
// has to be the first one in order to allow labels for all users to be
// viewed, including the special anonymous user.
if ($operation === 'view label') {
return AccessResult::allowed();
}
// The anonymous user's profile can neither be viewed, updated nor deleted.
if ($entity->isAnonymous()) {
return AccessResult::forbidden();