diff --git a/classes/check/presigned_urls.php b/classes/check/presigned_urls.php new file mode 100644 index 00000000..c471920f --- /dev/null +++ b/classes/check/presigned_urls.php @@ -0,0 +1,272 @@ +. + +namespace tool_objectfs\check; + +use action_link; +use core\check\check; +use core\check\result; +use moodle_url; +use stored_file; +use tool_objectfs\local\manager; + +/** + * Test that requested pre-signed URLs respond with the expected file headers. + * + * @package tool_objectfs + * @author Andrew Madden + * @copyright 2022 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class presigned_urls extends check { + + /** + * A link to a place to action this + * + * @return action_link + */ + public function get_action_link(): ?\action_link { + return new \action_link( + new \moodle_url('/admin/settings.php?section=tool_objectfs_settings'), + get_string('pluginsettings', 'tool_objectfs')); + } + + /** + * Test that presigned urls are set up correctly. + * + * @return result An object with information about the test. + * + * @throws \coding_exception + * @throws \dml_exception + * @throws \file_exception + * @throws \moodle_exception + * @throws \stored_file_creation_exception + */ + public function get_result(): result { + $config = manager::get_objectfs_config(); + if (empty($config->filesystem)) { + return new result(result::INFO, get_string('check:presigned_urls:infofilesystem', 'tool_objectfs')); + } + + $fs = new $config->filesystem(); + if (!$fs->supports_presigned_urls()) { + return new result(result::INFO, get_string('check:presigned_urls:infofilesystempresigned', 'tool_objectfs')); + } + + $testfiles = self::load_files($fs); + $requests = $this->generate_requests($testfiles); + + // Use first file to check that presigned urls will be used. + if (!$fs->should_redirect_to_presigned_url($testfiles[0]->get_contenthash(), $testfiles[0])) { + return new result(result::INFO, get_string('check:presigned_urls:infopresignedsetup', 'tool_objectfs')); + } + + // Make request to Moodle server to get the files. + $headers = $this->fetch_headers($requests); + $presignedrequests = []; + foreach ($testfiles as $testfile) { + $header = array_shift($headers); + ['httpcodes' => $httpcodes, 'headerparts' => $headerparts] = $this->decode_header($header); + + // Check that we were redirected to a presigned url. + if (!in_array(303, $httpcodes) || !array_key_exists('Location', $headerparts)) { + return new result(result::ERROR, + get_string('check:presigned_urls:errorredirect', 'tool_objectfs', ['filename' => $testfile->get_filename()])); + } + + // Now use the generated presigned urls to make the requests for the files. + // It's safer to recreate the presigned url using Moodle API. + $presignedurl = new moodle_url($headerparts['Location']); + $presignedrequests[] = [ + 'url' => $presignedurl->out(false), + 'returntransfer' => true, + ]; + + // Check if filename is as expected. We need to utf8_encode the filename as that is how it is sent in request. + $disposition = $presignedurl->get_param('response-content-disposition'); + if (is_null($disposition) || !str_contains($disposition, utf8_encode($testfile->get_filename()))) { + return new result(result::ERROR, + get_string('check:presigned_urls:errorfilename', 'tool_objectfs', ['filename' => $testfile->get_filename()])); + } + } + + // Now we can fetch the actual files. + $headers = $this->fetch_headers($presignedrequests); + foreach ($testfiles as $testfile) { + $header = array_shift($headers); + ['httpcodes' => $httpcodes, 'headerparts' => $headerparts] = $this->decode_header($header); + + // Check that we successfully retrieved file. + if (!in_array(200, $httpcodes)) { + return new result(result::ERROR, + get_string('check:presigned_urls:errorpresignedfetch', 'tool_objectfs', ['filename' => $testfile->get_filename()])); + } + + // Check if content length is as expected. + if (!array_key_exists('content-length', $headerparts) || $headerparts['content-length'] != $testfile->get_filesize()) { + return new result(result::ERROR, + get_string('check:presigned_urls:errorfilesize', 'tool_objectfs', ['filename' => $testfile->get_filename()])); + } + } + return new result(result::OK, get_string('check:presigned_urls:success', 'tool_objectfs')); + } + + /** + * Get array of files created from fixtures. + * + * @param \file_system $fs Current file system. + * @return stored_file[] Array of files to test. + * + * @throws \dml_exception + * @throws \file_exception + * @throws \stored_file_creation_exception + */ + public static function load_files(\file_system $fs): array { + global $CFG; + $filestorage = get_file_storage(); + $fixturespath = $CFG->dirroot.'/admin/tool/objectfs/tests/fixtures/'; + $fixturesfiles = glob($fixturespath.'*'); + $syscontext = \context_system::instance(); + $testfiles = []; + + foreach ($fixturesfiles as $fixturesfile) { + // Filter out possible compressed files. + if (false !== strpos($fixturesfile, '.br')) { + continue; + } + $testfilename = str_replace($fixturespath, '', $fixturesfile); + + $contextid = $syscontext->id; + $component = 'tool_objectfs'; + $filearea = 'settings'; + $itemid = 0; + $filepath = '/'; + + $filerecord = array( + 'contextid' => $contextid, + 'component' => $component, + 'filearea' => $filearea, + 'itemid' => $itemid, + 'filepath' => $filepath, + 'filename' => $testfilename + ); + + $testfile = $filestorage->get_file($contextid, $component, $filearea, $itemid, $filepath, $testfilename); + if (!$testfile) { + $testfile = $filestorage->create_file_from_pathname($filerecord, $fixturesfile); + } + + $contenthash = $testfile->get_contenthash(); + $readable = $fs->is_file_readable_externally_by_hash($contenthash); + if (!$readable) { + $fs->copy_from_local_to_external($contenthash); + } + $testfiles[] = $testfile; + } + + return $testfiles; + } + + + /** + * Generate a file url with adding a param to set 'Expires' header. + * + * @param stored_file $file A moodle file. + * @param int|bool $expires Seconds until the presigned url expires. + * @param bool $forcedownload Whether file should be downloaded. + * @return string A url to fetch a file. + */ + private function generate_file_url(stored_file $file, $expires = false, bool $forcedownload = false): string { + $url = \moodle_url::make_pluginfile_url( + \context_system::instance()->id, + OBJECTFS_PLUGIN_NAME, + 'settings', + 0, + '/', + $file->get_filename(), + $forcedownload + ); + $expires = (-1 !== $expires) ? $expires : false; + if (false !== $expires) { + $url->param('expires', $expires); + } + return $url->out(); + } + + /** + * Pull out the http codes, and the header key pairs from a header string. + * + * @param string $header A header string response. + * @return array list($httpcodes, $headerparts) httpcodes contains list of HTTP codes in header, and header parts contains + * list of key-value pairs decoded from the header. + */ + private function decode_header(string $header): array { + $headerparts = []; + $httpcodes = []; + + $responseparts = explode("\r\n", $header); + foreach ($responseparts as $part) { + if (empty($part)) { + continue; + } + + // First see if it's a HTTP response code. + if (strpos($part, "HTTP") !== false) { + $httpparts = explode (" ", $part); + $httpcodes[] = $httpparts[1]; + } else { + // Otherwise, create a header map. + $headerpair = explode(":", $part, 2); + $headerparts[$headerpair[0]] = trim($headerpair[1]); + } + } + return ['httpcodes' => $httpcodes, 'headerparts' => $headerparts]; + } + + /** + * Fetch headers for a list of curl requests. + * + * @param array $requests Array of curl request options. + * @return array Array of header strings for each request provided. + * + * @throws \coding_exception + */ + private function fetch_headers(array $requests): array { + $curl = new \curl(); + $curl->setopt([ + 'CURLOPT_HEADER' => true, + 'CURLOPT_NOBODY' => true, + ]); + return $curl->download($requests); + } + + /** + * Generate a list of curl request options to fetch multiple Moodle files. + * + * @param array $files List of stored_file objects. + * @return array List of curl request options. + */ + private function generate_requests(array $files): array { + $requests = []; + foreach ($files as $file) { + $requests[] = [ + 'url' => $this->generate_file_url($file), + 'returntransfer' => true, + ]; + } + return $requests; + } +} diff --git a/classes/local/store/object_file_system.php b/classes/local/store/object_file_system.php index 16f2f210..a6e30d75 100644 --- a/classes/local/store/object_file_system.php +++ b/classes/local/store/object_file_system.php @@ -462,10 +462,7 @@ public function xsendfile_file(stored_file $file): bool { } $contenthash = $file->get_contenthash(); - if ($this->presigned_url_configured() && - $this->presigned_url_should_redirect_file($file) && - $this->is_file_readable_externally_by_hash($contenthash)) { - + if ($this->should_redirect_to_presigned_url($contenthash, $file)) { return $this->redirect_to_presigned_url($contenthash, headers_list()); } @@ -495,9 +492,7 @@ public function xsendfile($contenthash) { return parent::xsendfile($contenthash); } $headers = headers_list(); - if ($this->presigned_url_configured() && - $this->is_file_readable_externally_by_hash($contenthash) && - $this->presigned_url_should_redirect($contenthash, $headers)) { + if ($this->should_redirect_to_presigned_url($contenthash, null, $headers)) { return $this->redirect_to_presigned_url($contenthash, $headers); } @@ -964,6 +959,23 @@ public function is_configured() { return true; } + /** + * Run some checks whether file should be redirected to use a presigned url. + * + * @param stored_file $file Moodle file object. + * @param string $contenthash File content hash. + * @return bool + */ + public function should_redirect_to_presigned_url(string $contenthash, stored_file $file = null, array $headers = []): bool { + $validsetup = $this->presigned_url_configured() && + $this->is_file_readable_externally_by_hash($contenthash); + if (is_null($file)) { + return $validsetup && $this->presigned_url_should_redirect($contenthash, $headers); + } else { + return $validsetup && $this->presigned_url_should_redirect_file($file); + } + } + /** * No cleanup required - don't trigger filesystem trash clear. */ diff --git a/classes/local/store/s3/client.php b/classes/local/store/s3/client.php index 3f10e9d5..052e2b81 100644 --- a/classes/local/store/s3/client.php +++ b/classes/local/store/s3/client.php @@ -25,6 +25,7 @@ namespace tool_objectfs\local\store\s3; +use tool_objectfs\check\presigned_urls; use tool_objectfs\local\manager; use tool_objectfs\local\store\object_client_base; use tool_objectfs\local\store\signed_url; @@ -542,7 +543,13 @@ private function generate_presigned_url_cloudfront($contenthash, array $headers $key .= ''; } } - $resource = $this->config->cloudfrontresourcedomain . '/' . $key; + + if (!empty($this->bucketkeyprefix)) { + $resource = $this->config->cloudfrontresourcedomain . '/' . $this->bucketkeyprefix . $key; + } else { + $resource = $this->config->cloudfrontresourcedomain . '/' . $key; + } + // This is the id of the Cloudfront key pair you generated. $keypairid = $this->config->cloudfrontkeypairid; @@ -779,14 +786,12 @@ public function curl_range_request_to_presigned_url($contenthash, $ranges, $head /** * Test proxy range request. * - * @param object $filesystem Filesystem to be tested. + * @param \file_system $filesystem Filesystem to be tested. * @return object * @throws \coding_exception */ public function test_range_request($filesystem) { - global $PAGE; - $output = $PAGE->get_renderer('tool_objectfs'); - $testfiles = $output->presignedurl_tests_load_files($filesystem); + $testfiles = presigned_urls::load_files($filesystem); foreach ($testfiles as $file) { if ($file->get_filename() == 'testvideo.mp4') { $ranges = (object)['rangefrom' => 0, 'rangeto' => 999, 'length' => 1000]; diff --git a/lang/en/tool_objectfs.php b/lang/en/tool_objectfs.php index 168c3221..b48fc113 100644 --- a/lang/en/tool_objectfs.php +++ b/lang/en/tool_objectfs.php @@ -268,3 +268,13 @@ $string['check:proxyrangerequestsdisabled'] = 'The proxy range request setting is disabled.'; $string['checkproxy_range_request'] = 'Pre-signed URL range request proxy'; + +$string['checkpresigned_urls'] = 'Presigned urls check'; +$string['check:presigned_urls:success'] = 'Successfully setup to use presigned urls for fetching external files.'; +$string['check:presigned_urls:errorfilesize'] = 'File with name {$a->filename} was not the expected size.'; +$string['check:presigned_urls:errorpresignedfetch'] = 'File with name {$a->filename} was not fetched using presigned url.'; +$string['check:presigned_urls:errorfilename'] = 'File with name {$a->filename} did not have expected filename.'; +$string['check:presigned_urls:errorredirect'] = 'File with name {$a->filename} did not redirect to a presigned url.'; +$string['check:presigned_urls:infopresignedsetup'] = 'Site is not setup to use presigned URLs for external files.'; +$string['check:presigned_urls:infofilesystempresigned'] = 'File system does not support presigned URLs.'; +$string['check:presigned_urls:infofilesystem'] = 'File system is not configured.'; diff --git a/lib.php b/lib.php index 59159ebe..1d2e7849 100644 --- a/lib.php +++ b/lib.php @@ -125,3 +125,14 @@ function tool_objectfs_status_checks() { return []; } + +/** + * Get performance checks for tool_objectfs. + * + * @return array + */ +function tool_objectfs_performance_checks() { + return [ + new \tool_objectfs\check\presigned_urls(), + ]; +} diff --git a/presignedurl_tests.php b/presignedurl_tests.php deleted file mode 100644 index 1ac1bc66..00000000 --- a/presignedurl_tests.php +++ /dev/null @@ -1,82 +0,0 @@ -. - -/** - * Pre-Signed URL testing page - * - * @package tool_objectfs - * @author Mikhail Golenkov - * @copyright Catalyst IT - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use tool_objectfs\local\manager; - -require_once(__DIR__ . '/../../../config.php'); -require_once(__DIR__ . '/lib.php'); -require_once($CFG->dirroot . '/lib/adminlib.php'); - -admin_externalpage_setup('tool_objectfs_presignedurl_testing'); - -$output = $PAGE->get_renderer('tool_objectfs'); - -$delete = optional_param('delete', 0, PARAM_BOOL); -$deletedsuccess = ''; -if ($delete) { - require_sesskey(); - $output->delete_presignedurl_tests_files(); - $deletedstring = get_string('settings:presignedurl:deletedsuccess', OBJECTFS_PLUGIN_NAME); - $deletedsuccess = $output->notification($deletedstring, 'success'); -} - -echo $output->header(); -echo $output->heading(get_string('presignedurl_testing:page', 'tool_objectfs')); -$settingslink = \html_writer::link(new \moodle_url('/admin/settings.php?section=tool_objectfs_settings'), - get_string('presignedurl_testing:objectfssettings', 'tool_objectfs')); - -$config = manager::get_objectfs_config(); -$support = false; -if (!empty($config->filesystem)) { - $fs = new $config->filesystem(); - $support = $fs->supports_presigned_urls(); -} -if ($support) { - $deleteurl = new \moodle_url('/admin/tool/objectfs/presignedurl_tests.php', ['delete' => 1, 'sesskey' => sesskey()]); - $deletelinktext = get_string('settings:presignedurl:deletefiles', OBJECTFS_PLUGIN_NAME); - echo $output->heading(html_writer::link($deleteurl, $deletelinktext) . $deletedsuccess, 6); - $client = manager::get_client($config); - if ($client and $client->get_availability()) { - $connection = $client->test_connection(); - if ($connection->success) { - $testfiles = $output->presignedurl_tests_load_files($fs); - echo $output->presignedurl_tests_content($fs, $testfiles); - } else { - echo $output->notification(get_string('settings:connectionfailure', 'tool_objectfs', $connection->details), - 'notifyproblem'); - echo $output->heading(get_string('presignedurl_testing:checkconnectionsettings', 'tool_objectfs').$settingslink, 5); - } - - } else { - echo $output->notification(get_string('settings:clientnotavailable', 'tool_objectfs'), 'notifyproblem'); - echo $output->heading(get_string('presignedurl_testing:checkclientsettings', 'tool_objectfs').$settingslink, 5); - } - -} else { - echo $output->notification(get_string('presignedurl_testing:presignedurlsnotsupported', 'tool_objectfs'), 'notifyproblem'); - echo $output->heading(get_string('presignedurl_testing:checkfssettings', 'tool_objectfs').$settingslink, 5); -} - -echo $output->footer(); diff --git a/renderer.php b/renderer.php index 7f177715..c9147ca2 100644 --- a/renderer.php +++ b/renderer.php @@ -23,189 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -use tool_objectfs\local\manager; -use tool_objectfs\local\report\objectfs_report; -use tool_objectfs\local\store\object_file_system; - class tool_objectfs_renderer extends plugin_renderer_base { - /** - * Delete test files from files table - * @throws coding_exception - * @throws dml_exception - */ - public function delete_presignedurl_tests_files() { - $filestorage = get_file_storage(); - $filesarea = $filestorage->get_area_files( - \context_system::instance()->id, - OBJECTFS_PLUGIN_NAME, - 'settings', - 0 - ); - foreach ($filesarea as $testfile) { - if ('.' === $testfile->get_filename()) { - continue; - } - $testfile->delete(); - } - } - - public function presignedurl_tests_load_files($fs) { - global $CFG; - $filestorage = get_file_storage(); - $fixturespath = $CFG->dirroot.'/admin/tool/objectfs/tests/fixtures/'; - $fixturesfiles = glob($fixturespath.'*'); - $syscontext = \context_system::instance(); - - foreach ($fixturesfiles as $fixturesfile) { - // Filter out possible compressed files. - if (false !== strpos($fixturesfile, '.br')) { - continue; - } - $testfilename = str_replace($fixturespath, '', $fixturesfile); - - $contextid = $syscontext->id; - $component = 'tool_objectfs'; - $filearea = 'settings'; - $itemid = 0; - $filepath = '/'; - - $filerecord = array( - 'contextid' => $contextid, - 'component' => $component, - 'filearea' => $filearea, - 'itemid' => $itemid, - 'filepath' => $filepath, - 'filename' => $testfilename - ); - - $testfile = $filestorage->get_file($contextid, $component, $filearea, $itemid, $filepath, $testfilename); - if (!$testfile) { - $testfile = $filestorage->create_file_from_pathname($filerecord, $fixturesfile); - } - - $contenthash = $testfile->get_contenthash(); - $readable = $fs->is_file_readable_externally_by_hash($contenthash); - if (!$readable) { - $fs->copy_from_local_to_external($contenthash); - } - $testfiles[] = $testfile; - } - - return $testfiles; - } - - public function presignedurl_tests_content($fs, $testfiles) { - global $CFG; - $CFG->enablepresignedurls = true; - $output = ''; - - $output .= $this->box(''); - $output .= $this->heading(get_string('presignedurl_testing:test1', 'tool_objectfs'), 4); - foreach ($testfiles as $file) { - $presignedurl = $this->generate_file_url($file, false, true); - $output .= $this->heading($this->get_output($fs, $presignedurl, $file, 'downloadfile'), 5); - } - - $output .= $this->box(''); - $output .= $this->heading(get_string('presignedurl_testing:test2', 'tool_objectfs'), 4); - foreach ($testfiles as $file) { - $presignedurl = $this->generate_file_url($file, false, true); - - $output .= $this->heading($this->get_output($fs, $presignedurl, $file, 'downloadfile'), 5); - } - - $output .= $this->box(''); - $output .= $this->heading(get_string('presignedurl_testing:test3', 'tool_objectfs'), 4); - foreach ($testfiles as $file) { - $presignedurl = $this->generate_file_url($file); - - $output .= $this->heading($this->get_output($fs, $presignedurl, $file, 'openinbrowser'), 5); - } - - $output .= $this->box(''); - $output .= $this->heading(get_string('presignedurl_testing:test4', 'tool_objectfs'), 4); - foreach ($testfiles as $file) { - $presignedurl = $this->generate_file_url($file); - - $outputstring = '"'.$file->get_filename().'" '.get_string('presignedurl_testing:fileiniframe', 'tool_objectfs').':'; - $output .= $this->heading($outputstring, 5); - - $output .= $this->box($this->get_output($fs, $presignedurl, $file, 'iframesnotsupported')); - $output .= $this->box(''); - } - - $output .= $this->box(''); - $output .= $this->heading(get_string('presignedurl_testing:test5', 'tool_objectfs'), 4); - // Expires in seconds. - $testexpirefiles = ['testimage.png' => 0, 'testlarge.pdf' => 10, 'test.txt' => -1]; - foreach ($testfiles as $key => $file) { - $filename = $file->get_filename(); - if (!isset($testexpirefiles[$filename])) { - continue; - } - $presignedurl = $this->generate_file_url($file, $testexpirefiles[$filename]); - - $outputstring = '"' . $filename . '" '. - get_string('presignedurl_testing:fileiniframe', OBJECTFS_PLUGIN_NAME) . ':'; - $output .= $this->heading($outputstring, 5); - - $output .= $this->box($this->get_output($fs, $presignedurl, $file, 'iframesnotsupported')); - $output .= $this->box(''); - } - - return $output; - } - - /** - * Generate a file url with adding a param to set 'Expires' header. - * @param stored_file $file - * @param int|bool $expires - * @param bool $forcedownload - * @return string - * @throws dml_exception - */ - private function generate_file_url($file, $expires = false, $forcedownload = false) { - $url = \moodle_url::make_pluginfile_url( - \context_system::instance()->id, - OBJECTFS_PLUGIN_NAME, - 'settings', - 0, - '/', - $file->get_filename(), - $forcedownload - ); - $expires = (-1 !== $expires) ? $expires : false; - if (false !== $expires) { - $url->param('expires', $expires); - } - return $url->out(); - } - - /** - * Generates the output string that contains the presignedurl or local url. - * @param object_file_system $fs - * @param string $url - * @param stored_file $file - * @param string $identifier - * @return string - * @throws coding_exception - */ - private function get_output($fs, $url, $file, $identifier) { - $icon = $this->output->pix_icon('i/grade_correct', '', 'moodle', ['class' => 'icon']); - $redirect = $icon . 'Redirecting to external storage: '; - if (!$fs->presigned_url_should_redirect($file->get_contenthash())) { - $redirect = $this->output->pix_icon('i/grade_incorrect', '', 'moodle', ['class' => 'icon']) . 'Not redirecting: '; - } - $output = get_string('presignedurl_testing:' . $identifier, 'tool_objectfs') . ': '. - ''. $file->get_filename() . ''; - if ('iframesnotsupported' === $identifier) { - $output = ''; - } - return $output . '
' . $redirect . $url . '';; - } - /** * Returns a header for Object status history page. * diff --git a/settings.php b/settings.php index a941e506..53181c24 100644 --- a/settings.php +++ b/settings.php @@ -23,6 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use core\check\result; + defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/classes/local/manager.php'); @@ -39,10 +41,6 @@ $settings = new admin_settingpage('tool_objectfs_settings', get_string('pluginsettings', 'tool_objectfs')); $ADMIN->add('tool_objectfs', $settings); -$ADMIN->add('tool_objectfs', new admin_externalpage('tool_objectfs_presignedurl_testing', - get_string('presignedurl_testing:page', 'tool_objectfs'), - new moodle_url('/admin/tool/objectfs/presignedurl_tests.php'))); - $ADMIN->add('reports', new admin_externalpage('tool_objectfs_object_status', get_string('object_status:page', 'tool_objectfs'), new moodle_url('/admin/tool/objectfs/object_status.php'))); @@ -183,9 +181,27 @@ new lang_string('settings:presignedurl:proxyrangerequests', 'tool_objectfs'), new lang_string('settings:presignedurl:proxyrangerequests_help', 'tool_objectfs') . $warningtext, '1')); + // Add presigned url check to page to help with setup. + $check = new \tool_objectfs\check\presigned_urls(); + $result = $check->get_result(); + switch ($result->get_status()) { + case result::OK: + $notificationtype = \core\output\notification::NOTIFY_SUCCESS; + break; + case result::INFO: + $notificationtype = \core\output\notification::NOTIFY_INFO; + break; + case result::WARNING: + $notificationtype = \core\output\notification::NOTIFY_WARNING; + break; + default: + $notificationtype = \core\output\notification::NOTIFY_ERROR; + } + $presignedinfo = $OUTPUT->notification($result->get_summary(), $notificationtype); + $settings->add(new admin_setting_configcheckbox('tool_objectfs/enablepresignedurls', new lang_string('settings:presignedurl:enablepresignedurls', 'tool_objectfs'), - new lang_string('settings:presignedurl:enablepresignedurls_help', 'tool_objectfs'), '')); + new lang_string('settings:presignedurl:enablepresignedurls_help', 'tool_objectfs') . $presignedinfo, '')); $settings->add(new admin_setting_configduration('tool_objectfs/expirationtime', new lang_string('settings:presignedurl:expirationtime', 'tool_objectfs'), diff --git a/tests/check/presigned_urls_test.php b/tests/check/presigned_urls_test.php new file mode 100644 index 00000000..833ffad1 --- /dev/null +++ b/tests/check/presigned_urls_test.php @@ -0,0 +1,86 @@ +. + +namespace tool_objectfs\check; + +use core\check\result; +use tool_objectfs\s3_file_system; + +/** + * Test the presigned_url performance check. + * + * @package tool_objectfs + * @author Andrew Madden + * @copyright 2023 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class presigned_urls_test extends \advanced_testcase { + + /** + * This method runs before every test. + */ + public function setUp(): void { + $this->resetAfterTest(); + } + + /** + * Test that action link is set up. + */ + public function test_get_action_link() { + $check = new presigned_urls(); + $link = $check->get_action_link(); + $this->assertInstanceOf(\action_link::class, $link); + } + + /** + * Test fixture files are set up correctly. + */ + public function test_load_files() { + $fsstub = $this->createStub(s3_file_system::class); + $fsstub->method('is_file_readable_externally_by_hash')->willReturn(true); + $files = presigned_urls::load_files($fsstub); + $this->assertCount(10, $files); + // Test array contains files. + $file = array_shift($files); + $this->assertInstanceOf(\stored_file::class, $file); + } + + /** + * Test warning returned if no filesystem is set up. + */ + public function test_get_result_with_no_filesystem_setup() { + // Do not set a file system. + set_config('filesystem', '', 'tool_objectfs'); + $check = new presigned_urls(); + $result = $check->get_result(); + $this->assertInstanceOf(result::class, $result); + $this->assertEquals(result::INFO, $result->get_status()); + $this->assertEquals(get_string('check:presigned_urls:infofilesystem', 'tool_objectfs'), $result->get_summary()); + } + + /** + * Test warning returned if filesystem does not support presigned urls. + */ + public function test_get_result_with_unsupported_filesystem() { + // Set file system that does not support presigned urls. + set_config('filesystem', '\tool_objectfs\azure_file_system', 'tool_objectfs'); + $check = new presigned_urls(); + $result = $check->get_result(); + $this->assertInstanceOf(result::class, $result); + $this->assertEquals(result::INFO, $result->get_status()); + $this->assertEquals(get_string('check:presigned_urls:infofilesystempresigned', 'tool_objectfs'), $result->get_summary()); + } +}