jfederico /
moodle-mod_bigbluebuttonbn
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | // This file is part of Moodle - http://moodle.org/ |
||
| 3 | // |
||
| 4 | // Moodle is free software: you can redistribute it and/or modify |
||
| 5 | // it under the terms of the GNU General Public License as published by |
||
| 6 | // the Free Software Foundation, either version 3 of the License, or |
||
| 7 | // (at your option) any later version. |
||
| 8 | // |
||
| 9 | // Moodle is distributed in the hope that it will be useful, |
||
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 12 | // GNU General Public License for more details. |
||
| 13 | // |
||
| 14 | // You should have received a copy of the GNU General Public License |
||
| 15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. |
||
| 16 | |||
| 17 | /** |
||
| 18 | * Privacy class for requesting user data. |
||
| 19 | * |
||
| 20 | * @package mod_bigbluebuttonbn |
||
| 21 | * @copyright 2018 - present, Blindside Networks Inc |
||
| 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
||
| 23 | * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com) |
||
| 24 | */ |
||
| 25 | |||
| 26 | namespace mod_bigbluebuttonbn\privacy; |
||
| 27 | |||
| 28 | use \core_privacy\local\metadata\collection; |
||
| 29 | use \core_privacy\local\request\approved_contextlist; |
||
| 30 | use core_privacy\local\request\approved_userlist; |
||
| 31 | use \core_privacy\local\request\contextlist; |
||
| 32 | use \core_privacy\local\request\helper; |
||
| 33 | use \core_privacy\local\request\transform; |
||
| 34 | use core_privacy\local\request\userlist; |
||
| 35 | use \core_privacy\local\request\writer; |
||
| 36 | |||
| 37 | defined('MOODLE_INTERNAL') || die(); |
||
| 38 | |||
| 39 | global $CFG; |
||
| 40 | require_once($CFG->dirroot . '/mod/bigbluebuttonbn/locallib.php'); |
||
| 41 | |||
| 42 | /* |
||
| 43 | * This part is to be eliminated as soon as possible but allows the phpunit test to pass Ok on MOODLE_33 and below WHILST allowing |
||
| 44 | * also the privacy/tests/provider_test.php tests to pass |
||
| 45 | * (vendor/bin/phpunit --fail-on-risky --disallow-test-output -v privacy/tests/provider_test.php). |
||
| 46 | * Downside we add a new warning to the code checker. This is not ideal but will be ok until we stop supporting MOODLE_33 or we |
||
| 47 | * change the test in provider_test.php so to cater for classes which are implementing the right method but not necessarily |
||
| 48 | * inheriting from the new interface setup in MOODLE_34 (\core_privacy\local\request\core_userlist_provider). |
||
| 49 | * This is linked to CONTRIB-7983 |
||
| 50 | */ |
||
| 51 | if (!interface_exists("\\core_privacy\\local\\request\\core_userlist_provider")) { |
||
| 52 | interface core_userlist_provider { |
||
| 53 | /** |
||
| 54 | * Get the list of users who have data within a context. |
||
| 55 | * |
||
| 56 | * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. |
||
| 57 | */ |
||
| 58 | public static function get_users_in_context(userlist $userlist); |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Delete multiple users within a single context. |
||
| 62 | * |
||
| 63 | * @param approved_userlist $userlist The approved context and user information to delete information for. |
||
| 64 | */ |
||
| 65 | public static function delete_data_for_users(approved_userlist $userlist); |
||
| 66 | } |
||
| 67 | } else { |
||
| 68 | interface core_userlist_provider extends \core_privacy\local\request\core_userlist_provider { |
||
|
0 ignored issues
–
show
|
|||
| 69 | |||
| 70 | } |
||
| 71 | } |
||
| 72 | |||
| 73 | /** |
||
| 74 | * Privacy class for requesting user data. |
||
| 75 | * |
||
| 76 | * @package mod_bigbluebuttonbn |
||
| 77 | * @copyright 2018 - present, Blindside Networks Inc |
||
| 78 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
||
| 79 | * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com) |
||
| 80 | */ |
||
| 81 | class provider implements |
||
| 82 | // This plugin has data. |
||
| 83 | \core_privacy\local\metadata\provider, |
||
| 84 | |||
| 85 | // This plugin currently implements the original plugin\provider interface. |
||
| 86 | \core_privacy\local\request\plugin\provider, |
||
| 87 | |||
| 88 | // This plugin is capable of determining which users have data within it. |
||
| 89 | core_userlist_provider { |
||
| 90 | |||
| 91 | // This trait must be included. |
||
| 92 | use \core_privacy\local\legacy_polyfill; |
||
| 93 | |||
| 94 | /** |
||
| 95 | * Returns metadata. |
||
| 96 | * |
||
| 97 | * @param collection $collection The initialised collection to add items to. |
||
| 98 | * @return collection A listing of user data stored through this system. |
||
| 99 | */ |
||
| 100 | public static function _get_metadata(collection $collection) { |
||
| 101 | |||
| 102 | // The table bigbluebuttonbn stores only the room properties. |
||
| 103 | // However, there is a chance that some personal information is stored as metadata. |
||
| 104 | // This would be done in the column 'participants' where rules can be set to define BBB roles. |
||
| 105 | // It is fair to say that only the userid is stored, which is useless if user is removed. |
||
| 106 | // But if this is a concern a refactoring on the way the rules are stored will be required. |
||
| 107 | $collection->add_database_table('bigbluebuttonbn', [ |
||
| 108 | 'participants' => 'privacy:metadata:bigbluebuttonbn:participants', |
||
| 109 | ], 'privacy:metadata:bigbluebuttonbn'); |
||
| 110 | |||
| 111 | // The table bigbluebuttonbn_logs stores events triggered by users when using the plugin. |
||
| 112 | // Some personal information along with the resource accessed is stored. |
||
| 113 | $collection->add_database_table('bigbluebuttonbn_logs', [ |
||
| 114 | 'userid' => 'privacy:metadata:bigbluebuttonbn_logs:userid', |
||
| 115 | 'timecreated' => 'privacy:metadata:bigbluebuttonbn_logs:timecreated', |
||
| 116 | 'meetingid' => 'privacy:metadata:bigbluebuttonbn_logs:meetingid', |
||
| 117 | 'log' => 'privacy:metadata:bigbluebuttonbn_logs:log', |
||
| 118 | 'meta' => 'privacy:metadata:bigbluebuttonbn_logs:meta', |
||
| 119 | ], 'privacy:metadata:bigbluebuttonbn_logs'); |
||
| 120 | |||
| 121 | // Personal information has to be passed to BigBlueButton. |
||
| 122 | // This includes the user ID and fullname. |
||
| 123 | $collection->add_external_location_link('bigbluebutton', [ |
||
| 124 | 'userid' => 'privacy:metadata:bigbluebutton:userid', |
||
| 125 | 'fullname' => 'privacy:metadata:bigbluebutton:fullname', |
||
| 126 | ], 'privacy:metadata:bigbluebutton'); |
||
| 127 | |||
| 128 | return $collection; |
||
| 129 | } |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Get the list of contexts that contain user information for the specified user. |
||
| 133 | * |
||
| 134 | * @param int $userid The user to search. |
||
| 135 | * @return contextlist $contextlist The list of contexts used in this plugin. |
||
| 136 | */ |
||
| 137 | public static function _get_contexts_for_userid(int $userid) { |
||
| 138 | // If user was already deleted, do nothing. |
||
| 139 | if (!\core_user::get_user($userid)) { |
||
| 140 | return; |
||
| 141 | } |
||
| 142 | // Fetch all bigbluebuttonbn logs. |
||
| 143 | $sql = "SELECT c.id |
||
| 144 | FROM {context} c |
||
| 145 | INNER JOIN {course_modules} cm |
||
| 146 | ON cm.id = c.instanceid |
||
| 147 | AND c.contextlevel = :contextlevel |
||
| 148 | INNER JOIN {modules} m |
||
| 149 | ON m.id = cm.module |
||
| 150 | AND m.name = :modname |
||
| 151 | INNER JOIN {bigbluebuttonbn} bigbluebuttonbn |
||
| 152 | ON bigbluebuttonbn.id = cm.instance |
||
| 153 | INNER JOIN {bigbluebuttonbn_logs} bigbluebuttonbnlogs |
||
| 154 | ON bigbluebuttonbnlogs.bigbluebuttonbnid = bigbluebuttonbn.id |
||
| 155 | WHERE bigbluebuttonbnlogs.userid = :userid"; |
||
| 156 | |||
| 157 | $params = [ |
||
| 158 | 'modname' => 'bigbluebuttonbn', |
||
| 159 | 'contextlevel' => CONTEXT_MODULE, |
||
| 160 | 'userid' => $userid, |
||
| 161 | ]; |
||
| 162 | $contextlist = new contextlist(); |
||
| 163 | $contextlist->add_from_sql($sql, $params); |
||
| 164 | return $contextlist; |
||
| 165 | } |
||
| 166 | |||
| 167 | /** |
||
| 168 | * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist. |
||
| 169 | * |
||
| 170 | * @param approved_contextlist $contextlist a list of contexts approved for export. |
||
| 171 | */ |
||
| 172 | public static function _export_user_data(approved_contextlist $contextlist) { |
||
| 173 | self::_export_user_data_bigbliebuttonbn_logs($contextlist); |
||
| 174 | } |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Delete all data for all users in the specified context. |
||
| 178 | * |
||
| 179 | * @param \context $context the context to delete in. |
||
| 180 | */ |
||
| 181 | public static function _delete_data_for_all_users_in_context(\context $context) { |
||
| 182 | global $DB; |
||
| 183 | |||
| 184 | if (!$context instanceof \context_module) { |
||
|
0 ignored issues
–
show
The class
context_module does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. Loading history...
|
|||
| 185 | return; |
||
| 186 | } |
||
| 187 | |||
| 188 | $instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid], MUST_EXIST); |
||
| 189 | $DB->delete_records('bigbluebuttonbn_logs', ['bigbluebuttonbnid' => $instanceid]); |
||
| 190 | } |
||
| 191 | |||
| 192 | /** |
||
| 193 | * Delete all user data for the specified user, in the specified contexts. |
||
| 194 | * |
||
| 195 | * @param approved_contextlist $contextlist a list of contexts approved for deletion. |
||
| 196 | */ |
||
| 197 | public static function _delete_data_for_user(approved_contextlist $contextlist) { |
||
| 198 | global $DB; |
||
| 199 | $count = $contextlist->count(); |
||
| 200 | if (empty($count)) { |
||
| 201 | return; |
||
| 202 | } |
||
| 203 | $userid = $contextlist->get_user()->id; |
||
| 204 | foreach ($contextlist->get_contexts() as $context) { |
||
| 205 | if (!$context instanceof \context_module) { |
||
|
0 ignored issues
–
show
The class
context_module does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. Loading history...
|
|||
| 206 | return; |
||
| 207 | } |
||
| 208 | $instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid], MUST_EXIST); |
||
| 209 | $DB->delete_records('bigbluebuttonbn_logs', ['bigbluebuttonbnid' => $instanceid, 'userid' => $userid]); |
||
| 210 | } |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * Export personal data for the given approved_contextlist related to bigbluebuttonbn logs. |
||
| 215 | * |
||
| 216 | * @param approved_contextlist $contextlist a list of contexts approved for export. |
||
| 217 | */ |
||
| 218 | protected static function _export_user_data_bigbliebuttonbn_logs(approved_contextlist $contextlist) { |
||
| 219 | global $DB; |
||
| 220 | |||
| 221 | // Filter out any contexts that are not related to modules. |
||
| 222 | $cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) { |
||
| 223 | if ($context->contextlevel == CONTEXT_MODULE) { |
||
| 224 | $carry[] = $context->instanceid; |
||
| 225 | } |
||
| 226 | return $carry; |
||
| 227 | }, []); |
||
| 228 | |||
| 229 | if (empty($cmids)) { |
||
| 230 | return; |
||
| 231 | } |
||
| 232 | |||
| 233 | $user = $contextlist->get_user(); |
||
| 234 | |||
| 235 | // Get all the bigbluebuttonbn activities associated with the above course modules. |
||
| 236 | $instanceidstocmids = self::get_instance_ids_to_cmids_from_cmids($cmids); |
||
| 237 | $instanceids = array_keys($instanceidstocmids); |
||
| 238 | |||
| 239 | list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED); |
||
| 240 | $params = array_merge($inparams, ['userid' => $user->id]); |
||
| 241 | $recordset = $DB->get_recordset_select( |
||
| 242 | 'bigbluebuttonbn_logs', "bigbluebuttonbnid $insql AND userid = :userid", $params, 'timecreated, id'); |
||
| 243 | self::recordset_loop_and_export($recordset, 'bigbluebuttonbnid', [], |
||
| 244 | function($carry, $record) use ($user, $instanceidstocmids) { |
||
| 245 | $carry[] = [ |
||
| 246 | 'timecreated' => transform::datetime($record->timecreated), |
||
| 247 | 'meetingid' => $record->meetingid, |
||
| 248 | 'log' => $record->log, |
||
| 249 | 'meta' => $record->meta, |
||
| 250 | ]; |
||
| 251 | return $carry; |
||
| 252 | }, |
||
| 253 | function($instanceid, $data) use ($user, $instanceidstocmids) { |
||
| 254 | $context = \context_module::instance($instanceidstocmids[$instanceid]); |
||
| 255 | $contextdata = helper::get_context_data($context, $user); |
||
| 256 | $finaldata = (object) array_merge((array) $contextdata, ['logs' => $data]); |
||
| 257 | helper::export_context_files($context, $user); |
||
| 258 | writer::with_context($context)->export_data([], $finaldata); |
||
| 259 | } |
||
| 260 | ); |
||
| 261 | } |
||
| 262 | |||
| 263 | /** |
||
| 264 | * Return a dict of bigbluebuttonbn IDs mapped to their course module ID. |
||
| 265 | * |
||
| 266 | * @param array $cmids The course module IDs. |
||
| 267 | * @return array In the form of [$bigbluebuttonbnid => $cmid]. |
||
| 268 | */ |
||
| 269 | protected static function get_instance_ids_to_cmids_from_cmids(array $cmids) { |
||
| 270 | global $DB; |
||
| 271 | |||
| 272 | list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED); |
||
| 273 | $sql = "SELECT bigbluebuttonbn.id, cm.id AS cmid |
||
| 274 | FROM {bigbluebuttonbn} bigbluebuttonbn |
||
| 275 | JOIN {modules} m |
||
| 276 | ON m.name = :bigbluebuttonbn |
||
| 277 | JOIN {course_modules} cm |
||
| 278 | ON cm.instance = bigbluebuttonbn.id |
||
| 279 | AND cm.module = m.id |
||
| 280 | WHERE cm.id $insql"; |
||
| 281 | $params = array_merge($inparams, ['bigbluebuttonbn' => 'bigbluebuttonbn']); |
||
| 282 | |||
| 283 | return $DB->get_records_sql_menu($sql, $params); |
||
| 284 | } |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Loop and export from a recordset. |
||
| 288 | * |
||
| 289 | * @param \moodle_recordset $recordset The recordset. |
||
| 290 | * @param string $splitkey The record key to determine when to export. |
||
| 291 | * @param mixed $initial The initial data to reduce from. |
||
| 292 | * @param callable $reducer The function to return the dataset, receives current dataset, and the current record. |
||
| 293 | * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset. |
||
| 294 | * @return void |
||
| 295 | */ |
||
| 296 | protected static function recordset_loop_and_export(\moodle_recordset $recordset, $splitkey, $initial, |
||
| 297 | callable $reducer, callable $export) { |
||
| 298 | $data = $initial; |
||
| 299 | $lastid = null; |
||
| 300 | |||
| 301 | foreach ($recordset as $record) { |
||
| 302 | if ($lastid && $record->{$splitkey} != $lastid) { |
||
| 303 | $export($lastid, $data); |
||
| 304 | $data = $initial; |
||
| 305 | } |
||
| 306 | $data = $reducer($data, $record); |
||
| 307 | $lastid = $record->{$splitkey}; |
||
| 308 | } |
||
| 309 | $recordset->close(); |
||
| 310 | |||
| 311 | if (!empty($lastid)) { |
||
| 312 | $export($lastid, $data); |
||
| 313 | } |
||
| 314 | } |
||
| 315 | |||
| 316 | /** |
||
| 317 | * Get the list of users who have data within a context. |
||
| 318 | * |
||
| 319 | * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. |
||
| 320 | */ |
||
| 321 | public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) { |
||
| 322 | |||
| 323 | $context = $userlist->get_context(); |
||
| 324 | |||
| 325 | if (!$context instanceof \context_module) { |
||
|
0 ignored issues
–
show
The class
context_module does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. Loading history...
|
|||
| 326 | return; |
||
| 327 | } |
||
| 328 | |||
| 329 | $params = [ |
||
| 330 | 'instanceid' => $context->instanceid, |
||
| 331 | 'modulename' => 'bigbluebuttonbn', |
||
| 332 | ]; |
||
| 333 | |||
| 334 | $sql = "SELECT bnl.userid |
||
| 335 | FROM {course_modules} cm |
||
| 336 | JOIN {modules} m ON m.id = cm.module AND m.name = :modulename |
||
| 337 | JOIN {bigbluebuttonbn} bn ON bn.id = cm.instance |
||
| 338 | JOIN {bigbluebuttonbn_logs} bnl ON bnl.bigbluebuttonbnid = bn.id |
||
| 339 | WHERE cm.id = :instanceid"; |
||
| 340 | |||
| 341 | $userlist->add_from_sql('userid', $sql, $params); |
||
| 342 | } |
||
| 343 | |||
| 344 | /** |
||
| 345 | * Delete multiple users within a single context. |
||
| 346 | * |
||
| 347 | * @param approved_userlist $userlist The approved context and user information to delete information for. |
||
| 348 | */ |
||
| 349 | public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) { |
||
| 350 | global $DB; |
||
| 351 | |||
| 352 | $context = $userlist->get_context(); |
||
| 353 | $cm = $DB->get_record('course_modules', ['id' => $context->instanceid]); |
||
| 354 | |||
| 355 | list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); |
||
| 356 | $params = array_merge(['bigbluebuttonbnid' => $cm->instance], $userinparams); |
||
| 357 | $sql = "bigbluebuttonbnid = :bigbluebuttonbnid AND userid {$userinsql}"; |
||
| 358 | |||
| 359 | $DB->delete_records_select('bigbluebuttonbn_logs', $sql, $params); |
||
| 360 | } |
||
| 361 | } |
||
| 362 |
This check looks for classes that have been defined more than once in the same file.
If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.
This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.