From c9f689ae6ccd3d60371594bd4362aaaf53e1e607 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Mon, 31 Jul 2023 15:37:03 +0100 Subject: [PATCH] MDL-78844 reportbuilder: external validate system report access method. --- lib/db/services.php | 6 + .../external/systemreports/can_view.php | 120 ++++++++++++++++++ .../external/systemreports/can_view_test.php | 73 +++++++++++ reportbuilder/upgrade.txt | 4 +- version.php | 2 +- 5 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 reportbuilder/classes/external/systemreports/can_view.php create mode 100644 reportbuilder/tests/external/systemreports/can_view_test.php diff --git a/lib/db/services.php b/lib/db/services.php index 559a9408c9d..354b2da1197 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -2951,6 +2951,12 @@ $functions = array( 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], + 'core_reportbuilder_can_view_system_report' => [ + 'classname' => 'core_reportbuilder\external\systemreports\can_view', + 'description' => 'Determine access to a system report', + 'type' => 'read', + 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], + ], 'core_reportbuilder_view_report' => [ 'classname' => 'core_reportbuilder\external\reports\view', 'description' => 'Trigger custom report viewed', diff --git a/reportbuilder/classes/external/systemreports/can_view.php b/reportbuilder/classes/external/systemreports/can_view.php new file mode 100644 index 00000000000..c953440eb4e --- /dev/null +++ b/reportbuilder/classes/external/systemreports/can_view.php @@ -0,0 +1,120 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\external\systemreports; + +use core_external\external_api; +use core_external\external_multiple_structure; +use core_external\external_single_structure; +use core_external\external_function_parameters; +use core_external\external_value; +use core_reportbuilder\report_access_exception; +use core_reportbuilder\system_report_factory; + +/** + * External method for validating access to a system report + * + * @package core_reportbuilder + * @copyright 2023 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class can_view extends external_api { + + /** + * External method parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'source' => new external_value(PARAM_RAW, 'Report class path'), + 'context' => self::get_context_parameters(), + 'component' => new external_value(PARAM_COMPONENT, 'Report component', VALUE_DEFAULT, ''), + 'area' => new external_value(PARAM_AREA, 'Report area', VALUE_DEFAULT, ''), + 'itemid' => new external_value(PARAM_INT, 'Report item ID', VALUE_DEFAULT, 0), + 'parameters' => new external_multiple_structure( + new external_single_structure([ + 'name' => new external_value(PARAM_RAW), + 'value' => new external_value(PARAM_RAW), + ]), + 'Report parameters', VALUE_DEFAULT, [] + ), + ]); + } + + /** + * External method execution + * + * @param string $source + * @param array $context + * @param string $component + * @param string $area + * @param int $itemid + * @param array[] $parameters + * @return bool + */ + public static function execute( + string $source, + array $context, + string $component = '', + string $area = '', + int $itemid = 0, + array $parameters = [], + ): bool { + + [ + 'source' => $source, + 'context' => $context, + 'component' => $component, + 'area' => $area, + 'itemid' => $itemid, + 'parameters' => $parameters, + ] = self::validate_parameters(self::execute_parameters(), [ + 'source' => $source, + 'context' => $context, + 'component' => $component, + 'area' => $area, + 'itemid' => $itemid, + 'parameters' => $parameters, + ]); + + $context = self::get_context_from_params($context); + self::validate_context($context); + + // Flatten the report parameters. + $parameters = array_combine(array_column($parameters, 'name'), array_column($parameters, 'value')); + + try { + $report = system_report_factory::create($source, $context, $component, $area, $itemid, $parameters); + $report->require_can_view(); + } catch (report_access_exception $exception) { + return false; + } + + return true; + } + + /** + * External method return value + * + * @return external_value + */ + public static function execute_returns(): external_value { + return new external_value(PARAM_BOOL); + } +} diff --git a/reportbuilder/tests/external/systemreports/can_view_test.php b/reportbuilder/tests/external/systemreports/can_view_test.php new file mode 100644 index 00000000000..22c50823064 --- /dev/null +++ b/reportbuilder/tests/external/systemreports/can_view_test.php @@ -0,0 +1,73 @@ +. + +declare(strict_types=1); + +namespace core_reportbuilder\external\systemreports; + +use core\context\system; +use core_external\external_api; +use externallib_advanced_testcase; +use core_reportbuilder\local\systemreports\reports_list; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once("{$CFG->dirroot}/webservice/tests/helpers.php"); + +/** + * Unit tests of external class for validating access to a system report + * + * @package core_reportbuilder + * @covers \core_reportbuilder\external\systemreports\can_view + * @copyright 2023 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class can_view_test extends externallib_advanced_testcase { + + /** + * Text execute method + */ + public function test_execute(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $result = can_view::execute(reports_list::class, ['contextid' => system::instance()->id], '', '', 0, []); + $result = external_api::clean_returnvalue(can_view::execute_returns(), $result); + + $this->assertTrue($result); + } + + /** + * Test execute method for a user without permission to view report + */ + public function test_execute_access_none(): void { + global $DB; + + $this->resetAfterTest(); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $userrole = $DB->get_field('role', 'id', ['shortname' => 'user']); + unassign_capability('moodle/reportbuilder:view', $userrole, system::instance()); + + $result = can_view::execute(reports_list::class, ['contextid' => system::instance()->id], '', '', 0, []); + $result = external_api::clean_returnvalue(can_view::execute_returns(), $result); + + $this->assertFalse($result); + } +} diff --git a/reportbuilder/upgrade.txt b/reportbuilder/upgrade.txt index 959cdbf7942..075c55d8198 100644 --- a/reportbuilder/upgrade.txt +++ b/reportbuilder/upgrade.txt @@ -3,7 +3,9 @@ Information provided here is intended especially for developers. === 4.3 === -* New external method `core_reportbuilder_retrieve_system_report` for retrieving system report data +* New external methods for retrieving system report data: + - `core_reportbuilder_can_view_system_report` + - `core_reportbuilder_retrieve_system_report` * New `get_tag_joins_for_entity` helper in base entity class, for returning SQL joins necessary for retrieving tags * New `set_is_deprecated` method in base `local\report\[column|filter]` classes to deprecate report entity columns and filters * The following report entity columns have been deprecated, with replacements as follows: diff --git a/version.php b/version.php index 268826e82e4..31b908930c3 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2023080400.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2023080400.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.3dev (Build: 20230804)'; // Human-friendly version name