MDL-55358 mod_lti: Add group variable Moodle.Person.userGroupIds
[moodle.git] / report / security / locallib.php
blob7252fa25e95346125257b061cb9476e5ddae226a
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.
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/>.
17 /**
18 * Lib functions
20 * @package report
21 * @subpackage security
22 * @copyright 2008 petr Skoda
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die;
29 define('REPORT_SECURITY_OK', 'ok');
30 define('REPORT_SECURITY_INFO', 'info');
31 define('REPORT_SECURITY_WARNING', 'warning');
32 define('REPORT_SECURITY_SERIOUS', 'serious');
33 define('REPORT_SECURITY_CRITICAL', 'critical');
35 function report_security_hide_timearning() {
36 global $PAGE;
37 $PAGE->requires->js_init_code("Y.one('#timewarning').addClass('timewarninghidden')");
40 function report_security_get_issue_list() {
41 return array(
42 'report_security_check_unsecuredataroot',
43 'report_security_check_displayerrors',
44 'report_security_check_noauth',
45 'report_security_check_embed',
46 'report_security_check_mediafilterswf',
47 'report_security_check_openprofiles',
48 'report_security_check_google',
49 'report_security_check_passwordpolicy',
50 'report_security_check_emailchangeconfirmation',
51 'report_security_check_cookiesecure',
52 'report_security_check_configrw',
53 'report_security_check_riskxss',
54 'report_security_check_riskadmin',
55 'report_security_check_riskbackup',
56 'report_security_check_defaultuserrole',
57 'report_security_check_guestrole',
58 'report_security_check_frontpagerole',
59 'report_security_check_webcron',
60 'report_security_check_preventexecpath',
65 function report_security_doc_link($issue, $name) {
66 global $CFG, $OUTPUT;
68 if (empty($CFG->docroot)) {
69 return $name;
72 return $OUTPUT->doc_link('report/security/'.$issue, $name);
75 ///=============================================
76 /// Issue checks
77 ///=============================================
80 /**
81 * Verifies unsupported noauth setting
82 * @param bool $detailed
83 * @return object result
85 function report_security_check_noauth($detailed=false) {
86 global $CFG;
88 $result = new stdClass();
89 $result->issue = 'report_security_check_noauth';
90 $result->name = get_string('check_noauth_name', 'report_security');
91 $result->info = null;
92 $result->details = null;
93 $result->status = null;
94 $result->link = null;
95 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=manageauths\">".get_string('authsettings', 'admin').'</a>';
97 if (is_enabled_auth('none')) {
98 $result->status = REPORT_SECURITY_CRITICAL;
99 $result->info = get_string('check_noauth_error', 'report_security');
100 } else {
101 $result->status = REPORT_SECURITY_OK;
102 $result->info = get_string('check_noauth_ok', 'report_security');
105 if ($detailed) {
106 $result->details = get_string('check_noauth_details', 'report_security');
109 return $result;
113 * Verifies if password policy set
114 * @param bool $detailed
115 * @return object result
117 function report_security_check_passwordpolicy($detailed=false) {
118 global $CFG;
120 $result = new stdClass();
121 $result->issue = 'report_security_check_passwordpolicy';
122 $result->name = get_string('check_passwordpolicy_name', 'report_security');
123 $result->info = null;
124 $result->details = null;
125 $result->status = null;
126 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
128 if (empty($CFG->passwordpolicy)) {
129 $result->status = REPORT_SECURITY_WARNING;
130 $result->info = get_string('check_passwordpolicy_error', 'report_security');
131 } else {
132 $result->status = REPORT_SECURITY_OK;
133 $result->info = get_string('check_passwordpolicy_ok', 'report_security');
136 if ($detailed) {
137 $result->details = get_string('check_passwordpolicy_details', 'report_security');
140 return $result;
144 * Verifies sloppy embedding - this should have been removed long ago!!
145 * @param bool $detailed
146 * @return object result
148 function report_security_check_embed($detailed=false) {
149 global $CFG;
151 $result = new stdClass();
152 $result->issue = 'report_security_check_embed';
153 $result->name = get_string('check_embed_name', 'report_security');
154 $result->info = null;
155 $result->details = null;
156 $result->status = null;
157 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
159 if (!empty($CFG->allowobjectembed)) {
160 $result->status = REPORT_SECURITY_CRITICAL;
161 $result->info = get_string('check_embed_error', 'report_security');
162 } else {
163 $result->status = REPORT_SECURITY_OK;
164 $result->info = get_string('check_embed_ok', 'report_security');
167 if ($detailed) {
168 $result->details = get_string('check_embed_details', 'report_security');
171 return $result;
175 * Verifies sloppy swf embedding - this should have been removed long ago!!
176 * @param bool $detailed
177 * @return object result
179 function report_security_check_mediafilterswf($detailed=false) {
180 global $CFG;
182 $result = new stdClass();
183 $result->issue = 'report_security_check_mediafilterswf';
184 $result->name = get_string('check_mediafilterswf_name', 'report_security');
185 $result->info = null;
186 $result->details = null;
187 $result->status = null;
188 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=managemediaplayers\">" .
189 get_string('managemediaplayers', 'media') . '</a>';
191 $activefilters = filter_get_globally_enabled();
193 $enabledmediaplayers = \core\plugininfo\media::get_enabled_plugins();
194 if (array_search('mediaplugin', $activefilters) !== false and array_key_exists('swf', $enabledmediaplayers)) {
195 $result->status = REPORT_SECURITY_CRITICAL;
196 $result->info = get_string('check_mediafilterswf_error', 'report_security');
197 } else {
198 $result->status = REPORT_SECURITY_OK;
199 $result->info = get_string('check_mediafilterswf_ok', 'report_security');
202 if ($detailed) {
203 $result->details = get_string('check_mediafilterswf_details', 'report_security');
206 return $result;
210 * Verifies fatal misconfiguration of dataroot
211 * @param bool $detailed
212 * @return object result
214 function report_security_check_unsecuredataroot($detailed=false) {
215 global $CFG;
217 $result = new stdClass();
218 $result->issue = 'report_security_check_unsecuredataroot';
219 $result->name = get_string('check_unsecuredataroot_name', 'report_security');
220 $result->info = null;
221 $result->details = null;
222 $result->status = null;
223 $result->link = null;
225 $insecuredataroot = is_dataroot_insecure(true);
227 if ($insecuredataroot == INSECURE_DATAROOT_WARNING) {
228 $result->status = REPORT_SECURITY_SERIOUS;
229 $result->info = get_string('check_unsecuredataroot_warning', 'report_security', $CFG->dataroot);
231 } else if ($insecuredataroot == INSECURE_DATAROOT_ERROR) {
232 $result->status = REPORT_SECURITY_CRITICAL;
233 $result->info = get_string('check_unsecuredataroot_error', 'report_security', $CFG->dataroot);
235 } else {
236 $result->status = REPORT_SECURITY_OK;
237 $result->info = get_string('check_unsecuredataroot_ok', 'report_security');
240 if ($detailed) {
241 $result->details = get_string('check_unsecuredataroot_details', 'report_security');
244 return $result;
248 * Verifies displaying of errors - problem for lib files and 3rd party code
249 * because we can not disable debugging in these scripts (they do not include config.php)
250 * @param bool $detailed
251 * @return object result
253 function report_security_check_displayerrors($detailed=false) {
254 $result = new stdClass();
255 $result->issue = 'report_security_check_displayerrors';
256 $result->name = get_string('check_displayerrors_name', 'report_security');
257 $result->info = null;
258 $result->details = null;
259 $result->status = null;
260 $result->link = null;
262 if (defined('WARN_DISPLAY_ERRORS_ENABLED')) {
263 $result->status = REPORT_SECURITY_WARNING;
264 $result->info = get_string('check_displayerrors_error', 'report_security');
265 } else {
266 $result->status = REPORT_SECURITY_OK;
267 $result->info = get_string('check_displayerrors_ok', 'report_security');
270 if ($detailed) {
271 $result->details = get_string('check_displayerrors_details', 'report_security');
274 return $result;
278 * Verifies open profiles - originally open by default, not anymore because spammer abused it a lot
279 * @param bool $detailed
280 * @return object result
282 function report_security_check_openprofiles($detailed=false) {
283 global $CFG;
285 $result = new stdClass();
286 $result->issue = 'report_security_check_openprofiles';
287 $result->name = get_string('check_openprofiles_name', 'report_security');
288 $result->info = null;
289 $result->details = null;
290 $result->status = null;
291 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
293 if (empty($CFG->forcelogin) and empty($CFG->forceloginforprofiles)) {
294 $result->status = REPORT_SECURITY_WARNING;
295 $result->info = get_string('check_openprofiles_error', 'report_security');
296 } else {
297 $result->status = REPORT_SECURITY_OK;
298 $result->info = get_string('check_openprofiles_ok', 'report_security');
301 if ($detailed) {
302 $result->details = get_string('check_openprofiles_details', 'report_security');
305 return $result;
309 * Verifies google access not combined with disabled guest access
310 * because attackers might gain guest access by modifying browser signature.
311 * @param bool $detailed
312 * @return object result
314 function report_security_check_google($detailed=false) {
315 global $CFG;
317 $result = new stdClass();
318 $result->issue = 'report_security_check_google';
319 $result->name = get_string('check_google_name', 'report_security');
320 $result->info = null;
321 $result->details = null;
322 $result->status = null;
323 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
325 if (empty($CFG->opentogoogle)) {
326 $result->status = REPORT_SECURITY_OK;
327 $result->info = get_string('check_google_ok', 'report_security');
328 } else if (!empty($CFG->guestloginbutton)) {
329 $result->status = REPORT_SECURITY_INFO;
330 $result->info = get_string('check_google_info', 'report_security');
331 } else {
332 $result->status = REPORT_SECURITY_SERIOUS;
333 $result->info = get_string('check_google_error', 'report_security');
336 if ($detailed) {
337 $result->details = get_string('check_google_details', 'report_security');
340 return $result;
344 * Verifies email confirmation - spammers were changing mails very often
345 * @param bool $detailed
346 * @return object result
348 function report_security_check_emailchangeconfirmation($detailed=false) {
349 global $CFG;
351 $result = new stdClass();
352 $result->issue = 'report_security_check_emailchangeconfirmation';
353 $result->name = get_string('check_emailchangeconfirmation_name', 'report_security');
354 $result->info = null;
355 $result->details = null;
356 $result->status = null;
357 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
359 if (empty($CFG->emailchangeconfirmation)) {
360 if (empty($CFG->allowemailaddresses)) {
361 $result->status = REPORT_SECURITY_WARNING;
362 $result->info = get_string('check_emailchangeconfirmation_error', 'report_security');
363 } else {
364 $result->status = REPORT_SECURITY_INFO;
365 $result->info = get_string('check_emailchangeconfirmation_info', 'report_security');
367 } else {
368 $result->status = REPORT_SECURITY_OK;
369 $result->info = get_string('check_emailchangeconfirmation_ok', 'report_security');
372 if ($detailed) {
373 $result->details = get_string('check_emailchangeconfirmation_details', 'report_security');
376 return $result;
380 * Verifies if https enabled only secure cookies allowed,
381 * this prevents redirections and sending of cookies to unsecure port.
382 * @param bool $detailed
383 * @return object result
385 function report_security_check_cookiesecure($detailed=false) {
386 global $CFG;
388 if (!is_https()) {
389 return null;
392 $result = new stdClass();
393 $result->issue = 'report_security_check_cookiesecure';
394 $result->name = get_string('check_cookiesecure_name', 'report_security');
395 $result->info = null;
396 $result->details = null;
397 $result->status = null;
398 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=httpsecurity\">".get_string('httpsecurity', 'admin').'</a>';
400 if (!is_moodle_cookie_secure()) {
401 $result->status = REPORT_SECURITY_SERIOUS;
402 $result->info = get_string('check_cookiesecure_error', 'report_security');
403 } else {
404 $result->status = REPORT_SECURITY_OK;
405 $result->info = get_string('check_cookiesecure_ok', 'report_security');
408 if ($detailed) {
409 $result->details = get_string('check_cookiesecure_details', 'report_security');
412 return $result;
416 * Verifies config.php is not writable anymore after installation,
417 * config files were changed on several outdated server.
418 * @param bool $detailed
419 * @return object result
421 function report_security_check_configrw($detailed=false) {
422 global $CFG;
424 $result = new stdClass();
425 $result->issue = 'report_security_check_configrw';
426 $result->name = get_string('check_configrw_name', 'report_security');
427 $result->info = null;
428 $result->details = null;
429 $result->status = null;
430 $result->link = null;
432 if (is_writable($CFG->dirroot.'/config.php')) {
433 $result->status = REPORT_SECURITY_WARNING;
434 $result->info = get_string('check_configrw_warning', 'report_security');
435 } else {
436 $result->status = REPORT_SECURITY_OK;
437 $result->info = get_string('check_configrw_ok', 'report_security');
440 if ($detailed) {
441 $result->details = get_string('check_configrw_details', 'report_security');
444 return $result;
449 * Lists all users with XSS risk, it would be great to combine this with risk trusts in user table,
450 * unfortunately nobody implemented user trust UI yet :-(
451 * @param bool $detailed
452 * @return object result
454 function report_security_check_riskxss($detailed=false) {
455 global $DB;
457 $result = new stdClass();
458 $result->issue = 'report_security_check_riskxss';
459 $result->name = get_string('check_riskxss_name', 'report_security');
460 $result->info = null;
461 $result->details = null;
462 $result->status = REPORT_SECURITY_WARNING;
463 $result->link = null;
465 $params = array('capallow'=>CAP_ALLOW);
467 $sqlfrom = "FROM (SELECT rcx.*
468 FROM {role_capabilities} rcx
469 JOIN {capabilities} cap ON (cap.name = rcx.capability AND ".$DB->sql_bitand('cap.riskbitmask', RISK_XSS)." <> 0)
470 WHERE rcx.permission = :capallow) rc,
471 {context} c,
472 {context} sc,
473 {role_assignments} ra,
474 {user} u
475 WHERE c.id = rc.contextid
476 AND (sc.path = c.path OR sc.path LIKE ".$DB->sql_concat('c.path', "'/%'")." OR c.path LIKE ".$DB->sql_concat('sc.path', "'/%'").")
477 AND u.id = ra.userid AND u.deleted = 0
478 AND ra.contextid = sc.id AND ra.roleid = rc.roleid";
480 $count = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) $sqlfrom", $params);
482 $result->info = get_string('check_riskxss_warning', 'report_security', $count);
484 if ($detailed) {
485 $userfields = user_picture::fields('u');
486 $users = $DB->get_records_sql("SELECT DISTINCT $userfields $sqlfrom", $params);
487 foreach ($users as $uid=>$user) {
488 $users[$uid] = fullname($user);
490 $users = implode(', ', $users);
491 $result->details = get_string('check_riskxss_details', 'report_security', $users);
494 return $result;
498 * Verifies sanity of default user role.
499 * @param bool $detailed
500 * @return object result
502 function report_security_check_defaultuserrole($detailed=false) {
503 global $DB, $CFG;
505 $result = new stdClass();
506 $result->issue = 'report_security_check_defaultuserrole';
507 $result->name = get_string('check_defaultuserrole_name', 'report_security');
508 $result->info = null;
509 $result->details = null;
510 $result->status = null;
511 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'</a>';
513 if (!$default_role = $DB->get_record('role', array('id'=>$CFG->defaultuserroleid))) {
514 $result->status = REPORT_SECURITY_WARNING;
515 $result->info = get_string('check_defaultuserrole_notset', 'report_security');
516 $result->details = $result->info;
518 return $result;
521 // risky caps - usually very dangerous
522 $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$default_role->id);
523 $sql = "SELECT COUNT(DISTINCT rc.contextid)
524 FROM {role_capabilities} rc
525 JOIN {capabilities} cap ON cap.name = rc.capability
526 WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
527 AND rc.permission = :capallow
528 AND rc.roleid = :roleid";
530 $riskycount = $DB->count_records_sql($sql, $params);
532 // it may have either none or 'user' archetype - nothing else, or else it would break during upgrades badly
533 if ($default_role->archetype === '' or $default_role->archetype === 'user') {
534 $legacyok = true;
535 } else {
536 $legacyok = false;
539 if ($riskycount or !$legacyok) {
540 $result->status = REPORT_SECURITY_CRITICAL;
541 $result->info = get_string('check_defaultuserrole_error', 'report_security', role_get_name($default_role));
543 } else {
544 $result->status = REPORT_SECURITY_OK;
545 $result->info = get_string('check_defaultuserrole_ok', 'report_security');
548 if ($detailed) {
549 $result->details = get_string('check_defaultuserrole_details', 'report_security');
552 return $result;
556 * Verifies sanity of guest role
557 * @param bool $detailed
558 * @return object result
560 function report_security_check_guestrole($detailed=false) {
561 global $DB, $CFG;
563 $result = new stdClass();
564 $result->issue = 'report_security_check_guestrole';
565 $result->name = get_string('check_guestrole_name', 'report_security');
566 $result->info = null;
567 $result->details = null;
568 $result->status = null;
569 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'</a>';
571 if (!$guest_role = $DB->get_record('role', array('id'=>$CFG->guestroleid))) {
572 $result->status = REPORT_SECURITY_WARNING;
573 $result->info = get_string('check_guestrole_notset', 'report_security');
574 $result->details = $result->info;
576 return $result;
579 // risky caps - usually very dangerous
580 $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$guest_role->id);
581 $sql = "SELECT COUNT(DISTINCT rc.contextid)
582 FROM {role_capabilities} rc
583 JOIN {capabilities} cap ON cap.name = rc.capability
584 WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
585 AND rc.permission = :capallow
586 AND rc.roleid = :roleid";
588 $riskycount = $DB->count_records_sql($sql, $params);
590 // it may have either no or 'guest' archetype - nothing else, or else it would break during upgrades badly
591 if ($guest_role->archetype === '' or $guest_role->archetype === 'guest') {
592 $legacyok = true;
593 } else {
594 $legacyok = false;
597 if ($riskycount or !$legacyok) {
598 $result->status = REPORT_SECURITY_CRITICAL;
599 $result->info = get_string('check_guestrole_error', 'report_security', format_string($guest_role->name));
601 } else {
602 $result->status = REPORT_SECURITY_OK;
603 $result->info = get_string('check_guestrole_ok', 'report_security');
606 if ($detailed) {
607 $result->details = get_string('check_guestrole_details', 'report_security');
610 return $result;
614 * Verifies sanity of frontpage role
615 * @param bool $detailed
616 * @return object result
618 function report_security_check_frontpagerole($detailed=false) {
619 global $DB, $CFG;
621 $result = new stdClass();
622 $result->issue = 'report_security_check_frontpagerole';
623 $result->name = get_string('check_frontpagerole_name', 'report_security');
624 $result->info = null;
625 $result->details = null;
626 $result->status = null;
627 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=frontpagesettings\">".get_string('frontpagesettings','admin').'</a>';
629 if (!$frontpage_role = $DB->get_record('role', array('id'=>$CFG->defaultfrontpageroleid))) {
630 $result->status = REPORT_SECURITY_INFO;
631 $result->info = get_string('check_frontpagerole_notset', 'report_security');
632 $result->details = get_string('check_frontpagerole_details', 'report_security');
634 return $result;
637 // risky caps - usually very dangerous
638 $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$frontpage_role->id);
639 $sql = "SELECT COUNT(DISTINCT rc.contextid)
640 FROM {role_capabilities} rc
641 JOIN {capabilities} cap ON cap.name = rc.capability
642 WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
643 AND rc.permission = :capallow
644 AND rc.roleid = :roleid";
646 $riskycount = $DB->count_records_sql($sql, $params);
648 // there is no legacy role type for frontpage yet - anyway we can not allow teachers or admins there!
649 if ($frontpage_role->archetype === 'teacher' or $frontpage_role->archetype === 'editingteacher'
650 or $frontpage_role->archetype === 'coursecreator' or $frontpage_role->archetype === 'manager') {
651 $legacyok = false;
652 } else {
653 $legacyok = true;
656 if ($riskycount or !$legacyok) {
657 $result->status = REPORT_SECURITY_CRITICAL;
658 $result->info = get_string('check_frontpagerole_error', 'report_security', format_string($frontpage_role->name));
660 } else {
661 $result->status = REPORT_SECURITY_OK;
662 $result->info = get_string('check_frontpagerole_ok', 'report_security');
665 if ($detailed) {
666 $result->details = get_string('check_frontpagerole_details', 'report_security');
669 return $result;
673 * Lists all admins.
674 * @param bool $detailed
675 * @return object result
677 function report_security_check_riskadmin($detailed=false) {
678 global $DB, $CFG;
680 $result = new stdClass();
681 $result->issue = 'report_security_check_riskadmin';
682 $result->name = get_string('check_riskadmin_name', 'report_security');
683 $result->info = null;
684 $result->details = null;
685 $result->status = null;
686 $result->link = null;
688 $userfields = user_picture::fields('u');
689 $sql = "SELECT $userfields
690 FROM {user} u
691 WHERE u.id IN ($CFG->siteadmins)";
693 $admins = $DB->get_records_sql($sql);
694 $admincount = count($admins);
696 if ($detailed) {
697 foreach ($admins as $uid=>$user) {
698 $url = "$CFG->wwwroot/user/view.php?id=$user->id";
699 $admins[$uid] = '<li><a href="'.$url.'">'.fullname($user).' ('.$user->email.')</a></li>';
701 $admins = '<ul>'.implode('', $admins).'</ul>';
704 $result->status = REPORT_SECURITY_OK;
705 $result->info = get_string('check_riskadmin_ok', 'report_security', $admincount);
707 if ($detailed) {
708 $result->details = get_string('check_riskadmin_detailsok', 'report_security', $admins);
711 return $result;
715 * Lists all roles that have the ability to backup user data, as well as users
716 * @param bool $detailed
717 * @return object result
719 function report_security_check_riskbackup($detailed=false) {
720 global $CFG, $DB;
722 $result = new stdClass();
723 $result->issue = 'report_security_check_riskbackup';
724 $result->name = get_string('check_riskbackup_name', 'report_security');
725 $result->info = null;
726 $result->details = null;
727 $result->status = null;
728 $result->link = null;
730 $syscontext = context_system::instance();
732 $params = array('capability'=>'moodle/backup:userinfo', 'permission'=>CAP_ALLOW, 'contextid'=>$syscontext->id);
733 $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder, r.archetype
734 FROM {role} r
735 JOIN {role_capabilities} rc ON rc.roleid = r.id
736 WHERE rc.capability = :capability
737 AND rc.contextid = :contextid
738 AND rc.permission = :permission";
739 $systemroles = $DB->get_records_sql($sql, $params);
741 $params = array('capability'=>'moodle/backup:userinfo', 'permission'=>CAP_ALLOW, 'contextid'=>$syscontext->id);
742 $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder, r.archetype, rc.contextid
743 FROM {role} r
744 JOIN {role_capabilities} rc ON rc.roleid = r.id
745 WHERE rc.capability = :capability
746 AND rc.contextid <> :contextid
747 AND rc.permission = :permission";
748 $overriddenroles = $DB->get_records_sql($sql, $params);
750 // list of users that are able to backup personal info
751 // note: "sc" is context where is role assigned,
752 // "c" is context where is role overridden or system context if in role definition
753 $params = array('capability'=>'moodle/backup:userinfo', 'permission'=>CAP_ALLOW, 'context1'=>CONTEXT_COURSE, 'context2'=>CONTEXT_COURSE);
755 $sqluserinfo = "
756 FROM (SELECT rcx.*
757 FROM {role_capabilities} rcx
758 WHERE rcx.permission = :permission AND rcx.capability = :capability) rc,
759 {context} c,
760 {context} sc,
761 {role_assignments} ra,
762 {user} u
763 WHERE c.id = rc.contextid
764 AND (sc.path = c.path OR sc.path LIKE ".$DB->sql_concat('c.path', "'/%'")." OR c.path LIKE ".$DB->sql_concat('sc.path', "'/%'").")
765 AND u.id = ra.userid AND u.deleted = 0
766 AND ra.contextid = sc.id AND ra.roleid = rc.roleid
767 AND sc.contextlevel <= :context1 AND c.contextlevel <= :context2";
769 $usercount = $DB->count_records_sql("SELECT COUNT('x') FROM (SELECT DISTINCT u.id $sqluserinfo) userinfo", $params);
770 $systemrolecount = empty($systemroles) ? 0 : count($systemroles);
771 $overriddenrolecount = empty($overriddenroles) ? 0 : count($overriddenroles);
773 if (max($usercount, $systemrolecount, $overriddenrolecount) > 0) {
774 $result->status = REPORT_SECURITY_WARNING;
775 } else {
776 $result->status = REPORT_SECURITY_OK;
779 $a = (object)array('rolecount'=>$systemrolecount,'overridecount'=>$overriddenrolecount,'usercount'=>$usercount);
780 $result->info = get_string('check_riskbackup_warning', 'report_security', $a);
782 if ($detailed) {
784 $result->details = ''; // Will be added to later
786 // Make a list of roles
787 if ($systemroles) {
788 $links = array();
789 foreach ($systemroles as $role) {
790 $role->name = role_get_name($role);
791 $role->url = "$CFG->wwwroot/$CFG->admin/roles/manage.php?action=edit&amp;roleid=$role->id";
792 $links[] = '<li>'.get_string('check_riskbackup_editrole', 'report_security', $role).'</li>';
794 $links = '<ul>'.implode($links).'</ul>';
795 $result->details .= get_string('check_riskbackup_details_systemroles', 'report_security', $links);
798 // Make a list of overrides to roles
799 $rolelinks2 = array();
800 if ($overriddenroles) {
801 $links = array();
802 foreach ($overriddenroles as $role) {
803 $role->name = $role->localname;
804 $context = context::instance_by_id($role->contextid);
805 $role->name = role_get_name($role, $context, ROLENAME_BOTH);
806 $role->contextname = $context->get_context_name();
807 $role->url = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$role->contextid&amp;roleid=$role->id";
808 $links[] = '<li>'.get_string('check_riskbackup_editoverride', 'report_security', $role).'</li>';
810 $links = '<ul>'.implode('', $links).'</ul>';
811 $result->details .= get_string('check_riskbackup_details_overriddenroles', 'report_security', $links);
814 // Get a list of affected users as well
815 $users = array();
817 list($sort, $sortparams) = users_order_by_sql('u');
818 $userfields = user_picture::fields('u');
819 $rs = $DB->get_recordset_sql("SELECT DISTINCT $userfields, ra.contextid, ra.roleid
820 $sqluserinfo ORDER BY $sort", array_merge($params, $sortparams));
822 foreach ($rs as $user) {
823 $context = context::instance_by_id($user->contextid);
824 $url = "$CFG->wwwroot/$CFG->admin/roles/assign.php?contextid=$user->contextid&amp;roleid=$user->roleid";
825 $a = (object)array('fullname'=>fullname($user), 'url'=>$url, 'email'=>$user->email,
826 'contextname'=>$context->get_context_name());
827 $users[] = '<li>'.get_string('check_riskbackup_unassign', 'report_security', $a).'</li>';
829 if (!empty($users)) {
830 $users = '<ul>'.implode('', $users).'</ul>';
831 $result->details .= get_string('check_riskbackup_details_users', 'report_security', $users);
835 return $result;
839 * Verifies the status of web cron
841 * @param bool $detailed
842 * @return object result
844 function report_security_check_webcron($detailed = false) {
845 global $CFG;
847 $croncli = $CFG->cronclionly;
848 $cronremotepassword = $CFG->cronremotepassword;
850 $result = new stdClass();
851 $result->issue = 'report_security_check_webcron';
852 $result->name = get_string('check_webcron_name', 'report_security');
853 $result->details = null;
854 $result->link = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">"
855 .get_string('sitepolicies', 'admin').'</a>';
857 if (empty($croncli) && empty($cronremotepassword)) {
858 $result->status = REPORT_SECURITY_WARNING;
859 $result->info = get_string('check_webcron_warning', 'report_security');
860 } else {
861 $result->status = REPORT_SECURITY_OK;
862 $result->info = get_string('check_webcron_ok', 'report_security');
865 if ($detailed) {
866 $result->details = get_string('check_webcron_details', 'report_security');
869 return $result;
873 * Verifies the status of preventexecpath
875 * @param bool $detailed
876 * @return object result
878 function report_security_check_preventexecpath($detailed = false) {
879 global $CFG;
881 $result = new stdClass();
882 $result->issue = 'report_security_check_preventexecpath';
883 $result->name = get_string('check_preventexecpath_name', 'report_security');
884 $result->details = null;
885 $result->link = null;
887 if (empty($CFG->preventexecpath)) {
888 $result->status = REPORT_SECURITY_WARNING;
889 $result->info = get_string('check_preventexecpath_warning', 'report_security');
890 if ($detailed) {
891 $result->details = get_string('check_preventexecpath_details', 'report_security');
893 } else {
894 $result->status = REPORT_SECURITY_OK;
895 $result->info = get_string('check_preventexecpath_ok', 'report_security');
898 return $result;