MDL-69028 repository: Put a rate limit on draft file uploads

This commit is contained in:
Shamim Rezaie
2021-05-04 17:23:11 +02:00
committed by Jenkins
parent f40dfdf51e
commit 9c11ceac1f
7 changed files with 168 additions and 1 deletions
+60 -1
View File
@@ -40,6 +40,16 @@ define('IGNORE_FILE_MERGE', -1);
*/
define('FILE_AREA_MAX_BYTES_UNLIMITED', -1);
/**
* Capacity of the draft area bucket when using the leaking bucket technique to limit the draft upload rate.
*/
define('DRAFT_AREA_BUCKET_CAPACITY', 50);
/**
* Leaking rate of the draft area bucket when using the leaking bucket technique to limit the draft upload rate.
*/
define('DRAFT_AREA_BUCKET_LEAK', 0.2);
require_once("$CFG->libdir/filestorage/file_exceptions.php");
require_once("$CFG->libdir/filestorage/file_storage.php");
require_once("$CFG->libdir/filestorage/zip_packer.php");
@@ -390,7 +400,7 @@ function file_get_unused_draft_itemid() {
* @return string|null returns string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
*/
function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null) {
global $CFG, $USER, $CFG;
global $CFG, $USER;
$options = (array)$options;
if (!isset($options['subdirs'])) {
@@ -606,6 +616,55 @@ function file_is_draft_area_limit_reached($draftitemid, $areamaxbytes, $newfiles
return false;
}
/**
* Returns whether a user has reached their draft area upload rate.
*
* @param int $userid The user id
* @return bool
*/
function file_is_draft_areas_limit_reached(int $userid): bool {
global $CFG;
$capacity = $CFG->draft_area_bucket_capacity ?? DRAFT_AREA_BUCKET_CAPACITY;
$leak = $CFG->draft_area_bucket_leak ?? DRAFT_AREA_BUCKET_LEAK;
$since = time() - floor($capacity / $leak); // The items that were in the bucket before this time are already leaked by now.
// We are going to be a bit generous to the user when using the leaky bucket
// algorithm below. We are going to assume that the bucket is empty at $since.
// We have to do an assumption here unless we really want to get ALL user's draft
// items without any limit and put all of them in the leaking bucket.
// I decided to favour performance over accuracy here.
$fs = get_file_storage();
$items = $fs->get_user_draft_items($userid, $since);
$items = array_reverse($items); // So that the items are sorted based on time in the ascending direction.
// We only need to store the time that each element in the bucket is going to leak. So $bucket is array of leaking times.
$bucket = [];
foreach ($items as $item) {
$now = $item->timemodified;
// First let's see if items can be dropped from the bucket as a result of leakage.
while (!empty($bucket) && ($now >= $bucket[0])) {
array_shift($bucket);
}
// Calculate the time that the new item we put into the bucket will be leaked from it, and store it into the bucket.
if ($bucket) {
$bucket[] = max($bucket[count($bucket) - 1], $now) + (1 / $leak);
} else {
$bucket[] = $now + (1 / $leak);
}
}
// Recalculate the bucket's content based on the leakage until now.
$now = time();
while (!empty($bucket) && ($now >= $bucket[0])) {
array_shift($bucket);
}
return count($bucket) >= $capacity;
}
/**
* Get used space of files
* @global moodle_database $DB