Permalink
Browse files

Merge branch 'MDL-36754-master' of git://github.com/andrewnicols/moodle

  • Loading branch information...
dmonllao committed Aug 14, 2018
2 parents ed9d61e + 7fa3089 commit 3ff9233a18120f9c6651f3cdfbfea3ff10565742
@@ -27,6 +27,8 @@
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
/**
* Data provider class.
@@ -41,7 +43,10 @@
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\subsystem\plugin_provider {
\core_privacy\local\request\subsystem\plugin_provider,
// We store a userkey for token-based file access.
\core_privacy\local\request\subsystem\provider {
/**
* Returns metadata.
@@ -65,7 +70,95 @@ public static function get_metadata(collection $collection) : collection {
'timemodified' => 'privacy:metadata:files:timemodified',
], 'privacy:metadata:files');
$collection->add_subsystem_link('core_userkey', [], 'privacy:metadata:core_userkey');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* This is currently just the user context.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid) : contextlist {
$sql = "SELECT ctx.id
FROM {user_private_key} k
JOIN {user} u ON k.userid = u.id
JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel
WHERE k.userid = :userid AND k.script = :script";
$params = [
'userid' => $userid,
'contextlevel' => CONTEXT_USER,
'script' => 'core_files',
];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
// If the user has data, then only the CONTEXT_USER should be present so get the first context.
$contexts = $contextlist->get_contexts();
if (count($contexts) == 0) {
return;
}
// Sanity check that context is at the user context level, then get the userid.
$context = reset($contexts);
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Export associated userkeys.
$subcontext = [
get_string('files'),
];
\core_userkey\privacy\provider::export_userkeys($context, $subcontext, 'core_files');
}
/**
* Delete all use data which matches the specified deletion_criteria.
*
* @param context $context A user context.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
// Sanity check that context is at the user context level, then get the userid.
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('core_files', $context->instanceid);
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
// If the user has data, then only the user context should be present so get the first context.
$contexts = $contextlist->get_contexts();
if (count($contexts) == 0) {
return;
}
// Sanity check that context is at the user context level, then get the userid.
$context = reset($contexts);
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Delete all the userkeys for core_files..
\core_userkey\privacy\provider::delete_userkeys('core_files', $context->instanceid);
}
}
@@ -37,3 +37,4 @@
$string['privacy:metadata:files:timecreated'] = 'The time when the file was created';
$string['privacy:metadata:files:timemodified'] = 'The time when the file was last modified';
$string['privacy:metadata:files:userid'] = 'The user who created the file';
$string['privacy:metadata:core_userkey'] = 'A private token is generated and stored. This token can be used to access Moodle files without requiring you to log in.';
@@ -455,30 +455,41 @@ function file_prepare_draft_area(&$draftitemid, $contextid, $component, $fileare
* Passing a new option reverse = true in the $options var will make the function to convert actual URLs in $text to encoded URLs
* in the @@PLUGINFILE@@ form.
*
* @category files
* @global stdClass $CFG
* @param string $text The content that may contain ULRs in need of rewriting.
* @param string $file The script that should be used to serve these files. pluginfile.php, draftfile.php, etc.
* @param int $contextid This parameter and the next two identify the file area to use.
* @param string $component
* @param string $filearea helps identify the file area.
* @param int $itemid helps identify the file area.
* @param array $options text and file options ('forcehttps'=>false), use reverse = true to reverse the behaviour of the function.
* @return string the processed text.
* @param string $text The content that may contain ULRs in need of rewriting.
* @param string $file The script that should be used to serve these files. pluginfile.php, draftfile.php, etc.
* @param int $contextid This parameter and the next two identify the file area to use.
* @param string $component
* @param string $filearea helps identify the file area.
* @param int $itemid helps identify the file area.
* @param array $options
* bool $options.forcehttps Force the user of https
* bool $options.reverse Reverse the behaviour of the function
* bool $options.includetoken Use a token for authentication
* string The processed text.
*/
function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $filearea, $itemid, array $options=null) {
global $CFG;
global $CFG, $USER;
$options = (array)$options;
if (!isset($options['forcehttps'])) {
$options['forcehttps'] = false;
}
if (!$CFG->slasharguments) {
$file = $file . '?file=';
$baseurl = "{$CFG->wwwroot}/{$file}";
if (!empty($options['includetoken'])) {
$token = get_user_key('core_files', $USER->id);
$finalfile = basename($file);
$tokenfile = "token{$finalfile}";
$file = substr($file, 0, strlen($file) - strlen($finalfile)) . $tokenfile;
if (!$CFG->slasharguments) {
$baseurl .= "?token={$token}&file=";
} else {
$baseurl .= "/{$token}";
}
}
$baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
$baseurl .= "/{$contextid}/{$component}/{$filearea}/";
if ($itemid !== null) {
$baseurl .= "$itemid/";
@@ -3112,9 +3112,10 @@ function validate_user_key($keyvalue, $script, $instance) {
* @uses PARAM_ALPHANUM
* @param string $script unique script identifier
* @param int $instance optional instance id
* @param string $keyvalue The key. If not supplied, this will be fetched from the current session.
* @return int Instance ID
*/
function require_user_key_login($script, $instance=null) {
function require_user_key_login($script, $instance = null, $keyvalue = null) {
global $DB;
if (!NO_MOODLE_COOKIES) {
@@ -3124,7 +3125,9 @@ function require_user_key_login($script, $instance=null) {
// Extra safety.
\core\session\manager::write_close();
$keyvalue = required_param('key', PARAM_ALPHANUM);
if (null === $keyvalue) {
$keyvalue = required_param('key', PARAM_ALPHANUM);
}
$key = validate_user_key($keyvalue, $script, $instance);
@@ -206,6 +206,11 @@ class user_picture implements renderable {
*/
public $includefullname = false;
/**
* @var bool Include user authentication token.
*/
public $includetoken = false;
/**
* User picture constructor.
*
@@ -403,7 +408,8 @@ public function get_url(moodle_page $page, renderer_base $renderer = null) {
$path .= $page->theme->name.'/';
}
// Set the image URL to the URL for the uploaded file and return.
$url = moodle_url::make_pluginfile_url($contextid, 'user', 'icon', NULL, $path, $filename);
$url = moodle_url::make_pluginfile_url(
$contextid, 'user', 'icon', null, $path, $filename, false, $this->includetoken);
$url->param('rev', $this->user->picture);
return $url;
}
@@ -2503,6 +2503,7 @@ public function spacer(array $attributes = null, $br = false) {
* - class = image class attribute (default 'userpicture')
* - visibletoscreenreaders=true (whether to be visible to screen readers)
* - includefullname=false (whether to include the user's full name together with the user picture)
* - includetoken = false
* @return string HTML fragment
*/
public function user_picture(stdClass $user, array $options = null) {
@@ -1052,6 +1052,79 @@ public function test_file_rewrite_pluginfile_urls() {
$this->assertEquals($originaltext, $finaltext);
}
/**
* Test file_rewrite_pluginfile_urls with includetoken.
*/
public function test_file_rewrite_pluginfile_urls_includetoken() {
global $USER, $CFG;
$CFG->slasharguments = true;
$this->resetAfterTest();
$syscontext = context_system::instance();
$originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">';
$options = ['includetoken' => true];
// Rewrite the content. This will generate a new token.
$finaltext = file_rewrite_pluginfile_urls(
$originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
$token = get_user_key('core_files', $USER->id);
$expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png");
$expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">";
$this->assertEquals($expectedtext, $finaltext);
// Do it again - the second time will use an existing token.
$finaltext = file_rewrite_pluginfile_urls(
$originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
$this->assertEquals($expectedtext, $finaltext);
// Now undo.
$options['reverse'] = true;
$finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
// Compare the final text is the same that the original.
$this->assertEquals($originaltext, $finaltext);
}
/**
* Test file_rewrite_pluginfile_urls with includetoken with slasharguments disabled..
*/
public function test_file_rewrite_pluginfile_urls_includetoken_no_slashargs() {
global $USER, $CFG;
$CFG->slasharguments = false;
$this->resetAfterTest();
$syscontext = context_system::instance();
$originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">';
$options = ['includetoken' => true];
// Rewrite the content. This will generate a new token.
$finaltext = file_rewrite_pluginfile_urls(
$originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
$token = get_user_key('core_files', $USER->id);
$expectedurl = new \moodle_url("/tokenpluginfile.php");
$expectedurl .= "?token={$token}&file=/{$syscontext->id}/user/private/0/image.png";
$expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">";
$this->assertEquals($expectedtext, $finaltext);
// Do it again - the second time will use an existing token.
$finaltext = file_rewrite_pluginfile_urls(
$originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
$this->assertEquals($expectedtext, $finaltext);
// Now undo.
$options['reverse'] = true;
$finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
// Compare the final text is the same that the original.
$this->assertEquals($originaltext, $finaltext);
}
/**
* Helpter function to create draft files
*
Oops, something went wrong.

0 comments on commit 3ff9233

Please sign in to comment.