mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
Modify notify_login_failures() for better scale. MDL-11661
This commit is contained in:
parent
e072450699
commit
4b3f69957e
1 changed files with 90 additions and 59 deletions
|
@ -5827,6 +5827,10 @@ function moodle_needs_upgrading() {
|
||||||
/**
|
/**
|
||||||
* Notify admin users or admin user of any failed logins (since last notification).
|
* Notify admin users or admin user of any failed logins (since last notification).
|
||||||
*
|
*
|
||||||
|
* Note that this function must be only executed from the cron script
|
||||||
|
* It uses the cache_flags system to store temporary records, deleting them
|
||||||
|
* by name before finishing
|
||||||
|
*
|
||||||
* @uses $CFG
|
* @uses $CFG
|
||||||
* @uses $db
|
* @uses $db
|
||||||
* @uses HOURSECS
|
* @uses HOURSECS
|
||||||
|
@ -5852,67 +5856,94 @@ function notify_login_failures() {
|
||||||
$CFG->notifyloginthreshold = 10; // default to something sensible.
|
$CFG->notifyloginthreshold = 10; // default to something sensible.
|
||||||
}
|
}
|
||||||
|
|
||||||
$notifyipsrs = $db->Execute('SELECT ip FROM '. $CFG->prefix .'log WHERE time > '. $CFG->lastnotifyfailure .'
|
/// Get all the IPs with more than notifyloginthreshold failures since lastnotifyfailure
|
||||||
AND module=\'login\' AND action=\'error\' GROUP BY ip HAVING count(*) > '. $CFG->notifyloginthreshold);
|
/// and insert them into the cache_flags temp table
|
||||||
|
$iprs = get_recordset_sql("SELECT ip, count(*)
|
||||||
$notifyusersrs = $db->Execute('SELECT info FROM '. $CFG->prefix .'log WHERE time > '. $CFG->lastnotifyfailure .'
|
FROM {$CFG->prefix}log
|
||||||
AND module=\'login\' AND action=\'error\' GROUP BY info HAVING count(*) > '. $CFG->notifyloginthreshold);
|
WHERE module = 'login'
|
||||||
|
AND action = 'error'
|
||||||
if ($notifyipsrs) {
|
AND time > $CFG->lastnotifyfailure
|
||||||
$ipstr = '';
|
GROUP BY ip
|
||||||
while ($row = rs_fetch_next_record($notifyipsrs)) {
|
HAVING count(*) >= $CFG->notifyloginthreshold");
|
||||||
$ipstr .= "'". $row->ip ."',";
|
while ($iprec = rs_fetch_next_record($iprs)) {
|
||||||
}
|
if (!empty($iprec->ip)) {
|
||||||
rs_close($notifyipsrs);
|
set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
|
||||||
$ipstr = substr($ipstr,0,strlen($ipstr)-1);
|
|
||||||
}
|
|
||||||
if ($notifyusersrs) {
|
|
||||||
$userstr = '';
|
|
||||||
while ($row = rs_fetch_next_record($notifyusersrs)) {
|
|
||||||
$userstr .= "'". $row->info ."',";
|
|
||||||
}
|
|
||||||
rs_close($notifyusersrs);
|
|
||||||
$userstr = substr($userstr,0,strlen($userstr)-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($userstr) > 0 || strlen($ipstr) > 0) {
|
|
||||||
$count = 0;
|
|
||||||
$logs = get_logs('time > '. $CFG->lastnotifyfailure .' AND module=\'login\' AND action=\'error\' '
|
|
||||||
.((strlen($ipstr) > 0 && strlen($userstr) > 0) ? ' AND ( ip IN ('. $ipstr .') OR info IN ('. $userstr .') ) '
|
|
||||||
: ((strlen($ipstr) != 0) ? ' AND ip IN ('. $ipstr .') ' : ' AND info IN ('. $userstr .') ')), 'l.time DESC', '', '', $count);
|
|
||||||
|
|
||||||
// if we haven't run in the last hour and we have something useful to report and we are actually supposed to be reporting to somebody
|
|
||||||
if (is_array($recip) and count($recip) > 0 and ((time() - HOURSECS) > $CFG->lastnotifyfailure)
|
|
||||||
and is_array($logs) and count($logs) > 0) {
|
|
||||||
|
|
||||||
$message = '';
|
|
||||||
$site = get_site();
|
|
||||||
$subject = get_string('notifyloginfailuressubject', '', format_string($site->fullname));
|
|
||||||
$message .= get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot)
|
|
||||||
.(($CFG->lastnotifyfailure != 0) ? '('.userdate($CFG->lastnotifyfailure).')' : '')."\n\n";
|
|
||||||
foreach ($logs as $log) {
|
|
||||||
$log->time = userdate($log->time);
|
|
||||||
$message .= get_string('notifyloginfailuresmessage','',$log)."\n";
|
|
||||||
}
|
|
||||||
$message .= "\n\n".get_string('notifyloginfailuresmessageend','',$CFG->wwwroot)."\n\n";
|
|
||||||
foreach ($recip as $admin) {
|
|
||||||
mtrace('Emailing '. $admin->username .' about '. count($logs) .' failed login attempts');
|
|
||||||
email_to_user($admin,get_admin(),$subject,$message);
|
|
||||||
}
|
|
||||||
$conf = new object();
|
|
||||||
$conf->name = 'lastnotifyfailure';
|
|
||||||
$conf->value = time();
|
|
||||||
if ($current = get_record('config', 'name', 'lastnotifyfailure')) {
|
|
||||||
$conf->id = $current->id;
|
|
||||||
if (! update_record('config', $conf)) {
|
|
||||||
mtrace('Could not update last notify time');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (! insert_record('config', $conf)) {
|
|
||||||
mtrace('Could not set last notify time');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rs_close($iprs);
|
||||||
|
|
||||||
|
/// Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure
|
||||||
|
/// and insert them into the cache_flags temp table
|
||||||
|
$infors = get_recordset_sql("SELECT info, count(*)
|
||||||
|
FROM {$CFG->prefix}log
|
||||||
|
WHERE module = 'login'
|
||||||
|
AND action = 'error'
|
||||||
|
AND time > $CFG->lastnotifyfailure
|
||||||
|
GROUP BY info
|
||||||
|
HAVING count(*) >= $CFG->notifyloginthreshold");
|
||||||
|
while ($inforec = rs_fetch_next_record($infors)) {
|
||||||
|
if (!empty($inforec->info)) {
|
||||||
|
set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs_close($infors);
|
||||||
|
|
||||||
|
/// Now, select all the login error logged records belonging to the ips and infos
|
||||||
|
/// since lastnotifyfailure, that we have stored in the cache_flags table
|
||||||
|
$logsrs = get_recordset_sql("SELECT l.*, u.firstname, u.lastname
|
||||||
|
FROM {$CFG->prefix}log l
|
||||||
|
JOIN {$CFG->prefix}cache_flags cf ON (l.ip = cf.name)
|
||||||
|
LEFT JOIN {$CFG->prefix}user u ON (l.userid = u.id)
|
||||||
|
WHERE l.module = 'login'
|
||||||
|
AND l.action = 'error'
|
||||||
|
AND l.time > $CFG->lastnotifyfailure
|
||||||
|
AND cf.flagtype = 'login_failure_by_ip'
|
||||||
|
UNION ALL
|
||||||
|
SELECT l.*, u.firstname, u.lastname
|
||||||
|
FROM {$CFG->prefix}log l
|
||||||
|
JOIN {$CFG->prefix}cache_flags cf ON (l.info = cf.name)
|
||||||
|
LEFT JOIN {$CFG->prefix}user u ON (l.userid = u.id)
|
||||||
|
WHERE l.module = 'login'
|
||||||
|
AND l.action = 'error'
|
||||||
|
AND l.time > $CFG->lastnotifyfailure
|
||||||
|
AND cf.flagtype = 'login_failure_by_info'
|
||||||
|
ORDER BY time DESC");
|
||||||
|
|
||||||
|
/// Init some variables
|
||||||
|
$count = 0;
|
||||||
|
$messages = '';
|
||||||
|
/// Iterate over the logs recordset
|
||||||
|
while ($log = rs_fetch_next_record($logsrs)) {
|
||||||
|
$log->time = userdate($log->time);
|
||||||
|
$messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
rs_close($logsrs);
|
||||||
|
|
||||||
|
/// If we haven't run in the last hour and
|
||||||
|
/// we have something useful to report and we
|
||||||
|
/// are actually supposed to be reporting to somebody
|
||||||
|
if ((time() - HOURSECS) > $CFG->lastnotifyfailure && $count > 0 && is_array($recip) && count($recip) > 0) {
|
||||||
|
$site = get_site();
|
||||||
|
$subject = get_string('notifyloginfailuressubject', '', format_string($site->fullname));
|
||||||
|
/// Calculate the complete body of notification (start + messages + end)
|
||||||
|
$body = get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot) .
|
||||||
|
(($CFG->lastnotifyfailure != 0) ? '('.userdate($CFG->lastnotifyfailure).')' : '')."\n\n" .
|
||||||
|
$messages .
|
||||||
|
"\n\n".get_string('notifyloginfailuresmessageend','',$CFG->wwwroot)."\n\n";
|
||||||
|
|
||||||
|
/// For each destination, send mail
|
||||||
|
foreach ($recip as $admin) {
|
||||||
|
mtrace('Emailing '. $admin->username .' about '. $count .' failed login attempts');
|
||||||
|
email_to_user($admin,get_admin(), $subject, $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update lastnotifyfailure with current time
|
||||||
|
set_config('lastnotifyfailure', time());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finally, delete all the temp records we have created in cache_flags
|
||||||
|
delete_records_select('cache_flags', "flagtype IN ('login_failure_by_ip', 'login_failure_by_info')");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue