--- a/includes/sessions.php Thu Dec 17 04:29:55 2009 -0500
+++ b/includes/sessions.php Thu Dec 17 04:31:55 2009 -0500
@@ -737,19 +737,19 @@
'lockout_policy' => 'disable'
);
- if ( $lockout_data['lockout_policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
+ if ( $lockout_data['policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
{
$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
// increment fail count
- $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
- $lockout_data['lockout_fails']++;
+ $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
+ $lockout_data['fails']++;
return array(
'success' => false,
- 'error' => ( $lockout_data['lockout_fails'] >= $lockout_data['lockout_threshold'] ) ? 'locked_out' : 'invalid_credentials',
- 'lockout_threshold' => $lockout_data['lockout_threshold'],
- 'lockout_duration' => ( $lockout_data['lockout_duration'] ),
- 'lockout_fails' => $lockout_data['lockout_fails'],
- 'lockout_policy' => $lockout_data['lockout_policy']
+ 'error' => ( $lockout_data['fails'] >= $lockout_data['threshold'] ) ? 'locked_out' : 'invalid_credentials',
+ 'lockout_threshold' => $lockout_data['threshold'],
+ 'lockout_duration' => ( $lockout_data['duration'] ),
+ 'lockout_fails' => $lockout_data['fails'],
+ 'lockout_policy' => $lockout_data['policy']
);
}
@@ -866,7 +866,7 @@
{
$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
// increment fail count
- $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
+ $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
}
return array(
@@ -981,7 +981,7 @@
if(!is_int($user_id))
die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
if(!is_int($level))
- die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
+ die(var_dump($level) . '<br />Somehow an SQL injection attempt crawled into our session registrar! (2)');
// Update RAM
$this->user_id = $user_id;
--- a/install/schemas/mysql_stage2.sql Thu Dec 17 04:29:55 2009 -0500
+++ b/install/schemas/mysql_stage2.sql Thu Dec 17 04:31:55 2009 -0500
@@ -274,6 +274,7 @@
ipaddr varchar(40) NOT NULL,
action ENUM('credential', 'level') NOT NULL DEFAULT 'credential',
timestamp int(12) NOT NULL DEFAULT 0,
+ username varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY ( id )
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
--- a/install/schemas/postgresql_stage2.sql Thu Dec 17 04:29:55 2009 -0500
+++ b/install/schemas/postgresql_stage2.sql Thu Dec 17 04:31:55 2009 -0500
@@ -275,6 +275,7 @@
ipaddr varchar(40) NOT NULL,
action varchar(20) NOT NULL DEFAULT 'credential',
timestamp int NOT NULL DEFAULT 0,
+ username varchar(255) NOT NULL DEFAULT '',
CHECK ( action IN ('credential', 'level') ),
PRIMARY KEY ( id )
);
--- a/install/schemas/upgrade/1.1.6-1.1.7-mysql.sql Thu Dec 17 04:29:55 2009 -0500
+++ b/install/schemas/upgrade/1.1.6-1.1.7-mysql.sql Thu Dec 17 04:31:55 2009 -0500
@@ -1,2 +1,4 @@
ALTER TABLE {{TABLE_PREFIX}}users_extra ADD COLUMN date_format varchar(32) NOT NULL DEFAULT 'F d, Y';
ALTER TABLE {{TABLE_PREFIX}}users_extra ADD COLUMN time_format varchar(32) NOT NULL DEFAULT 'G:i';
+ALTER TABLE {{TABLE_PREFIX}}lockout ADD COLUMN username varchar(255) NOT NULL DEFAULT '';
+
--- a/install/schemas/upgrade/1.1.6-1.1.7-postgresql.sql Thu Dec 17 04:29:55 2009 -0500
+++ b/install/schemas/upgrade/1.1.6-1.1.7-postgresql.sql Thu Dec 17 04:31:55 2009 -0500
@@ -1,2 +1,4 @@
ALTER TABLE {{TABLE_PREFIX}}users_extra ADD COLUMN date_format varchar(32) NOT NULL DEFAULT 'F d, Y';
ALTER TABLE {{TABLE_PREFIX}}users_extra ADD COLUMN time_format varchar(32) NOT NULL DEFAULT 'G:i';
+ALTER TABLE {{TABLE_PREFIX}}lockout ADD COLUMN username varchar(255) NOT NULL DEFAULT '';
+
--- a/language/english/admin.json Thu Dec 17 04:29:55 2009 -0500
+++ b/language/english/admin.json Thu Dec 17 04:31:55 2009 -0500
@@ -235,16 +235,33 @@
stat_lastupdate_never: 'Never',
heading_alerts: 'Active alerts',
+
msg_demo_title: 'Enano is running in demo mode.',
msg_demo_body: 'If you borked something up, or if you\'re done testing, you can <a href="%reset_url%">reset this site</a>. The site is reset automatically once every two hours. When a reset is performed, all custom modifications to the site are lost and replaced with default values.',
+
msg_install_files_title: 'Installer files found',
msg_install_files_body: 'Please delete the install/ directory from your Enano installation folder – it contains sensitive tools that might allow your site to be compromised.',
+
heading_updates: 'Check for updates',
msg_updates_info: 'The Enano team will on occasion release new versions of Enano. We always recommend that you run the latest available version because many releases contain security patches. Enano checks for updates by looking at an <a href="%updates_url%">XML file</a> and doesn\'t share any information about your site.',
btn_check_updates: 'Check for updates',
+
heading_inactive_users: 'Users are awaiting activation',
msg_inactive_users_one: '1 user has requested manual account activation. You can activate the account by going to the <a %um_flags%>User Manager</a>.',
msg_inactive_users_plural: '%num_users% users have requested manual account activation. You can activate those accounts by going to the <a %um_flags%>User Manager</a>.',
+
+ msg_users_locked_out: 'Active IP address lockouts',
+ msg_users_locked_out_hint: 'The following IP addresses have been automatically locked out from login attempts. You can delete these active lockouts, if you choose.',
+ th_locked_out_ip: 'IP address',
+ th_locked_out_username: 'Username (most recent attempt)',
+ th_locked_out_status: 'Status',
+ th_locked_out_time: 'Time remaining',
+ lbl_locked_out_warned: 'Warned (failures: %fail_count%)',
+ lbl_locked_out_banned: 'Locked out',
+ btn_lockout_unblock: 'Unblock',
+ btn_lockout_clear: 'Clear',
+ msg_lockout_clear_success: 'The IP address %ip% has been cleared from the active lockout list.',
+
heading_docs: 'Enano documentation',
msg_docs_info: 'The <a href="http://docs.enanocms.org/" onclick="window.open(this.href); return false;">Enano administrator\'s handbook</a> is maintained as a wiki. It will help you get started with Enano and learn about how we do things differently.',
heading_support: 'Get support',
--- a/language/english/core.json Thu Dec 17 04:29:55 2009 -0500
+++ b/language/english/core.json Thu Dec 17 04:31:55 2009 -0500
@@ -801,6 +801,10 @@
unit_months: 'months',
unit_year: 'year',
unit_years: 'years',
+ unit_minute: 'minute',
+ unit_minutes: 'minutes',
+ unit_minute_short: 'min',
+ unit_minutes_short: 'mins'
}
}
};
--- a/plugins/SpecialAdmin.php Thu Dec 17 04:29:55 2009 -0500
+++ b/plugins/SpecialAdmin.php Thu Dec 17 04:31:55 2009 -0500
@@ -671,7 +671,7 @@
<small><?php echo $lang->get('acpgc_field_passminimum_hint'); ?></small>
</td>
<td class="row1">
- <input type="text" name="pw_strength_minimum" value="<?php echo ( $x = getConfig('pw_strength_minimum') ) ? $x : '-10'; ?>" />
+ <input type="text" name="pw_strength_minimum" value="<?php echo strval(getConfig('pw_strength_minimum', -10)); ?>" />
</td>
</tr>
@@ -2081,7 +2081,7 @@
echo $lang->get('adm_page_tagline');
?>
<script type="text/javascript">
- function ajaxPage(t)
+ function ajaxPage(t, qs)
{
if ( KILL_SWITCH )
{
@@ -2134,9 +2134,9 @@
});
return;
}
- ajaxPageBin(t);
+ ajaxPageBin(t, qs);
}
- function ajaxPageBin(t)
+ function ajaxPageBin(t, qs)
{
if ( KILL_SWITCH )
{
@@ -2144,8 +2144,11 @@
return false;
}
document.getElementById('ajaxPageContainer').innerHTML = '<div class="wait-box">Loading page...</div>';
- ajaxGet('<?php echo scriptPath; ?>/ajax.php?title='+t+'&_mode=getpage&noheaders&auth=' + ENANO_SID, function(ajax) {
- if ( ajax.readyState == 4 && ajax.status == 200 ) {
+ qs = qs ? '&' + qs : '';
+ ajaxGet(makeUrl(t, 'noheaders' + qs), function(ajax)
+ {
+ if ( ajax.readyState == 4 && ajax.status == 200 )
+ {
var response = String(ajax.responseText + '');
if ( check_json_response(response) )
{
--- a/plugins/admin/Home.php Thu Dec 17 04:29:55 2009 -0500
+++ b/plugins/admin/Home.php Thu Dec 17 04:31:55 2009 -0500
@@ -79,6 +79,8 @@
}
$db->free_result();
+ acp_usermanager_lockouts(true);
+
// Update checker
echo '<div class="acphome-box info">';
echo '<h3>' . $lang->get('acphome_heading_updates') . '</h3>';
--- a/plugins/admin/UserManager.php Thu Dec 17 04:29:55 2009 -0500
+++ b/plugins/admin/UserManager.php Thu Dec 17 04:31:55 2009 -0500
@@ -590,10 +590,12 @@
</tr>';
}
echo '</table>';
+ echo '</div>';
}
$db->free_result();
}
+ acp_usermanager_lockouts();
}
/**
@@ -1221,4 +1223,81 @@
}
-?>
+function acp_usermanager_lockouts($homewrap = false)
+{
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang;
+
+ // Locked out users
+
+ if ( !empty($_GET['clear_lockout']) && is_valid_ip($_GET['clear_lockout']) )
+ {
+ $ip = $db->escape($_GET['clear_lockout']);
+ $q = $db->sql_query('DELETE FROM ' . table_prefix . "lockout WHERE ipaddr = '$ip' AND timestamp > ( " . time() . " - (" . getConfig('lockout_duration', 15) . "*60) );");
+ if ( !$q )
+ $db->_die();
+
+ echo '<div class="info-box">' . $lang->get('acphome_msg_lockout_clear_success', array('ip' => htmlspecialchars($ip))) . '</div>';
+ }
+
+ $q = $db->sql_query('SELECT COUNT(id) AS fail_count, ipaddr, username, timestamp FROM ' . table_prefix . "lockout\n"
+ . " WHERE timestamp > ( " . time() . " - " . intval(getConfig('lockout_duration', 15)) . "*60 ) GROUP BY ipaddr ORDER BY COUNT(id) DESC, timestamp DESC;");
+ if ( !$q )
+ $db->_die();
+
+ if ( $db->numrows() > 0 )
+ {
+ if ( $homewrap )
+ echo '<div class="acphome-box notice">';
+ echo '<h3>' . $lang->get('acphome_msg_users_locked_out') . '</h3>';
+ echo '<p>' . $lang->get('acphome_msg_users_locked_out_hint') . '</p>';
+
+ ?>
+ <div class="tblholder" style="margin-bottom: 10px;">
+ <table width="100%" cellspacing="1" cellpadding="4">
+ <tr>
+ <th><?php echo $lang->get('acphome_th_locked_out_ip'); ?></th>
+ <th><?php echo $lang->get('acphome_th_locked_out_username'); ?></th>
+ <th><?php echo $lang->get('acphome_th_locked_out_status'); ?></th>
+ <th><?php echo $lang->get('acphome_th_locked_out_time'); ?></th>
+ <th></th>
+ </tr>
+ <?php
+
+ while ( $row = $db->fetchrow() )
+ {
+ echo '<tr>';
+ echo '<td class="row1">' . htmlspecialchars($row['ipaddr']) . '</td>';
+ echo '<td class="row2">' . htmlspecialchars($row['username']) . '</td>';
+ // status
+ echo '<td class="row1" style="text-align: center;">' .
+ ( $row['fail_count'] >= getConfig('lockout_threshold', 5)
+ ? '<b>' . $lang->get('acphome_lbl_locked_out_banned') . '</b>'
+ : $lang->get('acphome_lbl_locked_out_warned', array('fail_count' => $row['fail_count']))
+ )
+ . '</td>';
+ // time left
+ if ( $row['fail_count'] >= getConfig('lockout_threshold', 5) )
+ {
+ $expire_time = $row['timestamp'] + ( getConfig('lockout_duration', 15) * 60 );
+ $time_left = round(($expire_time - time()) / 60);
+ $minutes = $time_left == 1 ? $lang->get('etc_unit_minute') : $lang->get('etc_unit_minutes');
+ echo '<td class="row2" style="text-align: center;">' . "$time_left $minutes" . '</td>';
+ }
+ else
+ {
+ echo '<td class="row2" style="text-align: center;">–</td>';
+ }
+ // action
+ $btn_text = $row['fail_count'] >= getConfig('lockout_threshold', 5) ? $lang->get('acphome_btn_lockout_unblock') : $lang->get('acphome_btn_lockout_clear');
+ echo '<td class="row1" style="text-align: center;"><a href="#" onclick="ajaxPage(\'' . $paths->nslist['Admin'] . 'UserManager\', \'clear_lockout=' . htmlspecialchars($row['ipaddr']) . '\'); return false;">' . $btn_text . '</a></td>';
+ echo '</tr>';
+ }
+ echo '</table>';
+ echo '</div>';
+ if ( $homewrap )
+ echo '</div>';
+ }
+
+ $db->free_result();
+}
--- a/themes/admin/css/default.css Thu Dec 17 04:29:55 2009 -0500
+++ b/themes/admin/css/default.css Thu Dec 17 04:31:55 2009 -0500
@@ -224,6 +224,10 @@
margin: 0 0 10px 0;
}
+div.acphome-box div.tblholder table {
+ color: black;
+}
+
div.acphome-box.warning {
background-color: #900000;
color: #fff;
@@ -249,6 +253,14 @@
color: #fff !important;
}
+div.acphome-box div.tblholder table a {
+ color: #294F75 !important;
+}
+
+div.acphome-box div.tblholder table a:hover {
+ color: #597FA5 !important;
+}
+
th.systemversion a {
font-weight: normal;
color: #fff !important;