Core and composer updates

This commit is contained in:
Rob Davies 2017-07-03 16:47:07 +01:00
parent a82634bb98
commit 62cac30480
1118 changed files with 21770 additions and 6306 deletions

View file

@ -904,6 +904,14 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
// If we made it this far it's safe to record this file in the database.
$file->save();
$files[$i] = $file;
// Allow an anonymous user who creates a non-public file to see it. See
// \Drupal\file\FileAccessControlHandler::checkAccess().
if ($user->isAnonymous() && $destination_scheme !== 'public') {
$session = \Drupal::request()->getSession();
$allowed_temp_files = $session->get('anonymous_allowed_file_ids', []);
$allowed_temp_files[$file->id()] = $file->id();
$session->set('anonymous_allowed_file_ids', $allowed_temp_files);
}
}
// Add files to the cache.

View file

@ -7,7 +7,7 @@ migration_tags:
source:
plugin: d6_file
constants:
# source_base_path must be set by the tool configuring this migration. It
# The tool configuring this migration must set source_base_path. It
# represents the fully qualified path relative to which URIs in the files
# table are specified, and must end with a /. See source_full_path
# configuration in this migration's process pipeline as an example.

View file

@ -13,7 +13,7 @@ process:
source: upload
process:
target_id:
plugin: migration
plugin: migration_lookup
migration: d6_file
source: fid
display: list

View file

@ -14,7 +14,14 @@ source:
settings: {}
process:
entity_type: 'constants/entity_type'
bundle: node_type
bundle:
-
plugin: migration_lookup
migration: d6_node_type
source: node_type
-
plugin: skip_on_empty
method: row
view_mode: 'constants/view_mode'
field_name: 'constants/name'
type: 'constants/type'

View file

@ -15,7 +15,14 @@ source:
progress_indicator: throbber
process:
entity_type: 'constants/entity_type'
bundle: node_type
bundle:
-
plugin: migration_lookup
migration: d6_node_type
source: node_type
-
plugin: skip_on_empty
method: row
field_name: 'constants/name'
form_mode: 'constants/form_mode'
type: 'constants/type'

View file

@ -13,7 +13,7 @@ process:
entity_type: 'constants/entity_type'
bundle:
-
plugin: migration
plugin: migration_lookup
migration: d6_node_type
source: node_type
-

View file

@ -7,8 +7,8 @@ migration_tags:
source:
plugin: d7_file
constants:
# source_base_path must be set by the tool configuring this migration. It
# represents the fully qualified path relative to which uris in the files
# The tool configuring this migration must set source_base_path. It
# represents the fully qualified path relative to which URIs in the files
# table are specified, and must end with a /. See source_full_path
# configuration in this migration's process pipeline as an example.
source_base_path: ''
@ -32,8 +32,7 @@ process:
- '@source_full_path'
- uri
filemime: filemime
# filesize is dynamically computed when file entities are saved, so there is
# no point in migrating it.
# No need to migrate filesize, it is computed when file entities are saved.
# filesize: filesize
status: status
# Drupal 7 didn't keep track of the file's creation or update time -- all it

View file

