Skip to content

Commit ccc613c

Browse files
object_file_system: embed_origin_url(), tool_objectfs_file_rewrite_urls()
redirect_to_presigned_url(): embed origin url into presigned url, and the corresponding reversal hook.
1 parent 8357690 commit ccc613c

4 files changed

Lines changed: 161 additions & 5 deletions

File tree

classes/local/store/object_file_system.php

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747

4848
abstract class object_file_system extends \file_system_filedir {
4949

50+
const PLUGINFILE_ORIGIN_SEGMENT_PREFIX = 'objectfs-origin=';
51+
52+
public $objectfs = true;
5053
public $externalclient;
5154
private $preferexternal;
5255
private $deleteexternally;
@@ -768,13 +771,60 @@ public function redirect_to_presigned_url($contenthash, $headers = array()) {
768771
}
769772
header('Cache-Control: ' . $cachevisibility . ', max-age=' . ($signedurl->expiresat - time()));
770773
}
771-
redirect($signedurl->url);
774+
redirect($this->embed_origin_url(
775+
$signedurl->url
776+
));
772777
} catch (\Exception $e) {
773778
debugging('Failed to redirect to pre-signed url: ' . $e->getMessage());
774779
return false;
775780
}
776781
}
777782

783+
/**
784+
* Embed origin URL in presigned URL.
785+
*
786+
* @param string $presignedurl
787+
* @return string $presignedurl with embedded origin
788+
* @throws moodle_exception
789+
*/
790+
public function embed_origin_url($presignedurl): string {
791+
$me = me();
792+
if (empty($me)) {
793+
throw new moodle_exception('invalidurl');
794+
}
795+
796+
return sprintf(
797+
"%s#%s%s",
798+
$presignedurl,
799+
self::PLUGINFILE_ORIGIN_SEGMENT_PREFIX,
800+
urlencode($me)
801+
);
802+
}
803+
804+
/**
805+
* Convert redirected URLs in $text.
806+
*
807+
* @param string $text The content that may contain URLs in need of rewriting.
808+
* @return string The processed text.
809+
*/
810+
public function rewrite_pluginfile_urls($text): string {
811+
if (!$this->presigned_url_configured()) { // Do nothing.
812+
return $text;
813+
}
814+
815+
$re = sprintf(
816+
'!https?://\S*?\#%s(\S+\w)!',
817+
preg_quote(self::PLUGINFILE_ORIGIN_SEGMENT_PREFIX),
818+
);
819+
return preg_replace_callback(
820+
$re,
821+
function ($matches) {
822+
global $CFG;
823+
return sprintf('%s%s', $CFG->wwwroot, urldecode($matches[1]));
824+
},
825+
$text
826+
);
827+
}
778828

779829
/**
780830
* Return if the file system supports presigned_urls.

classes/local/store/s3/client.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ class client extends object_client_base {
4545
const MAX_TEMP_LIMIT = 2097152;
4646

4747
protected $client;
48-
protected $bucket;
49-
private $signingmethod;
5048

5149
public function __construct($config) {
5250
global $CFG;
@@ -64,6 +62,14 @@ public function __construct($config) {
6462
$this->enablepresignedurls = $config->enablepresignedurls;
6563
$this->signingmethod = $config->signingmethod;
6664
$this->bucketkeyprefix = $config->key_prefix;
65+
$this->cloudfrontresourcedomain = $config->cloudfrontresourcedomain;
66+
67+
if ('cf' === $this->signingmethod) {
68+
if (!$this->cloudfrontresourcedomain) {
69+
throw new \moodle_exception(OBJECTFS_PLUGIN_NAME . ': cloudfrontresourcedomain not configured');
70+
}
71+
}
72+
6773
$this->set_client($config);
6874
} else {
6975
parent::__construct($config);
@@ -536,7 +542,7 @@ private function generate_presigned_url_cloudfront($contenthash, array $headers
536542
if ($nicefilename) {
537543
$key .= $this->get_nice_filename($headers);
538544
}
539-
$resource = $this->config->cloudfrontresourcedomain . '/' . $key;
545+
$resource = $this->cloudfrontresourcedomain . '/' . $key;
540546
// This is the id of the Cloudfront key pair you generated.
541547
$keypairid = $this->config->cloudfrontkeypairid;
542548

lib.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,20 @@ function tool_objectfs_status_checks() {
125125

126126
return [];
127127
}
128+
129+
/**
130+
* Convert presigned URLs in $text to the origin URL.
131+
*
132+
* @param string $text The content that may contain ULRs in need of rewriting.
133+
* @param int $contextid This parameter and the next two identify the file area to use.
134+
* @return string The processed text.
135+
*/
136+
function tool_objectfs_file_rewrite_urls($text, $contextid) {
137+
$fs = get_file_storage()->get_file_system();
138+
139+
if (!isset($fs->objectfs)) {
140+
return $text;
141+
}
142+
143+
return $fs->rewrite_pluginfile_urls($text);
144+
}

tests/object_file_system_test.php

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
require_once(__DIR__ . '/classes/test_client.php');
2525
require_once(__DIR__ . '/tool_objectfs_testcase.php');
2626

27-
class object_file_system_testcase extends tool_objectfs_testcase {
27+
class object_file_system_test extends tool_objectfs_testcase {
2828

2929
public function set_externalclient_config($key, $value) {
3030
// Get a reflection of externalclient object as a property.
@@ -899,4 +899,87 @@ public function test_is_configured_alternative_file_system_class_is_not_set() {
899899
unset($CFG->alternative_file_system_class);
900900
$this->assertFalse($this->filesystem->is_configured());
901901
}
902+
903+
/**
904+
* @return array
905+
*/
906+
public function rewrite_pluginfile_urls_s3_provider() {
907+
return [
908+
['/pluginfile.php', '/3854/tool_objectfs/content/1/test.pdf'],
909+
];
910+
}
911+
912+
/**
913+
* Test rewrite_pluginfile_urls() S3
914+
*
915+
* @dataProvider rewrite_pluginfile_urls_s3_provider
916+
*
917+
* @param string $script
918+
* @param int $pluginfile
919+
*/
920+
public function test_rewrite_pluginfile_urls_s3_proper($script, $pluginfile) {
921+
global $CFG, $ME;
922+
923+
if (!$CFG->phpunit_objectfs_s3_integration_test_credentials) {
924+
$this->markTestSkipped('No S3 test credentials in config file');
925+
}
926+
927+
$ME = "$script$pluginfile";
928+
$bucket = $CFG->phpunit_objectfs_s3_integration_test_credentials['s3_bucket'];
929+
$s3_region = $CFG->phpunit_objectfs_s3_integration_test_credentials['s3_region'];
930+
$bucketkeyprefix = 'test/';
931+
$presignedurl = "https://$bucket.s3.$s3_region.amazonaws.com/$bucketkeyprefix?param1=val1&param2=val2";
932+
933+
$this->filesystem = new test_file_system();
934+
$externalclient = $this->filesystem->get_external_client();
935+
$this->set_externalclient_config('enablepresignedurls', '1');
936+
$this->set_externalclient_config('signingmethod', 's3');
937+
$this->set_externalclient_config('bucketkeyprefix', $bucketkeyprefix);
938+
$this->assertTrue($this->filesystem->presigned_url_configured());
939+
940+
$embedded = $this->filesystem->embed_origin_url($presignedurl);
941+
$textformat = 'Fake test with pdf %s';
942+
$originaltext = sprintf($textformat, $embedded);
943+
944+
$this->assertEquals(
945+
sprintf($textformat, $CFG->wwwroot.$ME),
946+
$this->filesystem->rewrite_pluginfile_urls($originaltext)
947+
);
948+
}
949+
950+
/**
951+
* Test rewrite_pluginfile_urls() CloudFront
952+
*
953+
* @dataProvider rewrite_pluginfile_urls_s3_provider
954+
*
955+
* @param string $script
956+
* @param int $pluginfile
957+
*/
958+
public function test_rewrite_pluginfile_urls_s3_cf($script, $pluginfile) {
959+
global $CFG, $ME;
960+
961+
if (!$CFG->phpunit_objectfs_s3_integration_test_credentials) {
962+
$this->markTestSkipped('No S3 test credentials in config file');
963+
}
964+
965+
$ME = "$script$pluginfile";
966+
$cloudfrontresourcedomain = 'https://presigned.url';
967+
$presignedurl = "$cloudfrontresourcedomain/x?param1=val1&param2=val2";
968+
969+
$this->filesystem = new test_file_system();
970+
$externalclient = $this->filesystem->get_external_client();
971+
$this->set_externalclient_config('enablepresignedurls', '1');
972+
$this->set_externalclient_config('signingmethod', 'cf');
973+
$this->set_externalclient_config('cloudfrontresourcedomain', $cloudfrontresourcedomain);
974+
$this->assertTrue($this->filesystem->presigned_url_configured());
975+
976+
$embedded = $this->filesystem->embed_origin_url($presignedurl);
977+
$textformat = 'Fake test with pdf %s';
978+
$originaltext = sprintf($textformat, $embedded);
979+
980+
$this->assertEquals(
981+
sprintf($textformat, $CFG->wwwroot.$ME),
982+
$this->filesystem->rewrite_pluginfile_urls($originaltext)
983+
);
984+
}
902985
}

0 commit comments

Comments
 (0)