Commit 61e213fe authored by webchick's avatar webchick

Issue #2047659 by Wim Leers, dawehner: Add test coverage for edit module access checkers.

parent bbf70b0c
......@@ -16,15 +16,18 @@ edit_field_form:
pattern: '/edit/form/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}'
defaults:
_controller: '\Drupal\edit\EditController::fieldForm'
options:
_access_mode: 'ALL'
requirements:
_permission: 'access in-place editing'
_access_edit_entity_field: 'TRUE'
edit_entity_save:
pattern: '/edit/entity/{entity_type}/{entity}'
defaults:
_controller: '\Drupal\edit\EditController::entitySave'
options:
_access_mode: 'ALL'
requirements:
_permission: 'access in-place editing'
_access_edit_entity: 'TRUE'
......@@ -4,10 +4,12 @@ services:
arguments: ['@container.namespaces']
access_check.edit.entity_field:
class: Drupal\edit\Access\EditEntityFieldAccessCheck
arguments: ['@plugin.manager.entity', '@field.info']
tags:
- { name: access_check }
access_check.edit.entity:
class: Drupal\edit\Access\EditEntityAccessCheck
arguments: ['@plugin.manager.entity']
tags:
- { name: access_check }
edit.editor.selector:
......
......@@ -12,12 +12,30 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManager;
/**
* Access check for editing entities.
*/
class EditEntityAccessCheck implements StaticAccessCheckInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* Constructs a EditEntityAccessCheck object.
*
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager.
*/
public function __construct(EntityManager $entity_manager) {
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
......@@ -53,10 +71,10 @@ protected function validateAndUpcastRequestAttributes(Request $request) {
if (!is_object($entity = $request->attributes->get('entity'))) {
$entity_id = $entity;
$entity_type = $request->attributes->get('entity_type');
if (!$entity_type || !entity_get_info($entity_type)) {
if (!$entity_type || !$this->entityManager->getDefinition($entity_type)) {
throw new NotFoundHttpException();
}
$entity = entity_load($entity_type, $entity_id);
$entity = $this->entityManager->getStorageController($entity_type)->load($entity_id);
if (!$entity) {
throw new NotFoundHttpException();
}
......
......@@ -13,12 +13,41 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\FieldInfo;
use Drupal\Core\Entity\EntityManager;
/**
* Access check for editing entity fields.
*/
class EditEntityFieldAccessCheck implements StaticAccessCheckInterface, EditEntityFieldAccessCheckInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* The field info.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* Constructs a EditEntityFieldAccessCheck object.
*
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager.
* @param \Drupal\field\FieldInfo $field_info
* The field info.
*/
public function __construct(EntityManager $entity_manager, FieldInfo $field_info) {
$this->entityManager = $entity_manager;
$this->fieldInfo = $field_info;
}
/**
* {@inheritdoc}
*/
......@@ -42,7 +71,7 @@ public function access(Route $route, Request $request) {
* {@inheritdoc}
*/
public function accessEditEntityField(EntityInterface $entity, $field_name) {
return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
return $entity->access('update') && ($field = $this->fieldInfo->getField($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
}
/**
......@@ -53,10 +82,10 @@ protected function validateAndUpcastRequestAttributes(Request $request) {
if (!is_object($entity = $request->attributes->get('entity'))) {
$entity_id = $entity;
$entity_type = $request->attributes->get('entity_type');
if (!$entity_type || !entity_get_info($entity_type)) {
if (!$entity_type || !$this->entityManager->getDefinition($entity_type)) {
throw new NotFoundHttpException();
}
$entity = entity_load($entity_type, $entity_id);
$entity = $this->entityManager->getStorageController($entity_type)->load($entity_id);
if (!$entity) {
throw new NotFoundHttpException();
}
......@@ -65,7 +94,7 @@ protected function validateAndUpcastRequestAttributes(Request $request) {
// Validate the field name and language.
$field_name = $request->attributes->get('field_name');
if (!$field_name || !field_info_instance($entity->entityType(), $field_name, $entity->bundle())) {
if (!$field_name || !$this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $field_name)) {
throw new NotFoundHttpException();
}
$langcode = $request->attributes->get('langcode');
......
......@@ -89,6 +89,34 @@ function testUserWithoutPermission() {
$response = $this->retrieveMetadata(array('node/1/body/und/full'));
$this->assertIdentical('{}', $response);
$this->assertResponse(403);
// Edit's JavaScript would never hit these endpoints if the metadata was
// empty as above, but we need to make sure that malicious users aren't able
// to use any of the other endpoints either.
$response = $this->retrieveAttachments(array('form'));
// @todo Uncomment the below once https://drupal.org/node/2063303 is fixed.
// $this->assertIdentical('[]', $response);
$this->assertResponse(403);
$response = $this->retrieveFieldForm('node/1/body/und/full');
// @todo Uncomment the below once https://drupal.org/node/2063303 is fixed.
// $this->assertIdentical('[]', $response);
$this->assertResponse(403);
$edit = array();
$edit['form_id'] = 'edit_field_form';
$edit['form_token'] = 'xIOzMjuc-PULKsRn_KxFn7xzNk5Bx7XKXLfQfw1qOnA';
$edit['form_build_id'] = 'form-kVmovBpyX-SJfTT5kY0pjTV35TV-znor--a64dEnMR8';
$edit['body[und][0][summary]'] = '';
$edit['body[und][0][value]'] = '<p>Malicious content.</p>';
$edit['body[und][0][format]'] = 'filtered_html';
$edit['op'] = t('Save');
$response = $this->submitFieldForm('node/1/body/und/full', $edit);
// @todo Uncomment the below once https://drupal.org/node/2063303 is fixed.
// $this->assertIdentical('[]', $response);
$this->assertResponse(403);
$response = $this->saveEntity('node/1');
// @todo Uncomment the below once https://drupal.org/node/2063303 is fixed.
// $this->assertIdentical('[]', $response);
$this->assertResponse(403);
}
/**
......
<?php
/**
* @file
* Contains \Drupal\edit\Tests\edit\Access\EditEntityAccessCheckTest.
*/
namespace Drupal\edit\Tests\edit\Access;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Drupal\Core\Access\AccessCheckInterface;
use Drupal\edit\Access\EditEntityAccessCheck;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Entity\EntityInterface;
/**
* Tests the edit entity access controller.
*
* @group Drupal
* @group Edit
*
* @see \Drupal\edit\Access\EditEntityAccessCheck
*/
class EditEntityAccessCheckTest extends UnitTestCase {
/**
* The tested access checker.
*
* @var \Drupal\edit\Access\EditEntityAccessCheck
*/
protected $editAccessCheck;
/**
* The mocked entity manager.
*
* @var \Drupal\Core\Entity\EntityManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
/**
* The mocked entity storage controller.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityStorageController;
public static function getInfo() {
return array(
'name' => 'Edit entity access check test',
'description' => 'Unit test of edit entity access check.',
'group' => 'Edit'
);
}
protected function setUp() {
$this->entityManager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager')
->disableOriginalConstructor()
->getMock();
$this->entityStorageController = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface');
$this->entityManager->expects($this->any())
->method('getStorageController')
->will($this->returnValue($this->entityStorageController));
$this->editAccessCheck = new EditEntityAccessCheck($this->entityManager);
}
/**
* Tests the appliesTo method for the access checker.
*/
public function testAppliesTo() {
$this->assertEquals($this->editAccessCheck->appliesTo(), array('_access_edit_entity'), 'Access checker returned the expected appliesTo() array.');
}
/**
* Provides test data for testAccess().
*
* @see \Drupal\edit\Tests\edit\Access\EditEntityAccessCheckTest::testAccess()
*/
public function providerTestAccess() {
$editable_entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest')
->disableOriginalConstructor()
->getMock();
$editable_entity->expects($this->any())
->method('access')
->will($this->returnValue(TRUE));
$non_editable_entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest')
->disableOriginalConstructor()
->getMock();
$non_editable_entity->expects($this->any())
->method('access')
->will($this->returnValue(FALSE));
$data = array();
$data[] = array($editable_entity, AccessCheckInterface::ALLOW);
$data[] = array($non_editable_entity, AccessCheckInterface::DENY);
return $data;
}
/**
* Tests the method for checking access to routes.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* A mocked entity.
* @param bool|null $expected_result
* The expected result of the access call.
*
* @dataProvider providerTestAccess
*/
public function testAccess(EntityInterface $entity, $expected_result) {
$route = new Route('/edit/form/test_entity/1/body/und/full', array(), array('_access_edit_entity' => 'TRUE'));
$request = new Request();
// Prepare the request to be valid.
$request->attributes->set('entity', $entity);
$request->attributes->set('entity_type', 'test_entity');
$access = $this->editAccessCheck->access($route, $request);
$this->assertSame($expected_result, $access);
}
/**
* Tests the access method with an undefined entity type.
*
* @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function testAccessWithUndefinedEntityType() {
$route = new Route('/edit/form/test_entity/1/body/und/full', array(), array('_access_edit_entity' => 'TRUE'));
$request = new Request();
$request->attributes->set('entity_type', 'non_valid');
$this->entityManager->expects($this->once())
->method('getDefinition')
->with('non_valid')
->will($this->returnValue(NULL));
$this->editAccessCheck->access($route, $request);
}
/**
* Tests the access method with a non existing entity.
*
* @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function testAccessWithNotExistingEntity() {
$route = new Route('/edit/form/test_entity/1/body/und/full', array(), array('_access_edit_entity_field' => 'TRUE'));
$request = new Request();
$request->attributes->set('entity_type', 'entity_test');
$request->attributes->set('entity', 1);
$this->entityManager->expects($this->once())
->method('getDefinition')
->with('entity_test')
->will($this->returnValue(array('id' => 'entity_test')));
$this->entityStorageController->expects($this->once())
->method('load')
->with(1)
->will($this->returnValue(NULL));
$this->editAccessCheck->access($route, $request);
}
}
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