Skip to content
Snippets Groups Projects
Commit 7fa2077c authored by Valery Lourie's avatar Valery Lourie
Browse files

Security issue #173496 by alexpott,valthebald: remove insecure file example

parent 66cb9f2b
No related branches found
No related tags found
No related merge requests found
#
# This link will appear in the "Tools" menu.
#
file_example.fileapi:
title: File Example
parent: file_example.description
route_name: file_example.fileapi
<?php
/**
* @file
* Examples demonstrating the Drupal File API.
*/
use Drupal\Core\StreamWrapper\StreamWrapperManager;
/**
* @defgroup file_example Example: Files
* @ingroup examples
* @{
* Examples demonstrating the Drupal File API.
*
* The File Example module is part of the Examples for Developers Project and
* provides a variety of examples for the Developers project page. Some
* concepts we demonstrate with this module:
*
* * Creating, moving and deleting files, and reading and writing from them.
*
* * Using files that Drupal can manage via its Entity API ("managed files"),
* and unmanaged files (the usual kind of file programs deal with).
*
* * Creating and setting up directories with the right permissions, and with
* .htaccess files that prevent unwanted accesses.
*
* * Allowing restricted access to files the way Drupal private files are
* downloaded.
*
* * Using special "stream" URIs like public://, private://, and temporary://.
* Drupal has good support for this PHP language feature. You can implement
* new file schemes as well; see the Stream Wrapper Example for how to do
* that.If you enable the stream_wrapper_example module, you can use it
* together with the File Example to test how a custom stream works.
*
* To demonstrate all of this, the File Example implements a form that lets you
* play with files. Read src/Form/FileExampleReadWriteForm.php to see
* demonstrations of the various File API functions you will want to use in your
* code.
*
* Some links for further information on the File API and related information:
*
* @link http://drupal.org/project/examples Examples for Developers project
* page. @endlink
* @link file File summary on api.drupal.org @endlink for the function summary.
*/
/**
* Implements hook_file_download().
*
* This hook allows modules to enforce permissions on file downloads whenever
* Drupal is handling file download, as opposed to the web server bypassing
* Drupal and returning the file from a public directory. Modules can also
* provide headers to specify information like the file's name or MIME type.
*
* For our example module, we want to be able to see the temporary, private,
* and session (our test stream wrapper / file scheme). In general, you really
* would NEVER give general access to your temporary, and you certainly wouldn't
* do it for your private files. So we demonstrate this here, but kids, don't
* try this at home ;-) Remember: keep your files secure!
*
* For hook_file_download() to get called at all, your code needs set up your
* routes so that the download link uses FileDownloadController::download() as
* a controller. FileDownloadController::download() enforces access restrictions
* on the files it managed, in part by invoking hook_file_downloads(). See the
* File Example's routing file to see how to do this.
*
* @param string $uri
* The URI of the file.
*
* @return mixed
* If the user does not have permission to access the file, return -1. If the
* user has permission, return an array with the appropriate headers. If the
* file is not controlled by the current module, the return value should be
* NULL.
*
* @see file_download()
* @see hook_file_download()
* @see file_example.routing.yml
* @see \Drupal\system\FileDownloadController::download()
*/
function file_example_file_download($uri) {
$scheme = StreamWrapperManager::getScheme($uri);
if (in_array($scheme, ['private', 'temporary', 'session'])) {
$permission = "read $scheme files";
$current_user = \Drupal::currentUser();
$account = $current_user->getAccount();
if ($account->hasPermission($permission)) {
return [
'Content-Type: text/plain',
];
}
}
}
/**
* @} End of "defgroup file_example".
*/
'use file example':
title: Use the examples in the File Example module.
#
# We use the following permissions in our hook_file_download implementation.
# See file_example.module for details.
#
'read private files':
title: See private files in the File Example module demo.
'read temporary files':
title: See temporary files in the File Example module demo.
'read session files':
title: See session files in the File Example module demo.
# Main page for our example.
file_example.fileapi:
path: '/examples/file_example'
defaults:
_form: '\Drupal\file_example\Form\FileExampleReadWriteForm'
_title: 'File Example: Use the File API to read/write a file'
requirements:
_permission: 'use file example'
This diff is collapsed.
<?php
namespace Drupal\Tests\file_example\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Tests\examples\Functional\ExamplesBrowserTestBase;
/**
* Functional tests for the File Example module.
*
* @ingroup file_example
*
* @group file_example
* @group examples
*/
class FileExampleTest extends ExamplesBrowserTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['file_example'];
/**
* Test the basic File Example UI.
*
* - Create a directory to work with.
* - For each scheme create and read files using each of the three methods.
*/
public function testFileExampleBasic() {
$assert = $this->assertSession();
// Our test user needs to access some non-standard file types,
// so we bless it accordingly.
$permissions = [
'use file example',
'read private files',
'read temporary files',
'read session files',
];
$priviledged_user = $this->drupalCreateUser($permissions);
$this->drupalLogin($priviledged_user);
$expected_text = [
'Write managed file' => 'Saved managed file',
'Write unmanaged file' => 'Saved file as',
'Unmanaged using PHP' => 'Saved file as',
];
// For each of the three buttons == three write types.
$buttons = [
'Write managed file',
'Write unmanaged file',
'Unmanaged using PHP',
];
foreach ($buttons as $button) {
// For each scheme supported by Drupal + the session:// wrapper,
// which is defined in the stream_wrapper_exampnle.
$schemes = ['public', 'private', 'temporary', 'session'];
foreach ($schemes as $scheme) {
// Create a directory for use.
$dirname = $scheme . '://' . $this->randomMachineName(10);
// Directory does not yet exist; assert that.
$edit = [
'directory_name' => $dirname,
];
$this->drupalPostForm('examples/file_example', $edit, 'Check to see if directory exists');
$assert->pageTextContains((string) new FormattableMarkup('Directory @dirname does not exist', ['@dirname' => $dirname]));
$this->drupalPostForm('examples/file_example', $edit, 'Create directory');
$assert->pageTextContains((string) new FormattableMarkup('Directory @dirname is ready for use', ['@dirname' => $dirname]));
$this->drupalPostForm('examples/file_example', $edit, 'Check to see if directory exists');
$assert->pageTextContains((string) new FormattableMarkup('Directory @dirname exists', ['@dirname' => $dirname]));
// Create a file in the directory we created.
$content = $this->randomMachineName(30);
$filename = $dirname . '/' . $this->randomMachineName(30) . '.txt';
// Assert that the file we're about to create does not yet exist.
$edit = [
'fileops_file' => $filename,
];
$this->drupalPostForm('examples/file_example', $edit, 'Check to see if file exists');
$assert->pageTextContains((string) new FormattableMarkup('The file @filename does not exist', ['@filename' => $filename]));
$this->verbose("Processing button=$button, scheme=$scheme, dir=$dirname, file=$filename");
$edit = [
'write_contents' => $content,
'destination' => $filename,
];
$this->drupalPostForm('examples/file_example', $edit, $button);
$this->verbose($expected_text[$button]);
$assert->pageTextContains($expected_text[$button]);
// Capture the name of the output file, as it might have changed due
// to file renaming.
$element = $this->xpath('//span[@id="uri"]');
$output_filename = (string) $element[0]->getText();
$this->verbose($output_filename);
// Click the link provided that is an easy way to get the data for
// checking and make sure that the data we put in is what we get out.
if (!in_array($scheme, [])) {
$this->clickLink('this URL');
$assert->statusCodeEquals(200);
// assertText give sketchy answers when the content is *exactly* the
// contents of the buffer, so let's do something less fragile.
// $this->assertText($content);
$buffer = $this->getSession()->getPage()->getContent();
$this->assertEquals($content, $buffer);
}
// Verify that the file exists.
$edit = [
'fileops_file' => $filename,
];
$this->drupalPostForm('examples/file_example', $edit, 'Check to see if file exists');
$assert->pageTextContains("The file $filename exists");
// Now read the file that got written above and verify that we can use
// the writing tools.
$edit = [
'fileops_file' => $output_filename,
];
$this->drupalPostForm('examples/file_example', $edit, 'Read the file and store it locally');
$assert->pageTextContains('The file was read and copied');
$edit = [
'fileops_file' => $filename,
];
$this->drupalPostForm('examples/file_example', $edit, 'Delete file');
$assert->pageTextContains('Successfully deleted');
$this->drupalPostForm('examples/file_example', $edit, 'Check to see if file exists');
$assert->pageTextContains((string) new FormattableMarkup('The file @filename does not exist', ['@filename' => $filename]));
$edit = [
'directory_name' => $dirname,
];
$this->drupalPostForm('examples/file_example', $edit, 'Delete directory');
$this->drupalPostForm('examples/file_example', $edit, 'Check to see if directory exists');
$assert->pageTextContains((string) new FormattableMarkup('Directory @dirname does not exist', ['@dirname' => $dirname]));
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment