From 7bd13d581de0290f9c1a23a064b0af54533194db Mon Sep 17 00:00:00 2001 From: Laurent David Date: Wed, 14 Sep 2022 18:56:50 +0200 Subject: [PATCH] MDL-75753 mod_bigbluebuttonbn: Support SHA256 and SHA512 checksum * Add support for SHA256 and SHA512 checksum --- mod/bigbluebuttonbn/classes/local/config.php | 11 +++ .../classes/local/proxy/proxy_base.php | 21 ++++- .../classes/setting_validator.php | 5 +- mod/bigbluebuttonbn/classes/settings.php | 15 +++ .../lang/en/bigbluebuttonbn.php | 3 + mod/bigbluebuttonbn/tests/generator/lib.php | 88 ++++++++++-------- .../tests/local/proxy/proxy_base_test.php | 92 +++++++++++++++++++ 7 files changed, 191 insertions(+), 44 deletions(-) create mode 100644 mod/bigbluebuttonbn/tests/local/proxy/proxy_base_test.php diff --git a/mod/bigbluebuttonbn/classes/local/config.php b/mod/bigbluebuttonbn/classes/local/config.php index 55074672d71..5b4edfa0414 100644 --- a/mod/bigbluebuttonbn/classes/local/config.php +++ b/mod/bigbluebuttonbn/classes/local/config.php @@ -39,6 +39,16 @@ class config { /** @var string Default bigbluebutton data processing agreement url */ public const DEFAULT_DPA_URL = 'https://blindsidenetworks.com/dpa-moodle-free-tier'; + /** @var string the default bigbluebutton checksum algorithm */ + public const DEFAULT_CHECKSUM_ALGORITHM = 'SHA1'; + + /** @var array list of supported bigbluebutton checksum algorithm */ + const CHECKSUM_ALGORITHMS = [ + self::DEFAULT_CHECKSUM_ALGORITHM, + 'SHA256', + 'SHA512' + ]; + /** * Returns moodle version. * @@ -112,6 +122,7 @@ class config { 'welcome_default' => '', 'default_dpa_accepted' => false, 'poll_interval' => bigbluebutton_proxy::DEFAULT_POLL_INTERVAL, + 'checksum_algorithm' => self::DEFAULT_CHECKSUM_ALGORITHM, ]; } diff --git a/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php b/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php index a734b45e81a..8ed29249878 100644 --- a/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php +++ b/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php @@ -21,7 +21,6 @@ use mod_bigbluebuttonbn\local\exceptions\bigbluebutton_exception; use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception; use mod_bigbluebuttonbn\plugin; use moodle_url; -use SimpleXMLElement; /** * The abstract proxy base class. @@ -61,7 +60,8 @@ abstract class proxy_base { }, array_keys($metadata)), $metadata); $params = http_build_query($data + $metadata, '', '&'); - return $baseurl . $params . '&checksum=' . sha1($action . $params . self::sanitized_secret()); + $checksum = self::get_checksum($action, $params); + return $baseurl . $params . '&checksum=' . $checksum; } /** @@ -69,7 +69,7 @@ abstract class proxy_base { * * @return string */ - protected static function sanitized_url() { + protected static function sanitized_url(): string { $serverurl = trim(config::get('server_url')); if (PHPUNIT_TEST) { $serverurl = (new moodle_url(TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER))->out(false); @@ -95,7 +95,7 @@ abstract class proxy_base { /** * Throw an exception if there is a problem in the returned XML value * - * @param SimpleXMLElement|bool $xml + * @param \SimpleXMLElement|bool $xml * @param array|null $additionaldetails * @throws bigbluebutton_exception * @throws server_not_available_exception @@ -164,7 +164,7 @@ abstract class proxy_base { * @param string $action * @param array $data * @param array $metadata - * @return null|bool|SimpleXMLElement + * @return null|bool|\SimpleXMLElement */ protected static function fetch_endpoint_xml( string $action, @@ -179,4 +179,15 @@ abstract class proxy_base { $curl = new curl(); return $curl->get(self::action_url($action, $data, $metadata)); } + + /** + * Get checksum + * + * @param string $action + * @param string $params + * @return string + */ + public static function get_checksum(string $action, string $params): string { + return hash(config::get('checksum_algorithm'), $action . $params . self::sanitized_secret()); + } } diff --git a/mod/bigbluebuttonbn/classes/setting_validator.php b/mod/bigbluebuttonbn/classes/setting_validator.php index 014f036cba7..e3c2bdeacdd 100644 --- a/mod/bigbluebuttonbn/classes/setting_validator.php +++ b/mod/bigbluebuttonbn/classes/setting_validator.php @@ -37,7 +37,8 @@ class setting_validator { public static function section_general_shown() { global $CFG; return (!isset($CFG->bigbluebuttonbn['server_url']) || - !isset($CFG->bigbluebuttonbn['shared_secret']) + !isset($CFG->bigbluebuttonbn['shared_secret']) || + !isset($CFG->bigbluebuttonbn['checksum_algorithm']) ); } @@ -49,7 +50,7 @@ class setting_validator { public static function section_default_messages_shown() { global $CFG; return (!isset($CFG->bigbluebuttonbn['welcome_default']) || - !isset($CFG->bigbluebuttonbn['welcome_editable'])); + !isset($CFG->bigbluebuttonbn['welcome_editable'])); } /** diff --git a/mod/bigbluebuttonbn/classes/settings.php b/mod/bigbluebuttonbn/classes/settings.php index 588dd5b6003..4958640e033 100644 --- a/mod/bigbluebuttonbn/classes/settings.php +++ b/mod/bigbluebuttonbn/classes/settings.php @@ -21,6 +21,7 @@ use admin_setting; use admin_setting_configcheckbox; use admin_setting_configmultiselect; use admin_setting_configpasswordunmask; +use admin_setting_configselect; use admin_setting_configstoredfile; use admin_setting_configtext; use admin_setting_configtextarea; @@ -179,6 +180,20 @@ class settings { $item, $settingsgeneral ); + + $item = new admin_setting_configselect( + 'bigbluebuttonbn_checksum_algorithm', + get_string('config_checksum_algorithm', 'bigbluebuttonbn'), + get_string('config_checksum_algorithm_description', 'bigbluebuttonbn'), + config::DEFAULT_CHECKSUM_ALGORITHM, + array_combine(config::CHECKSUM_ALGORITHMS, config::CHECKSUM_ALGORITHMS) + ); + $this->add_conditional_element( + 'checksum_algorithm', + $item, + $settingsgeneral + ); + $item = new \admin_setting_description( 'bigbluebuttonbn_dpa_info', '', diff --git a/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php b/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php index 3ec2201f921..19ba90c7a4b 100644 --- a/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php +++ b/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php @@ -146,6 +146,9 @@ $string['config_server_url_description'] = 'The default credentials are for a '; $string['config_shared_secret'] = 'BigBlueButton shared secret'; $string['config_shared_secret_description'] = 'The security secret of your BigBlueButton server. The default secret is for a free BigBlueButton service provided by Blindside Networks.'; +$string['config_checksum_algorithm'] = 'BigBlueButton Checksum Algorithm'; +$string['config_checksum_algorithm_description'] = 'The checksum algorithm of your BigBlueButton server. + (SHA1 guarantees compatibility with older server versions but is less secure whereas SHA512 is FIPS 140-2 compliant.)'; $string['config_recording'] = 'Recording'; $string['config_recording_description'] = 'These settings are feature specific'; diff --git a/mod/bigbluebuttonbn/tests/generator/lib.php b/mod/bigbluebuttonbn/tests/generator/lib.php index 0f2db867243..ec8e8c4edc0 100644 --- a/mod/bigbluebuttonbn/tests/generator/lib.php +++ b/mod/bigbluebuttonbn/tests/generator/lib.php @@ -273,6 +273,50 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator { return (string) $result->recordID; } + /** + * Utility to send a request to the mock server + * + * @param string $endpoint + * @param array $params + * @param array $mockdata + * @return SimpleXMLElement|bool + * @throws moodle_exception + */ + protected function send_mock_request(string $endpoint, array $params = [], array $mockdata = []): SimpleXMLElement { + $url = $this->get_mocked_server_url($endpoint, $params); + + foreach ($mockdata as $key => $value) { + if (is_array($value)) { + foreach ($value as $subkey => $subvalue) { + $paramname = "{$key}_{$subkey}"; + $url->param($paramname, $subvalue); + } + } else { + $url->param($key, $value); + } + } + + $curl = new \curl(); + $result = $curl->get($url->out_omit_querystring(), $url->params()); + + $retvalue = @simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOBLANKS); + if ($retvalue === false) { + throw new moodle_exception('mockserverconnfailed', 'mod_bigbluebutton'); + } + return $retvalue; + } + + /** + * Get a URL for a mocked BBB server endpoint. + * + * @param string $endpoint + * @param array $params + * @return moodle_url + */ + protected function get_mocked_server_url(string $endpoint = '', array $params = []): moodle_url { + return new moodle_url(TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER . '/' . $endpoint, $params); + } + /** * Mock an in-progress meeting on the remote server. * @@ -356,47 +400,17 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator { } /** - * Get a URL for a mocked BBB server endpoint. + * Set a value in the Mock server * - * @param string $endpoint - * @param array $params - * @return moodle_url - */ - protected function get_mocked_server_url(string $endpoint = '', array $params = []): moodle_url { - return new moodle_url(TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER . '/' . $endpoint, $params); - } - - /** - * Utility to send a request to the mock server - * - * @param string $endpoint - * @param array $params - * @param array $mockdata - * @return SimpleXMLElement|bool + * @param string $name + * @param mixed $value + * @return void * @throws moodle_exception */ - protected function send_mock_request(string $endpoint, array $params = [], array $mockdata = []): SimpleXMLElement { - $url = $this->get_mocked_server_url($endpoint, $params); - - foreach ($mockdata as $key => $value) { - if (is_array($value)) { - foreach ($value as $subkey => $subvalue) { - $paramname = "{$key}_{$subkey}"; - $url->param($paramname, $subvalue); - } - } else { - $url->param($key, $value); - } + public function set_value(string $name, $value): void { + if (defined('TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER')) { + $this->send_mock_request('backoffice/set', [], ['name' => $name, 'value' => json_encode($value)]); } - - $curl = new \curl(); - $result = $curl->get($url->out_omit_querystring(), $url->params()); - - $retvalue = @simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOBLANKS); - if ($retvalue === false) { - throw new moodle_exception('mockserverconnfailed', 'mod_bigbluebutton'); - } - return $retvalue; } /** diff --git a/mod/bigbluebuttonbn/tests/local/proxy/proxy_base_test.php b/mod/bigbluebuttonbn/tests/local/proxy/proxy_base_test.php new file mode 100644 index 00000000000..7aba2c0be49 --- /dev/null +++ b/mod/bigbluebuttonbn/tests/local/proxy/proxy_base_test.php @@ -0,0 +1,92 @@ +. + +/** + * Tests for BigBlueButton Proxy server (and checksum). + * + * @package mod_bigbluebuttonbn + * @copyright 2018 - present, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Laurent David (laurent [at] call-learning [dt] fr) + */ + +namespace mod_bigbluebuttonbn\local\proxy; +use mod_bigbluebuttonbn\test\testcase_helper_trait; + +/** + * Proxy base test + * + * @package mod_bigbluebuttonbn + * @copyright 2018 - present, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Laurent David (laurent [at] call-learning [dt] fr) + * @covers \mod_bigbluebuttonbn\local\proxy\proxy_base + * @coversDefaultClass \mod_bigbluebuttonbn\local\proxy\proxy_base + */ +class proxy_base_test extends \advanced_testcase { + use testcase_helper_trait; + + /** + * Setup + */ + public function setUp(): void { + parent::setUp(); + $this->initialise_mock_server(); + } + + /** + * Test that different checksum algorithm work + * + * @return void + */ + public function test_get_checksum() { + $this->resetAfterTest(); + foreach (['SHA1', 'SHA512', 'SHA256'] as $algo) { + set_config('bigbluebuttonbn_checksum_algorithm', $algo); + $xmlinfo = self::get_status(); + $this->assertNotEmpty($xmlinfo); + $this->assertEquals('SUCCESS', $xmlinfo->returncode); + } + } + + /** + * Test that we send a checksumError whenever the algorithm is not supported + * + * @return void + */ + public function test_get_checksum_not_supported() { + $this->resetAfterTest(); + $bbbgenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); + $bbbgenerator->set_value('checksum_algorithms', ['SHA1', 'SHA256']); + // This should not be supported. + set_config('bigbluebuttonbn_checksum_algorithm', 'SHA512'); + $xmlinfo = self::get_status(); + $this->assertEquals($xmlinfo->messageKey, 'checksumError'); + } + + + /** + * Get the endpoint XML result. + * + * @return mixed + */ + protected static function get_status() { + $rc = new \ReflectionClass(proxy_base::class); + $rcm = $rc->getMethod('fetch_endpoint_xml'); + $rcm->setAccessible(true); + return $rcm->invoke(null, ''); + } +}