From d5ba5af5301fe956b95d519b3d90f936b7292e04 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 19 Apr 2024 23:22:15 +0200 Subject: [PATCH] chg: [security] Disable resetting password when password change is disabled --- app/Controller/Component/ACLComponent.php | 18 ++++++++++++------ app/Controller/UsersController.php | 10 +--------- app/Model/User.php | 7 +++---- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 36dea32c7..e9985bd21 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -793,10 +793,10 @@ class ACLComponent extends Component 'downloadTerms' => array('*'), 'edit' => array('self_management_enabled'), 'email_otp' => array('otp_enabled'), - 'forgot' => array('*'), - 'otp' => array('otp_enabled'), - 'hotp' => array('otp_enabled'), - 'totp_new' => array('otp_enabled'), + 'forgot' => ['AND' => ['password_forgotten_enabled', 'password_change_enabled']], + 'otp' => ['otp_enabled'], + 'hotp' => ['otp_enabled'], + 'totp_new' => ['otp_enabled'], 'totp_delete' => ['AND' => ['perm_admin', 'otp_enabled']], 'searchGpgKey' => array('*'), 'fetchGpgKey' => array('*'), @@ -806,7 +806,7 @@ class ACLComponent extends Component 'logout' => array('*'), 'logout401' => array('*'), 'notificationSettings' => ['*'], - 'password_reset' => array('*'), + 'password_reset' => ['AND' => ['password_forgotten_enabled', 'password_change_enabled']], 'register' => array('*'), 'registrations' => array(), 'resetAllSyncAuthKeys' => array(), @@ -925,7 +925,13 @@ class ACLComponent extends Component }; $this->dynamicChecks['otp_enabled'] = function (array $user) { if (Configure::read('Security.otp_disabled')) { - throw new ForbiddenException('OTP is disabled on this instance.'); + throw new ForbiddenException('OTP has been disabled on this instance.'); + } + return true; + }; + $this->dynamicChecks['password_forgotten_enabled'] = function (array $user) { + if (empty(Configure::read('Security.allow_password_forgotten'))) { + throw new ForbiddenException('Password reset has been disabled on this instance.'); } return true; }; diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 9a1b264b5..cb469e3f7 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -3187,10 +3187,6 @@ class UsersController extends AppController public function forgot() { - if (empty(Configure::read('Security.allow_password_forgotten'))) { - $this->Flash->error(__('This feature is disabled.')); - $this->redirect('/'); - } if (!empty($this->Auth->user()) && !$this->_isRest()) { $this->Flash->info(__('You are already logged in, no need to ask for a password reset. Log out first.')); $this->redirect('/'); @@ -3216,10 +3212,6 @@ class UsersController extends AppController public function password_reset($token) { - if (empty(Configure::read('Security.allow_password_forgotten'))) { - $this->Flash->error(__('This feature is disabled.')); - $this->redirect('/'); - } $this->loadModel('Server'); $this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']); $this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']); @@ -3236,7 +3228,7 @@ class UsersController extends AppController $this->redirect('/'); } } - if ($this->request->is('post') || $this->request->is('put')) { + if ($this->request->is(['post', 'put'])) { $abortPost = false; return $this->__pw_change(['User' => $user], 'password_reset', $abortPost, $token, true); } diff --git a/app/Model/User.php b/app/Model/User.php index b0d1d0358..dd4746561 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -2124,11 +2124,11 @@ class User extends AppModel return true; } else { - return $this->forgot($email); + return $this->forgot($email, $ip); } } - public function forgot($email, $ip, $jobId = null) + public function forgot($email, $ip) { $user = $this->find('first', [ 'recursive' => -1, @@ -2140,9 +2140,8 @@ class User extends AppModel if (empty($user)) { return false; } - $redis = $this->setupRedis(); $token = RandomTool::random_str(true, 40, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); - $redis->set('misp:forgot:' . $token, $user['User']['id'], ['nx', 'ex' => 600]); + RedisTool::init()->set('misp:forgot:' . $token, $user['User']['id'], ['nx', 'ex' => 600]); $baseurl = Configure::check('MISP.external_baseurl') ? Configure::read('MISP.external_baseurl') : Configure::read('MISP.baseurl'); $body = __( "Dear MISP user,\n\nyou have requested a password reset on the MISP instance at %s. Click the link below to change your password.\n\n%s\n\nThe link above is only valid for 10 minutes, feel free to request a new one if it has expired.\n\nIf you haven't requested a password reset, reach out to your admin team and let them know that someone has attempted it in your stead.\n\nMake sure you keep the contents of this e-mail confidential, do NOT ever forward it as it contains a reset token that is equivalent of a password if acted upon. The IP used to trigger the request was: %s\n\nBest regards,\nYour MISP admin team",