This commit is contained in:
Sara Arjona 2024-01-22 16:25:40 +01:00
commit 9e4366a51d
No known key found for this signature in database
7 changed files with 171 additions and 47 deletions

View File

@ -387,6 +387,7 @@ $string['reportbuilder:edit'] = 'Edit your own custom reports';
$string['reportbuilder:editall'] = 'Edit all custom reports';
$string['reportbuilder:scheduleviewas'] = 'Schedule reports to be viewed as other users';
$string['reportbuilder:view'] = 'View custom reports';
$string['reportbuilder:viewall'] = 'View all custom reports';
$string['resetrole'] = 'Reset';
$string['resettingrole'] = 'Resetting role \'{$a}\'';
$string['restore:configure'] = 'Configure restore options';

View File

@ -2686,6 +2686,13 @@ $capabilities = array(
],
],
// Allow users to view all custom reports.
'moodle/reportbuilder:viewall' => [
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [],
],
// Allow users to create/edit their own custom reports.
'moodle/reportbuilder:edit' => [
'captype' => 'write',

View File

@ -159,11 +159,11 @@ class audience {
/**
* Returns SQL to limit the list of reports to those that the given user has access to
*
* - A user with 'editall' capability will have access to all reports
* - A user with 'viewall/editall' capability will have access to all reports
* - A user with 'edit' capability will have access to:
* - Those reports this user has created
* - Those reports this user is in audience of
* - A user with 'view' capability will have access to:
* - Otherwise:
* - Those reports this user is in audience of
*
* @param string $reporttablealias
@ -182,31 +182,29 @@ class audience {
$context = context_system::instance();
}
// If user can't view all reports, limit the returned list to those reports they can see.
if (!has_capability('moodle/reportbuilder:editall', $context, $userid)) {
// Select all reports accessible to the user based on audience.
[$reportselect, $params] = $DB->get_in_or_equal(
self::user_reports_list($userid),
SQL_PARAMS_NAMED,
database::generate_param_name('_'),
true,
null,
);
$where = "{$reporttablealias}.id {$reportselect}";
// User can also see any reports that they can edit.
if (has_capability('moodle/reportbuilder:edit', $context, $userid)) {
$paramuserid = database::generate_param_name();
$where = "({$reporttablealias}.usercreated = :{$paramuserid} OR {$where})";
$params[$paramuserid] = $userid ?? $USER->id;
}
return [$where, $params];
if (has_any_capability(['moodle/reportbuilder:editall', 'moodle/reportbuilder:viewall'], $context, $userid)) {
return ['1=1', []];
}
return ['1=1', []];
// Limit the returned list to those reports the user can see, by selecting based on report audience.
[$reportselect, $params] = $DB->get_in_or_equal(
self::user_reports_list($userid),
SQL_PARAMS_NAMED,
database::generate_param_name('_'),
true,
null,
);
$where = "{$reporttablealias}.id {$reportselect}";
// User can also see any reports that they can edit.
if (has_capability('moodle/reportbuilder:edit', $context, $userid)) {
$paramuserid = database::generate_param_name();
$where = "({$reporttablealias}.usercreated = :{$paramuserid} OR {$where})";
$params[$paramuserid] = $userid ?? $USER->id;
}
return [$where, $params];
}
/**

View File

@ -61,9 +61,10 @@ class permission {
}
return !empty($CFG->enablecustomreports) && has_any_capability([
'moodle/reportbuilder:editall',
'moodle/reportbuilder:edit',
'moodle/reportbuilder:editall',
'moodle/reportbuilder:view',
'moodle/reportbuilder:viewall',
], $context, $userid);
}
@ -92,6 +93,10 @@ class permission {
return false;
}
if (has_capability('moodle/reportbuilder:viewall', $report->get_context(), $userid)) {
return true;
}
if (self::can_edit_report($report, $userid)) {
return true;
}

View File

@ -267,16 +267,6 @@ class audience_test extends advanced_testcase {
$this->setUser($usertwo);
$usertworeport = $generator->create_report(['name' => 'User two report', 'source' => users::class]);
// Admin user sees all reports.
$this->setAdminUser();
[$where, $params] = audience::user_reports_list_access_sql('r');
$reports = $DB->get_fieldset_sql("SELECT r.id FROM {reportbuilder_report} r WHERE {$where}", $params);
$this->assertEqualsCanonicalizing([
$useradminreport->get('id'),
$useronereport->get('id'),
$usertworeport->get('id'),
], $reports);
// User one sees only the report they created.
[$where, $params] = audience::user_reports_list_access_sql('r', (int) $userone->id);
$reports = $DB->get_fieldset_sql("SELECT r.id FROM {reportbuilder_report} r WHERE {$where}", $params);
@ -298,6 +288,49 @@ class audience_test extends advanced_testcase {
$this->assertEmpty($reports);
}
/**
* Data provider for {@see test_user_reports_list_access_sql_with_capability}
*
* @return array[]
*/
public static function user_reports_list_access_sql_with_capability_provider(): array {
return [
['moodle/reportbuilder:editall'],
['moodle/reportbuilder:viewall'],
];
}
/**
* Test retrieving list of reports that user can access observes capability to view all reports
*
* @param string $capability
*
* @dataProvider user_reports_list_access_sql_with_capability_provider
*/
public function test_user_reports_list_access_sql_with_capability(string $capability): void {
global $DB;
$this->resetAfterTest();
// Admin creates a report, no audience.
$this->setAdminUser();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Admin report', 'source' => users::class]);
// Switch to new user, assign capability.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$userrole = $DB->get_field('role', 'id', ['shortname' => 'user']);
assign_capability($capability, CAP_ALLOW, $userrole, context_system::instance());
[$where, $params] = audience::user_reports_list_access_sql('r');
$reports = $DB->get_fieldset_sql("SELECT r.id FROM {reportbuilder_report} r WHERE {$where}", $params);
$this->assertEquals([$report->get('id')], $reports);
}
/**
* Test getting list of audiences in use within schedules for a report
*/

View File

@ -43,8 +43,10 @@ class permission_test extends advanced_testcase {
$this->resetAfterTest();
// User with permission.
$this->setAdminUser();
// User with default permission.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
try {
permission::require_can_view_reports_list();
} catch (Throwable $exception) {
@ -52,9 +54,6 @@ class permission_test extends advanced_testcase {
}
// User without permission.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$userrole = $DB->get_field('role', 'id', ['shortname' => 'user']);
unassign_capability('moodle/reportbuilder:view', $userrole, context_system::instance());
@ -63,6 +62,47 @@ class permission_test extends advanced_testcase {
permission::require_can_view_reports_list();
}
/**
* Data provider for {@see test_require_can_view_reports_list_with_capability}
*
* @return array[]
*/
public static function require_can_view_reports_list_with_capability_provider(): array {
return [
['moodle/reportbuilder:edit'],
['moodle/reportbuilder:editall'],
['moodle/reportbuilder:viewall'],
];
}
/**
* Test that viewing reports list observes capability to do so
*
* @param string $capability
*
* @dataProvider require_can_view_reports_list_with_capability_provider
*/
public function test_require_can_view_reports_list_with_capability(string $capability): void {
global $DB;
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$userrole = $DB->get_field('role', 'id', ['shortname' => 'user']);
// Remove default capability, allow additional.
unassign_capability('moodle/reportbuilder:view', $userrole, context_system::instance());
assign_capability($capability, CAP_ALLOW, $userrole, context_system::instance());
try {
permission::require_can_view_reports_list();
} catch (Throwable $exception) {
$this->fail($exception->getMessage());
}
}
/**
* Test whether user can view reports list when custom reports are disabled
*/
@ -81,8 +121,6 @@ class permission_test extends advanced_testcase {
* Test whether user can view specific report
*/
public function test_require_can_view_report(): void {
global $DB;
$this->resetAfterTest();
/** @var core_reportbuilder_generator $generator */
@ -101,14 +139,56 @@ class permission_test extends advanced_testcase {
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$userrole = $DB->get_field('role', 'id', ['shortname' => 'user']);
unassign_capability('moodle/reportbuilder:view', $userrole, context_system::instance());
$this->expectException(report_access_exception::class);
$this->expectExceptionMessage('You cannot view this report');
permission::require_can_view_report($report);
}
/**
* Data provider for {@see test_require_can_view_report_with_capability}
*
* @return array[]
*/
public static function require_can_view_report_with_capability_provider(): array {
return [
['moodle/reportbuilder:editall'],
['moodle/reportbuilder:viewall'],
];
}
/**
* Test whether user can view specific report when they have capability to view all reports
*
* @param string $capability
*
* @dataProvider require_can_view_report_with_capability_provider
*/
public function test_require_can_view_report_with_capability(string $capability): void {
global $DB;
$this->resetAfterTest();
// Admin creates a report, no audience.
$this->setAdminUser();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Admin report', 'source' => users::class]);
// Switch to new user, assign capability.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$userrole = $DB->get_field('role', 'id', ['shortname' => 'user']);
assign_capability($capability, CAP_ALLOW, $userrole, context_system::instance());
try {
permission::require_can_view_report($report);
} catch (Throwable $exception) {
$this->fail($exception->getMessage());
}
}
/**
* Test whether user can view specific report when it belongs to an audience
*/

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2024011900.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2024011900.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.4dev (Build: 20240119)'; // Human-friendly version name