diff --git a/application/Language/en/Aauth.php b/application/Language/en/Aauth.php index 5fbe4a2..f6d2c13 100644 --- a/application/Language/en/Aauth.php +++ b/application/Language/en/Aauth.php @@ -48,7 +48,7 @@ return [ 'invalidUsername' => 'Invalid Username', 'invalidTOTPCode' => 'Invalid Authentication Code', 'invalidRecaptcha' => 'Sorry, the reCAPTCHA text entered was incorrect.', - 'invalidVercode' => 'Invalid Verification Code', + 'invalidVerficationCode' => 'Invalid Verification Code', 'requiredUsername' => 'Username required', 'requiredTOTPCode' => 'Authentication Code required', diff --git a/application/Libraries/Aauth.php b/application/Libraries/Aauth.php index f8e2aa7..78a0d42 100644 --- a/application/Libraries/Aauth.php +++ b/application/Libraries/Aauth.php @@ -218,10 +218,8 @@ class Aauth return false; } - else if ($userModel->delete($userId)) - { - return true; - } + + return $userModel->delete($userId); } /** @@ -310,7 +308,7 @@ class Aauth } else { - $this->error(lang('Aauth.invalidVercode')); + $this->error(lang('Aauth.invalidVerficationCode')); return false; } @@ -324,7 +322,9 @@ class Aauth * * Get user information * - * @param integer|boolean $userId User id to get or FALSE for current user + * @param integer|boolean $userId User id to get or FALSE for current user + * @param boolean $withVariables Whether to get user variables + * @param boolean $inclSystem Whether to get system user variables * * @return object|boolean User information or false if user not found */ @@ -383,7 +383,148 @@ class Aauth return $user->id; } - $this->error(lang('Aauth.notFoundUser')); + return false; + } + + /** + * Ban User + * + * @param integer $userId User id + * + * @return boolean + */ + public function banUser(int $userId) + { + $userModel = new UserModel(); + + if (! $userId) + { + $userId = $this->session->id; + } + + if (! $userModel->existsById($userId)) + { + $this->error(lang('Aauth.notFoundUser')); + + return false; + } + + return $userModel->updateBanned($userId, 1); + } + + /** + * Unban User + * + * @param integer $userId User id + * + * @return boolean + */ + public function unbanUser(int $userId) + { + $userModel = new UserModel(); + + if (! $userId) + { + $userId = $this->session->id; + } + + if (! $userModel->existsById($userId)) + { + $this->error(lang('Aauth.notFoundUser')); + + return false; + } + + return $userModel->updateBanned($userId, 0); + } + + /** + * Remind password + * + * Emails user with link to reset password + * + * @param string $email Email for account to remind + * + * @return boolean Remind fails/succeeds + */ + public function remindPassword(string $email) + { + $userModel = new UserModel(); + if ($user = $userModel->where('email', $email)->first()) + { + $userVariableModel = new UserVariableModel(); + $emailService = \Config\Services::email(); + $resetCode = sha1(strtotime('now')); + + $userVariableModel->save($user->id, 'verification_code', $resetCode, true); + + $messageData['code'] = $resetCode; + $messageData['link'] = site_url($this->config->linkResetPassword . '/' . $user->id . '/' . $resetCode); + + $emailService->initialize(isset($this->config->emailConfig) ? $this->config->emailConfig : []); + $emailService->setFrom($this->config->emailFrom, $this->config->emailFromName); + $emailService->setTo($user->email); + $emailService->setSubject(lang('Aauth.subjectReset')); + $emailService->setMessage(view('Aauth/Reset', $messageData)); + + return $emailService->send(); + } + + return false; + } + /** + * Reset password + * + * Generate new password and email it to the user + * + * @param string $resetCode Verification code for account + * + * @return boolean Password reset fails/succeeds + */ + public function resetPassword(string $resetCode) + { + $userVariableModel = new UserVariableModel(); + $variable = [ + 'data_key' => 'verification_code', + 'data_value' => $resetCode, + 'system' => 1, + ]; + + if ($userVariable = $userVariableModel->where($variable)->first()) + { + helper('text'); + $userModel = new UserModel(); + $password = random_string('alnum', $this->config->passwordMin); + + if ($user = $userModel->find($userVariable['user_id'])) + { + $emailService = \Config\Services::email(); + + $data['id'] = $user['id']; + $data['password'] = $password; + + $userModel->update($user['id'], $data); + $userVariableModel->delete($user['id'], 'verification_code', true); + + if ($this->config->totpEnabled && $this->config->totpResetPassword) + { + $userVariableModel->delete($user['id'], 'totp_secret', true); + } + + $messageData['password'] = $password; + + $emailService->initialize(isset($this->config->emailConfig) ? $this->config->emailConfig : []); + $emailService->setFrom($this->config->emailFrom, $this->config->emailFromName); + $emailService->setTo($user['email']); + $emailService->setSubject(lang('Aauth.subjectResetSuccess')); + $emailService->setMessage(view('Aauth/ResetSuccess', $messageData)); + + return true; + } + } + + $this->error(lang('Aauth.invalidVerficationCode')); + return false; } @@ -415,6 +556,7 @@ class Aauth if ($this->config->loginProtection && ! $loginAttemptModel->save()) { $this->error(lang('Aauth.loginAttemptsExceeded')); + return false; } @@ -433,12 +575,14 @@ class Aauth if (! $identifier || strlen($password) < $this->config->passwordMin || strlen($password) > $this->config->passwordMax) { $this->error(lang('Aauth.loginFailedName')); + return false; } if ($user = $userModel->where('username', $identifier)->first()) { $this->error(lang('Aauth.notFoundUser')); + return false; } } @@ -449,12 +593,14 @@ class Aauth if (! $validation->check($identifier, 'valid_email') || strlen($password) < $this->config->passwordMin || strlen($password) > $this->config->passwordMax) { $this->error(lang('Aauth.loginFailedEmail')); + return false; } if (! $user = $userModel->where('email', $identifier)->first()) { $this->error(lang('Aauth.notFoundUser')); + return false; } } diff --git a/application/Models/Aauth/UserModel.php b/application/Models/Aauth/UserModel.php index ec6cc0f..935bb0e 100644 --- a/application/Models/Aauth/UserModel.php +++ b/application/Models/Aauth/UserModel.php @@ -122,7 +122,7 @@ class UserModel extends Model * * @param integer $userId User id * - * @return void + * @return boolean */ public function updateLastLogin(int $userId) { @@ -130,7 +130,8 @@ class UserModel extends Model $data['last_login'] = $this->setDate(); $data['last_activity'] = $this->setDate(); - $builder->update($data, [$this->primaryKey => $userId]); + + return $builder->update($data, [$this->primaryKey => $userId]); } /** @@ -138,14 +139,32 @@ class UserModel extends Model * * @param integer $userId User id * - * @return void + * @return boolean */ public function updateLastActivity(int $userId) { $builder = $this->builder(); $data['last_activity'] = $this->setDate(); - $builder->update($data, [$this->primaryKey => $userId]); + + return $builder->update($data, [$this->primaryKey => $userId]); + } + + /** + * Update Banned by User ID + * + * @param integer $userId User id + * @param boolean $banned Whether true to ban + * + * @return boolean + */ + public function updateBanned(int $userId, bool $banned = false) + { + $builder = $this->builder(); + + $data['banned'] = ($banned ? 1 : 0); + + return $builder->update($data, [$this->primaryKey => $userId]); } /** @@ -166,7 +185,7 @@ class UserModel extends Model $builder->select('banned'); $builder->where($this->primaryKey, $userId); - // $builder->where('banned', 1); + $builder->where('banned', 1); if ($user = $builder->get()->getFirstRow()) { diff --git a/application/Models/Aauth/UserVariableModel.php b/application/Models/Aauth/UserVariableModel.php index 1e48c59..2b010a0 100644 --- a/application/Models/Aauth/UserVariableModel.php +++ b/application/Models/Aauth/UserVariableModel.php @@ -60,6 +60,22 @@ class UserVariableModel */ protected $DBGroup; + /** + * The format that the results should be returned as. + * Will be overridden if the as* methods are used. + * + * @var string + */ + protected $returnType = 'array'; + + /** + * Used by asArray and asObject to provide + * temporary overrides of model default. + * + * @var string + */ + protected $tempReturnType; + /** * Aauth Config object * @@ -74,9 +90,10 @@ class UserVariableModel */ public function __construct(ConnectionInterface &$db = null) { - $this->config = new AauthConfig(); - $this->DBGroup = $this->config->dbProfile; - $this->table = $this->config->dbTableUserVariables; + $this->config = new AauthConfig(); + $this->DBGroup = $this->config->dbProfile; + $this->table = $this->config->dbTableUserVariables; + $this->tempReturnType = $this->returnType; if ($db instanceof ConnectionInterface) { @@ -108,7 +125,7 @@ class UserVariableModel $builder->where('system', ($system ? 1 : 0)); - if ($row = $builder->get()->getFirstRow('array')) + if ($row = $builder->get()->getFirstRow($this->tempReturnType)) { return $row['data_value']; } @@ -130,7 +147,9 @@ class UserVariableModel $builder->where('user_id', $userId); $builder->where('system', ($system ? 1 : 0)); - return $builder->get()->getResult(); + $this->tempReturnType = $this->returnType; + + return $builder->get()->getResult($this->tempReturnType); } /** @@ -218,7 +237,7 @@ class UserVariableModel * * @return BaseBuilder */ - public function delete(int $userId, string $dataKey, bool $system) + public function delete(int $userId, string $dataKey, bool $system = null) { $builder = $this->builder(); $builder->where('user_id', $userId); @@ -228,6 +247,57 @@ class UserVariableModel return $builder->delete(); } + //-------------------------------------------------------------------- + // Utility + //-------------------------------------------------------------------- + + /** + * Sets the return type of the results to be as an associative array. + * + * @return Model + */ + public function asArray() + { + $this->tempReturnType = 'array'; + + return $this; + } + + /** + * Sets the return type to be of the specified type of object. + * Defaults to a simple object, but can be any class that has + * class vars with the same name as the table columns, or at least + * allows them to be created. + * + * @param string $class Class + * + * @return Model + */ + public function asObject(string $class = 'object') + { + $this->tempReturnType = $class; + + return $this; + } + + /** + * Returns the first row of the result set. Will take any previous + * Query Builder calls into account when determing the result set. + * + * @return array|object|null + */ + public function first() + { + $builder = $this->builder(); + + $row = $builder->limit(1, 0)->get(); + $row = $row->getFirstRow($this->tempReturnType); + + $this->tempReturnType = $this->returnType; + + return $row; + } + /** * Provides a shared instance of the Query Builder. * @@ -253,4 +323,41 @@ class UserVariableModel return $this->builder; } + + /** + * Provides direct access to method in the builder (if available) + * and the database connection. + * + * @param string $name Name + * @param array $params Params + * + * @return Model|null + */ + public function __call(string $name, array $params) + { + $result = null; + + if (method_exists($this->db, $name)) + { + $result = $this->db->$name(...$params); + } + elseif (method_exists($builder = $this->builder(), $name)) + { + $result = $builder->$name(...$params); + } + + // Don't return the builder object unless specifically requested + //, since that will interrupt the usability flow + // and break intermingling of model and builder methods. + if ($name !== 'builder' && empty($result)) + { + return $result; + } + if ($name !== 'builder' && ! $result instanceof BaseBuilder) + { + return $result; + } + + return $this; + } } diff --git a/application/Views/Aauth/Reset.php b/application/Views/Aauth/Reset.php new file mode 100644 index 0000000..405971e --- /dev/null +++ b/application/Views/Aauth/Reset.php @@ -0,0 +1 @@ + $code, 'link' => $link])?> diff --git a/application/Views/Aauth/ResetSuccess.php b/application/Views/Aauth/ResetSuccess.php new file mode 100644 index 0000000..f426cfa --- /dev/null +++ b/application/Views/Aauth/ResetSuccess.php @@ -0,0 +1 @@ + $password])?> diff --git a/application/Views/Aauth/Verification.php b/application/Views/Aauth/Verification.php new file mode 100644 index 0000000..9bda21f --- /dev/null +++ b/application/Views/Aauth/Verification.php @@ -0,0 +1 @@ + $code, 'link' => $link])?>