@ -40,8 +40,20 @@ class FileAccessControlHandler extends EntityAccessControlHandler {
}
elseif ($entity->getOwnerId() == $account->id()) {
// This case handles new nodes, or detached files. The user who uploaded
// the file can always access if it's not yet used.
return AccessResult::allowed();
// the file can access it even if it's not yet used.
if ($account->isAnonymous()) {
// For anonymous users, only the browser session that uploaded the
// file is positively allowed access to it. See file_save_upload().
// @todo Implement \Drupal\Core\Entity\EntityHandlerInterface so that
// services can be more properly injected.
$allowed_fids = \Drupal::service('session')->get('anonymous_allowed_file_ids', []);
if (!empty($allowed_fids[$entity->id()])) {
return AccessResult::allowed();
}
}
else {
return AccessResult::allowed();
}
}
}
@ -79,9 +91,24 @@ class FileAccessControlHandler extends EntityAccessControlHandler {
* {@inheritdoc}
*/
protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
// No user can edit the status of a file. Prevents saving a new file as
// persistent before even validating it.
if ($field_definition->getName() === 'status' && $operation === 'edit') {
// Deny access to fields that should only be set on file creation, and
// "status" which should only be changed based on a file's usage.
$create_only_fields = [
'uri',
'filemime',
'filesize',
];
// The operation is 'edit' when the entity is being created or updated.
// Determine if the entity is being updated by checking if it is new.
$field_name = $field_definition->getName();
if ($operation === 'edit' && $items && ($entity = $items->getEntity()) && !$entity->isNew() && in_array($field_name, $create_only_fields, TRUE)) {
return AccessResult::forbidden();
}
// Regardless of whether the entity exists access should be denied to the
// status field as this is managed via other APIs, for example:
// - \Drupal\file\FileUsage\FileUsageBase::add()
// - \Drupal\file\Plugin\EntityReferenceSelection\FileSelection::createNewEntity()
if ($operation === 'edit' && $field_name === 'status') {
return AccessResult::forbidden();
}
return parent::checkFieldAccess($operation, $field_definition, $account, $items);

View file

@ -25,8 +25,8 @@ interface FileUsageInterface {
* The name of the module using the file.
* @param string $type
* The type of the object that contains the referenced file.
* @param int $id
* The unique, numeric ID of the object containing the referenced file.
* @param string $id
* The unique ID of the object containing the referenced file.
* @param int $count
* (optional) The number of references to add to the object. Defaults to 1.
*/
@ -43,10 +43,10 @@ interface FileUsageInterface {
* (optional) The type of the object that contains the referenced file. May
* be omitted if all module references to a file are being deleted. Defaults
* to NULL.
* @param int $id
* (optional) The unique, numeric ID of the object containing the referenced
* file. May be omitted if all module references to a file are being
* deleted. Defaults to NULL.
* @param string $id
* (optional) The unique ID of the object containing the referenced file.
* May be omitted if all module references to a file are being deleted.
* Defaults to NULL.
* @param int $count
* (optional) The number of references to delete from the object. Defaults
* to 1. Zero may be specified to delete all references to the file within a

View file

@ -6,6 +6,7 @@ use Drupal\Core\Entity\Plugin\Validation\Constraint\ReferenceAccessConstraint;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\file\Entity\File;
use Drupal\node\Entity\NodeType;
use Drupal\user\RoleInterface;
/**
* Uploads a test to a private node and checks access.
@ -110,6 +111,118 @@ class FilePrivateTest extends FileFieldTestBase {
$this->drupalLogin($account);
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that access is denied for another user to the temporary file.');
// As an anonymous user, create a temporary file with no references and
// confirm that only the session that uploaded it may view it.
$this->drupalLogout();
user_role_change_permissions(
RoleInterface::ANONYMOUS_ID,
[
"create $type_name content" => TRUE,
'access content' => TRUE,
]
);
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = ['files[' . $field_name . '_0]' => drupal_realpath($test_file->getFileUri())];
$this->drupalPostForm(NULL, $edit, t('Upload'));
/** @var \Drupal\file\FileStorageInterface $file_storage */
$file_storage = $this->container->get('entity.manager')->getStorage('file');
$files = $file_storage->loadByProperties(['uid' => 0]);
$this->assertEqual(1, count($files), 'Loaded one anonymous file.');
$file = end($files);
$this->assertTrue($file->isTemporary(), 'File is temporary.');
$usage = $this->container->get('file.usage')->listUsage($file);
$this->assertFalse($usage, 'No file usage found.');
$file_url = file_create_url($file->getFileUri());
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the temporary file.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the temporary file.');
// As an anonymous user, create a permanent file, then remove all
// references to the file (so that it becomes temporary again) and confirm
// that only the session that uploaded it may view it.
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['files[' . $field_name . '_0]'] = drupal_realpath($test_file->getFileUri());
$this->drupalPostForm(NULL, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$file_id = $new_node->{$field_name}->target_id;
$file = File::load($file_id);
$this->assertTrue($file->isPermanent(), 'File is permanent.');
// Remove the reference to this file.
$new_node->{$field_name} = [];
$new_node->save();
$file = File::load($file_id);
$this->assertTrue($file->isTemporary(), 'File is temporary.');
$usage = $this->container->get('file.usage')->listUsage($file);
$this->assertFalse($usage, 'No file usage found.');
$file_url = file_create_url($file->getFileUri());
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the file whose references were removed.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the file whose references were removed.');
// As an anonymous user, create a permanent file that is referenced by a
// published node and confirm that all anonymous users may view it.
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['files[' . $field_name . '_0]'] = drupal_realpath($test_file->getFileUri());
$this->drupalPostForm(NULL, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$file = File::load($new_node->{$field_name}->target_id);
$this->assertTrue($file->isPermanent(), 'File is permanent.');
$usage = $this->container->get('file.usage')->listUsage($file);
$this->assertTrue($usage, 'File usage found.');
$file_url = file_create_url($file->getFileUri());
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the permanent file that is referenced by a published node.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that another anonymous user also has access to the permanent file that is referenced by a published node.');
// As an anonymous user, create a permanent file that is referenced by an
// unpublished node and confirm that no anonymous users may view it (even
// the session that uploaded the file) because they cannot view the
// unpublished node.
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['files[' . $field_name . '_0]'] = drupal_realpath($test_file->getFileUri());
$this->drupalPostForm(NULL, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$new_node->setPublished(FALSE);
$new_node->save();
$file = File::load($new_node->{$field_name}->target_id);
$this->assertTrue($file->isPermanent(), 'File is permanent.');
$usage = $this->container->get('file.usage')->listUsage($file);
$this->assertTrue($usage, 'File usage found.');
$file_url = file_create_url($file->getFileUri());
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that the anonymous uploader cannot access the permanent file when it is referenced by an unpublished node.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the permanent file when it is referenced by an unpublished node.');
}
}

View file

@ -85,11 +85,30 @@ class AccessTest extends KernelTestBase {
}
/**
* Tests that the status field is not editable.
* Tests file entity field access.
*
* @see \Drupal\file\FileAccessControlHandler::checkFieldAccess()
*/
public function testStatusFieldIsNotEditable() {
public function testCheckFieldAccess() {
\Drupal::currentUser()->setAccount($this->user1);
$this->assertFalse($this->file->get('status')->access('edit'));
/** @var \Drupal\file\FileInterface $file */
$file = File::create([
'uri' => 'public://test.png'
]);
// While creating a file entity access will be allowed for create-only
// fields.
$this->assertTrue($file->get('uri')->access('edit'));
$this->assertTrue($file->get('filemime')->access('edit'));
$this->assertTrue($file->get('filesize')->access('edit'));
// Access to the status field is denied whilst creating a file entity.
$this->assertFalse($file->get('status')->access('edit'));
$file->save();
// After saving the entity is no longer new and, therefore, access to
// create-only fields and the status field will be denied.
$this->assertFalse($file->get('uri')->access('edit'));
$this->assertFalse($file->get('filemime')->access('edit'));
$this->assertFalse($file->get('filesize')->access('edit'));
$this->assertFalse($file->get('status')->access('edit'));
}
/**

View file

@ -45,6 +45,7 @@ class FileItemValidationTest extends KernelTestBase {
'status' => 1,
]);
$this->user->save();
$this->container->get('current_user')->setAccount($this->user);
}
/**
@ -85,6 +86,7 @@ class FileItemValidationTest extends KernelTestBase {
// Test for max filesize.
$file = File::create([
'uri' => 'vfs://drupal_root/sites/default/files/test.txt',
'uid' => $this->user->id(),
]);
$file->setPermanent();
$file->save();

View file

@ -12,19 +12,25 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateUploadEntityDisplayTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->migrateFields();
$this->executeMigration('d6_upload_entity_display');
}
/**
* Tests the Drupal 6 upload settings to Drupal 8 entity display migration.
* Tests Drupal 6 upload settings to Drupal 8 entity display migration.
*/
public function testUploadEntityDisplay() {
$this->executeMigration('d6_upload_entity_display');
$display = EntityViewDisplay::load('node.page.default');
$component = $display->getComponent('upload');
$this->assertIdentical('file_default', $component['type']);
@ -41,4 +47,23 @@ class MigrateUploadEntityDisplayTest extends MigrateDrupal6TestBase {
$this->assertIdentical(['node', 'page', 'default', 'upload'], $this->getMigration('d6_upload_entity_display')->getIdMap()->lookupDestinationID(['page']));
}
/**
* Tests that entity displays are ignored appropriately.
*
* Entity displays should be ignored when they belong to node types which
* were not migrated.
*/
public function testSkipNonExistentNodeType() {
// The "story" node type is migrated by d6_node_type but we need to pretend
// that it didn't occur, so record that in the map table.
$this->mockFailure('d6_node_type', ['type' => 'story']);
// d6_upload_entity_display should skip over the "story" node type config
// because, according to the map table, it didn't occur.
$migration = $this->getMigration('d6_upload_entity_display');
$this->executeMigration($migration);
$this->assertNull($migration->getIdMap()->lookupDestinationIds(['node_type' => 'story'])[0][0]);
}
}

View file

@ -12,19 +12,25 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateUploadEntityFormDisplayTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->migrateFields();
$this->executeMigration('d6_upload_entity_form_display');
}
/**
* Tests the Drupal 6 upload settings to Drupal 8 entity form display migration.
* Tests Drupal 6 upload settings to Drupal 8 entity form display migration.
*/
public function testUploadEntityFormDisplay() {
$this->executeMigration('d6_upload_entity_form_display');
$display = EntityFormDisplay::load('node.page.default');
$component = $display->getComponent('upload');
$this->assertIdentical('file_generic', $component['type']);
@ -41,4 +47,23 @@ class MigrateUploadEntityFormDisplayTest extends MigrateDrupal6TestBase {
$this->assertIdentical(['node', 'page', 'default', 'upload'], $this->getMigration('d6_upload_entity_form_display')->getIdMap()->lookupDestinationID(['page']));
}
/**
* Tests that entity displays are ignored appropriately.
*
* Entity displays should be ignored when they belong to node types which
* were not migrated.
*/
public function testSkipNonExistentNodeType() {
// The "story" node type is migrated by d6_node_type but we need to pretend
// that it didn't occur, so record that in the map table.
$this->mockFailure('d6_node_type', ['type' => 'story']);
// d6_upload_entity_form_display should skip over the "story" node type
// config because according to the map table, it didn't occur.
$migration = $this->getMigration('d6_upload_entity_form_display');
$this->executeMigration($migration);
$this->assertNull($migration->getIdMap()->lookupDestinationIds(['node_type' => 'story'])[0][0]);
}
}

View file

@ -12,6 +12,11 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateUploadFieldTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/

View file

@ -12,6 +12,11 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateUploadInstanceTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/

View file

@ -13,6 +13,11 @@ use Drupal\node\Entity\Node;
*/
class MigrateUploadTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/