Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #209 from michaelbrooks/login-tracking

Added tracking of failed logins
  • Loading branch information...
commit cd282b023af96eaaaeec237597dd8cdda38edbef 2 parents 1a64d1e + e3c6e6c
@benedmunds benedmunds authored
View
16 config/ion_auth.php
@@ -29,7 +29,7 @@
$config['tables']['groups'] = 'groups';
$config['tables']['users'] = 'users';
$config['tables']['users_groups'] = 'users_groups';
-
+ $config['tables']['login_attempts'] = 'login_attempts';
/**
* Hash Method (sha1 or bcrypt)
@@ -202,6 +202,20 @@
$config['forgot_password_expiration'] = 0;
/**
+ * Track the number of failed login attempts for each user or ip.
+ **/
+ $config['track_login_attempts'] = false;
+
+ /**
+ * Set the maximum number of failed login attempts.
+ * This maximum is not enforced by the library, but is
+ * used by $this->ion_auth->is_max_login_attempts_exceeded().
+ * The controller should check this function and act
+ * appropriately. If this variable set to 0, there is no maximum.
+ **/
+ $config['maximum_login_attempts'] = 3;
+
+ /**
* Message Start Delimiter
**/
$config['message_start_delimiter'] = '<p>';
View
74 models/ion_auth_model.php
@@ -851,7 +851,9 @@ public function login($identity, $password, $remember=FALSE)
);
$this->update_last_login($user->id);
-
+
+ $this->clear_login_attempts($identity);
+
$this->session->set_userdata($session_data);
if ($remember && $this->config->item('remember_users', 'ion_auth'))
@@ -866,12 +868,82 @@ public function login($identity, $password, $remember=FALSE)
}
}
+ $this->increase_login_attempts($identity);
+
$this->trigger_events('post_login_unsuccessful');
$this->set_error('login_unsuccessful');
return FALSE;
}
+ /**
+ * is_max_login_attempts_exceeded
+ * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
+ *
+ * @param string $identity
+ * @return boolean
+ **/
+ public function is_max_login_attempts_exceeded($identity) {
+ if ($this->config->item('track_login_attempts', 'ion_auth')) {
+ $max_attempts = $this->config->item('maximum_login_attempts', 'ion_auth');
+ if ($max_attempts > 0) {
+ $attempts = $this->get_attempts_num($identity);
+ return $attempts >= $max_attempts;
+ }
+ }
+ return FALSE;
+ }
+
+ /**
+ * Get number of attempts to login occured from given IP-address or identity
+ * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
+ *
+ * @param string $identity
+ * @return int
+ */
+ function get_attempts_num($identity)
+ {
+ $ip_address = $this->input->ip_address();
+
+ $this->db->select('1', FALSE);
+ $this->db->where('ip_address', $ip_address);
+ if (strlen($identity) > 0) $this->db->or_where('login', $identity);
+
+ $qres = $this->db->get($this->tables['login_attempts']);
+ return $qres->num_rows();
+ }
+
+ /**
+ * increase_login_attempts
+ * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
+ *
+ * @param string $identity
+ **/
+ public function increase_login_attempts($identity) {
+ if ($this->config->item('track_login_attempts', 'ion_auth')) {
+ $ip_address = $this->input->ip_address();
+ $this->db->insert($this->tables['login_attempts'], array('ip_address' => $ip_address, 'login' => $identity, 'time' => time()));
+ }
+ }
+
+ /**
+ * clear_login_attempts
+ * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
+ *
+ * @param string $identity
+ **/
+ public function clear_login_attempts($identity, $expire_period = 86400) {
+ if ($this->config->item('track_login_attempts', 'ion_auth')) {
+ $ip_address = $this->input->ip_address();
+
+ $this->db->where(array('ip_address' => $ip_address, 'login' => $identity));
+ // Purge obsolete login attempts
+ $this->db->or_where('time <', time() - $expire_period);
+
+ $this->db->delete($this->tables['login_attempts']);
+ }
+ }
+
public function limit($limit)
{
$this->trigger_events('limit');
View
11 sql/ion_auth.mssql.sql
@@ -56,4 +56,13 @@ SET IDENTITY_INSERT users OFF;
SET IDENTITY_INSERT users_groups ON;
INSERT INTO users_groups (id, user_id, group_id) VALUES (1,1,1);
INSERT INTO users_groups (id, user_id, group_id) VALUES (2,1,2);
-SET IDENTITY_INSERT users_groups OFF;
+SET IDENTITY_INSERT users_groups OFF;
+
+CREATE TABLE login_attempts (
+ id int NOT NULL IDENTITY(1,1),
+ ip_address varchar(40) NOT NULL,
+ login varchar(100) NOT NULL,
+ time datetime,
+ PRIMARY KEY(id),
+ CONSTRAINT users_check_id CHECK(id >= 0)
+);
View
9 sql/ion_auth.postgre.sql
@@ -52,3 +52,12 @@ INSERT INTO users (ip_address, username, password, salt, email, activation_code,
INSERT INTO users_groups (user_id, group_id) VALUES
(1,1),
(1,2);
+
+CREATE TABLE "login_attempts" (
+ "id" SERIAL NOT NULL,
+ "ip_address" varchar(40) NOT NULL,
+ "login" varchar(100) NOT NULL,
+ "time" int,
+ PRIMARY KEY("id"),
+ CONSTRAINT "check_id" CHECK(id >= 0),
+);
View
15 sql/ion_auth.sql
@@ -73,3 +73,18 @@ CREATE TABLE `users_groups` (
INSERT INTO `users_groups` (`id`, `user_id`, `group_id`) VALUES
(1,1,1),
(2,1,2);
+
+
+DROP TABLE IF EXISTS `login_attempts`;
+
+#
+# Table structure for table 'login_attempts'
+#
+
+CREATE TABLE `login_attempts` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `ip_address` varchar(40) NOT NULL,
+ `login` varchar(100) NOT NULL,
+ `time` int(11) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`)
+);
View
99 userguide/index.html
@@ -59,6 +59,10 @@
<li><a href="#username_check">username_check()</a></li>
<li><a href="#email_check">email_check()</a></li>
<li><a href="#identity_check">identity_check()</a></li>
+ <li><a href="#is_max_login_attempts_exceeded">is_max_login_attempts_exceeded()</a></li>
+ <li><a href="#get_attempts_num">get_attempts_num()</a></li>
+ <li><a href="#increase_login_attempts">increase_login_attempts()</a></li>
+ <li><a href="#clear_login_attempts">clear_login_attempts()</a></li>
<li><a href="#user">user()</a></li>
<li><a href="#users">users()</a></li>
<li><a href="#group">group()</a></li>
@@ -142,6 +146,7 @@
<li>$config['tables']['groups']</li>
<li>$config['tables']['users']</li>
<li>$config['tables']['users_groups']</li>
+ <li>$config['tables']['login_attempts']</li>
<li>$config['site_title']</li>
<li>$config['admin_email']</li>
<li>$config['default_group']</li>
@@ -163,6 +168,8 @@
<li>$config['salt_length']</li>
<li>$config['store_salt'] </li>
<li>$config['forgot_password_expiration'] </li>
+ <li>$config['track_login_attempts'] </li>
+ <li>$config['maximum_login_attempts'] </li>
<li>$config['message_start_delimiter']</li>
<li>$config['message_end_delimiter']</li>
<li>$config['error_start_delimiter']</li>
@@ -181,6 +188,7 @@
<li>'tables['groups']' - The table name to use for the groups table. DEFAULT is 'groups'.</li>
<li>'tables['users']' - The table name to use for the users table. DEFAULT is 'users'.</li>
<li>'tables['users_groups']' - The table name to use for the users groups table. DEFAULT is 'users_groups'.</li>
+ <li>'tables['login_attempts']' - The table name to use for the login attempts table. DEFAULT is 'login_attempts'.</li>
<li>'site_title' - The title of your site, used for email.</li>
<li>'admin_email' - Your administrator email address. DEFAULT is 'admin@example.com'.</li>
<li>'default_group' - Name of the default user group. DEFAULT is 'members'.</li>
@@ -202,6 +210,10 @@
<li>'salt_length' - Length of the encryption salt. DEFAULT is '10'.</li>
<li>'store_salt' - TRUE or FALSE. Store the salt in a separate database column or not. This can be useful for integrating with existing apps. DEFAULT is 'false'.</li>
<li>'forgot_password_expiration' - Number of seconds before a forgot password request expires. If set to 0, requests will not expire. DEFAULT is 0.</li>
+ <li>'track_login_attempts' - Track the number of failed login attempts for each user or ip. DEFAULT is 'false'.</li>
+ <li>'maximum_login_attempts' - Set the maximum number of failed login attempts. This maximum is not enforced by the library, but is
+ used by $this->ion_auth->is_max_login_attempts_exceeded(). The controller should check this function and act appropriately.
+ If set to 0, there is no maximum. DEFAULT is 3.</li>
<li>'message_start_delimiter' - Starting delimiter for messages. DEFAULT is '&lt;p&gt;'.</li>
<li>'message_end_delimiter' - Ending delimiter for messages. DEFAULT is '&lt;/p&gt;'.</li>
<li>'error_start_delimiter' - Starting delimiter for errors. DEFAULT is '&lt;p&gt;'.</li>
@@ -571,6 +583,93 @@
}
</pre>
<br />
+
+ <a name="is_max_login_attempts_exceeded"></a>
+ <h2>is_max_login_attempts_exceeded()</h2>
+ <p>If login attempt tracking is enabled, checks to see if the number of failed login attempts for this identity or ip address has been exceeded.
+ The controller must call this method and take any necessary actions. Login attempt limits are not enforced in the library.</p>
+
+ <p><strong>Parameters</strong></p>
+ <ol>
+ <li>'Identity' - string REQUIRED.</li>
+ </ol>
+
+ <p><strong>Return</strong></p>
+ <ul>
+ <li>boolean. TRUE if maximum_login_attempts is exceeded FALSE if not or if login attempts not tracked.</li>
+ </ul>
+
+ <p><b>Usage</b></p>
+ <pre>
+ $identity = 'ben.edmunds@gmail.com';
+ if ($this->ion_auth->is_max_login_attempts_exceeded($identity))
+ {
+ $this->session->set_flashdata('message', 'You have too many login attempts');
+ redirect('welcome/index');
+ }
+ </pre>
+ <br />
+
+ <a name="get_attempts_num"></a>
+ <h2>get_attempts_num()</h2>
+ <p>Returns the number of failed login attempts for this identity or ip address.</p>
+
+ <p><strong>Parameters</strong></p>
+ <ol>
+ <li>'Identity' - string REQUIRED.</li>
+ </ol>
+
+ <p><strong>Return</strong></p>
+ <ul>
+ <li>int. The number of failed login attempts for this identity or ip address.</li>
+ </ul>
+
+ <p><b>Usage</b></p>
+ <pre>
+ $identity = 'ben.edmunds@gmail.com';
+ $num_attempts = $this->ion_auth->get_attempts_num($identity);
+ </pre>
+ <br />
+
+ <a name="increase_login_attempts"></a>
+ <h2>increase_login_attempts()</h2>
+ <p>If login attempt tracking is enabled, records another failed login attempt for this identity or ip address.
+ This method is automatically called during the login() method if the login failed.</p>
+
+ <p><strong>Parameters</strong></p>
+ <ol>
+ <li>'Identity' - string REQUIRED.</li>
+ </ol>
+
+ <p><b>Usage</b></p>
+ <pre>
+ $identity = 'ben.edmunds@gmail.com';
+ $password = '12345678';
+ if ($this->ion_auth->login($identity, $password) == FALSE) {
+ $this->ion_auth->increase_login_attempts($identity)
+ }
+ </pre>
+ <br />
+
+ <a name="clear_login_attempts"></a>
+ <h2>clear_login_attempts()</h2>
+ <p>Clears all failed login attempt records for this identity or this ip address.
+ This method is automatically called during the login() method if the login succeded.</p>
+
+ <p><strong>Parameters</strong></p>
+ <ol>
+ <li>'Identity' - string REQUIRED.</li>
+ </ol>
+
+ <p><b>Usage</b></p>
+ <pre>
+ $identity = 'ben.edmunds@gmail.com';
+ $password = '12345678';
+ if ($this->ion_auth->login($identity, $password) == TRUE) {
+ $this->ion_auth->clear_login_attempts($identity)
+ }
+ </pre>
+ <br />
<a name="user"></a>
<h2>user()</h2>
Please sign in to comment.
Something went wrong with that request. Please try again.