From f342c80e523bea6be31a2faa0e2e92ff09c1ef2c Mon Sep 17 00:00:00 2001 From: int2001 Date: Mon, 13 Jan 2025 08:54:37 +0000 Subject: [PATCH 1/8] Added new col login_attempts to user-table --- application/config/migration.php | 2 +- application/migrations/234_login_attempts.php | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 application/migrations/234_login_attempts.php diff --git a/application/config/migration.php b/application/config/migration.php index 5108d575b..bc1360558 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE; | */ -$config['migration_version'] = 233; +$config['migration_version'] = 234; /* |-------------------------------------------------------------------------- diff --git a/application/migrations/234_login_attempts.php b/application/migrations/234_login_attempts.php new file mode 100644 index 000000000..d465e3ab1 --- /dev/null +++ b/application/migrations/234_login_attempts.php @@ -0,0 +1,29 @@ +db->field_exists('login_attempts', 'users')) { + $fields = array( + 'login_attempts integer DEFAULT 0' + ); + + $this->dbforge->add_column('users', $fields); + } + } + + public function down() + { + if ($this->db->field_exists('login_attempts', 'users')) { + $this->dbforge->drop_column('users', 'login_attempts'); + } + } +} From f6003416491f5a227ded9a5bbe0801fbbd3bb0fa Mon Sep 17 00:00:00 2001 From: int2001 Date: Mon, 13 Jan 2025 09:13:15 +0000 Subject: [PATCH 2/8] Lock User after 3 failed attempts --- application/controllers/User.php | 3 +++ application/models/User_model.php | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/application/controllers/User.php b/application/controllers/User.php index b56d97c25..e6fbbdd58 100644 --- a/application/controllers/User.php +++ b/application/controllers/User.php @@ -1023,6 +1023,9 @@ class User extends CI_Controller { } else if ($login_attempt === 2) { $this->session->set_flashdata('warning', __("You can't login to a clubstation directly. Use your personal account instead.")); redirect('user/login'); + } else if ($login_attempt === 3) { + $this->session->set_flashdata('warning', __("Your account is locked, due to too many failed login-attempts. Please reset your Password.")); + redirect('user/login'); } else { if(ENVIRONMENT == 'maintenance') { $this->session->set_flashdata('notice', __("Sorry. This instance is currently in maintenance mode. If this message appears unexpectedly or keeps showing up, please contact an administrator. Only administrators are currently allowed to log in.")); diff --git a/application/models/User_model.php b/application/models/User_model.php index a18e5632f..d995a98b8 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -609,7 +609,14 @@ class User_Model extends CI_Model { return 2; } + if ($u->row()->login_attempts >= 3) { + $uid = $u->row()->user_id; + log_message('debug', "User ID: [$uid] Login rejected because of too many failed login attempts."); + return 3; + } + if($this->_auth($password, $u->row()->user_password)) { + $this->db->query("UPDATE users SET login_attempts = 0 WHERE user_id = ?", [$u->row()->user_id]); // Reset failurecount if (ENVIRONMENT != "maintenance") { return 1; } else { @@ -619,6 +626,8 @@ class User_Model extends CI_Model { return 1; } } + } else { // Update failurecount + $this->db->query("UPDATE users SET login_attempts = login_attempts+1 WHERE user_id = ?", [$u->row()->user_id]); } } return 0; From bdf7f29094d2b1c055428f6446140a28897f57be Mon Sep 17 00:00:00 2001 From: int2001 Date: Mon, 13 Jan 2025 09:26:17 +0000 Subject: [PATCH 3/8] Reset login_attempts on PW-Reset and new PW --- application/models/User_model.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/models/User_model.php b/application/models/User_model.php index d995a98b8..60c3809b1 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -361,6 +361,7 @@ class User_Model extends CI_Model { if($data['user_password'] == EPASSWORDINVALID) { return EPASSWORDINVALID; } + $data['login_attempts'] = 0; } } } @@ -755,7 +756,8 @@ class User_Model extends CI_Model { $data = array( 'user_password' => $this->_hash($password), 'reset_password_code' => NULL, - 'reset_password_date' => NULL + 'reset_password_date' => NULL, + 'login_attempts' => 0 ); $this->db->where('reset_password_code', $reset_code); From 2328dfad29d466df11f7e86f281cfb8ef94e9de2 Mon Sep 17 00:00:00 2001 From: int2001 Date: Mon, 13 Jan 2025 09:30:51 +0000 Subject: [PATCH 4/8] Set threshold to >3 instead of >=3 --- application/models/User_model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/models/User_model.php b/application/models/User_model.php index 60c3809b1..6c3adf85c 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -610,7 +610,7 @@ class User_Model extends CI_Model { return 2; } - if ($u->row()->login_attempts >= 3) { + if ($u->row()->login_attempts > 3) { $uid = $u->row()->user_id; log_message('debug', "User ID: [$uid] Login rejected because of too many failed login attempts."); return 3; From 62dea3fc2ee86bbc82df861fe679490fcbe3769f Mon Sep 17 00:00:00 2001 From: int2001 Date: Mon, 13 Jan 2025 15:23:14 +0000 Subject: [PATCH 5/8] Make amount of failed logins configurable (defaults to 3) --- application/config/config.sample.php | 8 ++++++++ application/models/User_model.php | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/application/config/config.sample.php b/application/config/config.sample.php index 6c871f3d0..ed62e8ca2 100644 --- a/application/config/config.sample.php +++ b/application/config/config.sample.php @@ -769,3 +769,11 @@ $config['disable_version_check'] = false; */ $config['enable_eqsl_massdownload'] = false; + +/* +|-------------------------------------------------------------------------- +| Lock Account after n failed login-attempts +|-------------------------------------------------------------------------- + */ + +$config['max_login_attempts'] = 3; diff --git a/application/models/User_model.php b/application/models/User_model.php index 6c3adf85c..597043d29 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -610,7 +610,12 @@ class User_Model extends CI_Model { return 2; } - if ($u->row()->login_attempts > 3) { + if ($this->config->item('max_login_attempts')) { + $maxattempts = $this->config->item('max_login_attempts'); + } else { + $maxattempts = 3; + } + if ($u->row()->login_attempts > $maxattempts) { $uid = $u->row()->user_id; log_message('debug', "User ID: [$uid] Login rejected because of too many failed login attempts."); return 3; From c4e7d3dd856cb92d1f6bd65ce8204bddb72b1a73 Mon Sep 17 00:00:00 2001 From: int2001 Date: Mon, 13 Jan 2025 15:24:54 +0000 Subject: [PATCH 6/8] Also for installation-config --- install/config/config.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/install/config/config.php b/install/config/config.php index ec0d71cc2..e1191c80f 100644 --- a/install/config/config.php +++ b/install/config/config.php @@ -769,3 +769,11 @@ $config['disable_version_check'] = false; */ $config['enable_eqsl_massdownload'] = false; + +/* +|-------------------------------------------------------------------------- +| Lock Account after n failed login-attempts +|-------------------------------------------------------------------------- + */ + +$config['max_login_attempts'] = 3; From eed874f3ebcde0a5a2c4fbe8a2c07ec660167958 Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Sun, 19 Jan 2025 09:44:54 +0100 Subject: [PATCH 7/8] ability for an admin to unlock a user from the users table by using the actions modal --- application/controllers/User.php | 31 ++++++++++++++++ application/models/User_model.php | 6 ++++ application/views/user/index.php | 24 +++++++------ .../views/user/modals/more_actions_modal.php | 35 +++++++++++++++++++ 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/application/controllers/User.php b/application/controllers/User.php index e6fbbdd58..724be3992 100644 --- a/application/controllers/User.php +++ b/application/controllers/User.php @@ -25,6 +25,12 @@ class User extends CI_Controller { $data['disable_impersonate'] = false; } + if ($this->config->item('max_login_attempts')) { + $data['maxattempts'] = $this->config->item('max_login_attempts'); + } else { + $data['maxattempts'] = 3; + } + // Get Date format if($this->session->userdata('user_date_format')) { // If Logged in and session exists @@ -63,6 +69,12 @@ class User extends CI_Controller { $custom_date_format = $this->config->item('qso_date_format'); } + if ($this->config->item('max_login_attempts')) { + $maxattempts = $this->config->item('max_login_attempts'); + } else { + $maxattempts = 3; + } + if ($this->user_model->exists_by_id($data['user_id']) && $modal != '') { $user = $this->user_model->get_by_id($data['user_id'])->row(); $gettext = new Gettext; @@ -74,6 +86,7 @@ class User extends CI_Controller { $data['user_lastname'] = $user->user_lastname; $data['user_language'] = $gettext->find_by('folder', $user->user_language)['name_en']; $data['is_clubstation'] = $user->clubstation == 1 ? true : false; + $data['is_locked'] = $user->login_attempts > $maxattempts ? true : false; $data['last_seen'] = $user->last_seen; $data['custom_date_format'] = $custom_date_format; $data['has_flossie'] = ($this->config->item('encryption_key') == 'flossie1234555541') ? true : false; @@ -85,6 +98,24 @@ class User extends CI_Controller { } } + public function unlock($uid) { + $this->load->model('user_model'); + if(!$this->user_model->authorize(99)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + + if ($this->user_model->exists_by_id($uid)) { + if ($this->user_model->unlock($uid)) { + $this->session->set_flashdata('success', __("User unlocked!")); + redirect('user'); + } else { + $this->session->set_flashdata('error', __("Failed to unlock user!")); + redirect('user'); + } + } else { + $this->session->set_flashdata('error', __("User not found!")); + redirect('dashboard'); + } + } + public function convert() { $this->load->model('user_model'); if(!$this->user_model->authorize(99)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } diff --git a/application/models/User_model.php b/application/models/User_model.php index 597043d29..23aa6df9b 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -672,6 +672,12 @@ class User_Model extends CI_Model { } } + // FUNCTION: bool unlock($user_id) + // Unlocks a user account after it was locked doe too many failed login attempts + function unlock($user_id) { + return $this->db->query("UPDATE users SET login_attempts = 0 WHERE user_id = ?", [$user_id]); + } + // FUNCTION: object users() // Returns a list of users with additional counts function users($club = '') { diff --git a/application/views/user/index.php b/application/views/user/index.php index cb56c254d..8d0e83403 100644 --- a/application/views/user/index.php +++ b/application/views/user/index.php @@ -70,17 +70,21 @@ } ?> - : stationcount; ?> -
- : logbookcount; ?> - qsocount > 0) { ?> -
lastqso; ?>"> - qsocount; ?> -
+ login_attempts > $maxattempts) { ?> + "> - "> - qsocount; ?> - + : stationcount; ?> +
+ : logbookcount; ?> + qsocount > 0) { ?> +
lastqso; ?>"> + qsocount; ?> +
+ + "> + qsocount; ?> + + diff --git a/application/views/user/modals/more_actions_modal.php b/application/views/user/modals/more_actions_modal.php index 1c79fa173..da5c44181 100644 --- a/application/views/user/modals/more_actions_modal.php +++ b/application/views/user/modals/more_actions_modal.php @@ -27,6 +27,12 @@
+ + + +
+ + +