From 7037112e5979d93ad9037330b83045c8668f2893 Mon Sep 17 00:00:00 2001 From: REJack Date: Mon, 4 Mar 2019 07:59:46 +0100 Subject: [PATCH] finalize Aauth - updated Copyright PHPDocs - updated Libraries Aauth, Aauth/CAPTCHA & Aauth/TOTP - updated LoginAttemptModel & UserSessionModel - updated Tests LoginAttemptModelTest, UserSessionModelTest, CAPTCHATest, LoginTest & TOTPTest --- app/Config/Aauth.php | 1 + app/Helpers/aauth_helper.php | 1 + app/Libraries/Aauth.php | 107 ++++++++++-------- app/Libraries/Aauth/CAPTCHA.php | 12 +- app/Libraries/Aauth/TOTP.php | 34 +++--- app/Models/Aauth/GroupModel.php | 1 + app/Models/Aauth/GroupToGroupModel.php | 1 + app/Models/Aauth/GroupToUserModel.php | 1 + app/Models/Aauth/GroupVariableModel.php | 1 + app/Models/Aauth/LoginAttemptModel.php | 42 ++++--- app/Models/Aauth/LoginTokenModel.php | 1 + app/Models/Aauth/PermModel.php | 1 + app/Models/Aauth/PermToGroupModel.php | 1 + app/Models/Aauth/PermToUserModel.php | 1 + app/Models/Aauth/UserModel.php | 1 + app/Models/Aauth/UserSessionModel.php | 48 ++++++++ app/Models/Aauth/UserVariableModel.php | 1 + .../Aauth/Database/LoginAttemptModelTest.php | 38 +++++++ tests/Aauth/Database/UserSessionModelTest.php | 49 ++++++-- tests/Aauth/Libraries/Aauth/CAPTCHATest.php | 13 ++- tests/Aauth/Libraries/Aauth/LoginTest.php | 19 +++- tests/Aauth/Libraries/Aauth/TOTPTest.php | 74 +++++++++--- 22 files changed, 342 insertions(+), 106 deletions(-) diff --git a/app/Config/Aauth.php b/app/Config/Aauth.php index 26de86e..0e22f18 100644 --- a/app/Config/Aauth.php +++ b/app/Config/Aauth.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Helpers/aauth_helper.php b/app/Helpers/aauth_helper.php index adbb745..03257f3 100644 --- a/app/Helpers/aauth_helper.php +++ b/app/Helpers/aauth_helper.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Libraries/Aauth.php b/app/Libraries/Aauth.php index 6cd6c86..b6495f6 100644 --- a/app/Libraries/Aauth.php +++ b/app/Libraries/Aauth.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @version 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay @@ -232,9 +233,9 @@ class Aauth $response = $request->getPostGet('h-captcha-response'); } - if (! $this->verifyCaptchaResponse($response)) + if (! $this->verifyCaptchaResponse($response)['success']) { - $this->error('Aauth.invalidCaptcha'); + $this->error(lang('Aauth.invalidCaptcha')); return false; } @@ -310,7 +311,7 @@ class Aauth } else if ($this->config->totpOnIpChange) { - if ($request->getIPAddress() !== $lastIpAddress) + if ($request->getIPAddress() !== $user['last_ip_address']) { if (! empty($totpSecret) && ! $totpCode) { @@ -335,7 +336,7 @@ class Aauth } else if ($this->config->totpOnIpChange) { - if ($request->getIPAddress() !== $lastIpAddress) + if ($request->getIPAddress() !== $user['last_ip_address']) { $this->session->set('totp_required', true); } @@ -351,6 +352,7 @@ class Aauth { $loginTokenModel->deleteAll($user['id']); $userSessionModel = new UserSessionModel(); + foreach ($userSessionModel->findAll() as $userSessionRow) { $result = $matches = []; @@ -433,6 +435,7 @@ class Aauth { $this->error(lang('Aauth.loginFailedAll')); } + return false; } } @@ -1267,7 +1270,7 @@ class Aauth * * @param string $email Email for account to remind * - * @return boolean Remind fails/succeeds + * @return boolean * * @codeCoverageIgnore */ @@ -1350,7 +1353,7 @@ class Aauth * * @param string $resetCode Verification code for account * - * @return boolean Password reset fails/succeeds + * @return boolean * * @codeCoverageIgnore */ @@ -1634,7 +1637,7 @@ class Aauth * @param string $name New group name * @param string $definition New group definition * - * @return boolean Update success/failure + * @return boolean */ public function updateGroup($groupPar, string $name = null, string $definition = null) { @@ -1680,7 +1683,7 @@ class Aauth * * @param string|integer $groupPar Group id or name * - * @return boolean Delete success/failure + * @return boolean */ public function deleteGroup($groupPar) { @@ -1725,7 +1728,7 @@ class Aauth * @param integer $userId User id to add to group * @param integer|string $groupPar Group id or name to add user to * - * @return boolean Add success/failure + * @return boolean */ public function addMember($groupPar, int $userId) { @@ -1760,7 +1763,7 @@ class Aauth * @param integer $userId User id to remove from group * @param integer|string $groupPar Group id or name to remove user from * - * @return boolean Remove success/failure + * @return boolean */ public function removeMember($groupPar, int $userId) { @@ -1776,7 +1779,7 @@ class Aauth * * @param integer|string $userId User id * - * @return object Array of group_id's + * @return boolean|array */ public function getUserGroups($userId) { @@ -1797,7 +1800,7 @@ class Aauth * * @param integer|string $userId User id * - * @return object Array of perm_id's + * @return boolean|array */ public function getUserPerms($userId, $state = null) { @@ -1819,7 +1822,7 @@ class Aauth * @param integer|string $groupPar Group id * @param integer|string $subgroupPar Subgroup id or name to add to group * - * @return boolean Add success/failure + * @return boolean */ public function addSubgroup($groupPar, $subgroupPar) { @@ -1880,7 +1883,7 @@ class Aauth * @param integer|string $groupPar Group id or name to remove * @param integer|string $subgroupPar Sub-Group id or name to remove * - * @return boolean Remove success/failure + * @return boolean */ public function removeSubgroup($groupPar, $subgroupPar) { @@ -1896,7 +1899,7 @@ class Aauth * * @param integer|string $groupPar Group id or name to get * - * @return object Array of subgroup_id's + * @return boolean|array */ public function getSubgroups($groupPar) { @@ -1918,7 +1921,7 @@ class Aauth * @param integer|string $groupPar Group id or name to get * @param integer $state State (1 = allowed, 0 = denied) * - * @return object Array of subgroup_id's + * @return boolean|array */ public function getGroupPerms($groupPar, int $state = null) { @@ -1937,7 +1940,7 @@ class Aauth * * @param integer $userId User id to remove from all groups * - * @return boolean Remove success/failure + * @return boolean */ public function removeMemberFromAll(int $userId) { @@ -1949,7 +1952,7 @@ class Aauth /** * List all groups * - * @return object Array of groups + * @return array */ public function listGroups() { @@ -1966,7 +1969,7 @@ class Aauth * @param integer $limit Limit of users to be returned * @param string $orderBy Order by MYSQL string (e.g. 'name ASC', 'email DESC') * - * @return array Array of groups + * @return array */ public function listGroupsPaginated(int $limit = 10, string $orderBy = null) { @@ -1988,7 +1991,7 @@ class Aauth * * @param integer $groupId Group id to get * - * @return string Group name + * @return string */ public function getGroupName($groupId) { @@ -2007,7 +2010,7 @@ class Aauth * * @param integer|string $groupPar Group id or name to get * - * @return integer Group id + * @return integer */ public function getGroupId($groupPar) { @@ -2036,7 +2039,7 @@ class Aauth * * @param integer|string $groupPar Group id or name to get * - * @return integer Group id + * @return integer */ public function getGroup($groupPar) { @@ -2055,7 +2058,7 @@ class Aauth * * @param integer|null $userId User id to get or false for current user * - * @return integer Group id + * @return boolean|array */ public function listUserGroups(int $userId = null) { @@ -2089,7 +2092,7 @@ class Aauth * @param integer $limit Limit of users to be returned * @param string $orderBy Order by MYSQL string (e.g. 'name ASC', 'email DESC') * - * @return array Array of users + * @return boolean|array */ public function listUserGroupsPaginated(int $userId = null, int $limit = 10, string $orderBy = null) { @@ -2176,7 +2179,7 @@ class Aauth * @param string $key Variable Key * @param integer $groupId Group id * - * @return boolean|string false if var is not set, the value of var if set + * @return boolean|string */ public function getGroupVar(string $key, int $groupId) { @@ -2286,7 +2289,7 @@ class Aauth * @param string $name New permission name * @param string $definition Permission description * - * @return boolean Update success/failure + * @return boolean */ public function updatePerm($permPar, string $name = null, string $definition = null) { @@ -2334,7 +2337,7 @@ class Aauth * * @param integer|string $permPar Permission id or perm name * - * @return boolean Delete success/failure + * @return boolean */ public function deletePerm($permPar) { @@ -2370,9 +2373,13 @@ class Aauth } } - /*$userId User id to allow + /** + * Allow User * - * @return bool Allow success/failure + * @param integer|string $permPar Permission id or perm name + * @param integer $userId User id to allow + * + * @return boolean */ public function allowUser($permPar, int $userId) { @@ -2399,9 +2406,13 @@ class Aauth return $permToUserModel->save($permId, $userId, 1); } - /*$userId User id to deny + /** + * Deny User + * + * @param integer|string $permPar Permission id or perm name + * @param integer $userId User id to deny * - * @return bool Deny success/failure + * @return boolean */ public function denyUser($permPar, int $userId) { @@ -2436,7 +2447,7 @@ class Aauth * @param integer|string $permPar Permission id or perm name * @param integer|string $groupPar Group id or name to allow * - * @return boolean Allow success/failure + * @return boolean */ public function allowGroup($permPar, $groupPar) { @@ -2470,7 +2481,7 @@ class Aauth * @param integer|string $permPar Permission id or perm name * @param integer|string $groupPar Group id or name to deny * - * @return boolean Deny success/failure + * @return boolean */ public function denyGroup($permPar, $groupPar) { @@ -2498,9 +2509,10 @@ class Aauth /** * List Permissions + * * List all permissions * - * @return object Array of permissions + * @return array */ public function listPerms() { @@ -2565,9 +2577,11 @@ class Aauth /** * Get permission + * * Get permission from permisison name or id * - * @param integer|string $permPar Permission id or name to get + * @param integer|string $permPar Permission id or name to get + * * @return integer Permission id or NULL if perm does not exist */ public function getPerm($permPar) @@ -2587,7 +2601,7 @@ class Aauth * * @param integer|string $groupPar Group id or name to get * - * @return integer Group id + * @return boolean|array */ public function listGroupPerms($groupPar) { @@ -2615,10 +2629,15 @@ class Aauth * @param boolean $includeBanneds Include banned users * @param string $orderBy Order by MYSQL string (e.g. 'name ASC', 'email DESC') * - * @return array Array of users + * @return boolean|array */ public function listGroupPermsPaginated(int $groupId, int $limit = 10, string $orderBy = null) { + if (! $groupId = $this->getGroupId($groupId)) + { + return false; + } + $permModel = new PermModel(); $permModel->select('id, name, definition, state'); @@ -2641,19 +2660,16 @@ class Aauth * * @param integer|string $groupPar Group id or name to get * - * @return integer Group id + * @return boolean|array */ public function listUserPerms(int $userId = null) { - $userModel = new UserModel(); - $groupModel = new GroupModel(); - if (! $userId) { $userId = (int) @$this->session->user['id']; } - if (! $userModel->existsById($userId)) + if (! $this->getUser($userId)) { return false; } @@ -2677,19 +2693,16 @@ class Aauth * @param boolean $includeBanneds Include banned users * @param string $orderBy Order by MYSQL string (e.g. 'name ASC', 'email DESC') * - * @return array Array of users + * @return boolean|array */ public function listUserPermsPaginated(int $userId = null, int $limit = 10, string $orderBy = null) { - $userModel = new UserModel(); - $groupModel = new GroupModel(); - if (! $userId) { $userId = (int) @$this->session->user['id']; } - if (! $userModel->existsById($userId)) + if (! $this->getUser($userId)) { return false; } diff --git a/app/Libraries/Aauth/CAPTCHA.php b/app/Libraries/Aauth/CAPTCHA.php index 42e1572..b6e2fef 100644 --- a/app/Libraries/Aauth/CAPTCHA.php +++ b/app/Libraries/Aauth/CAPTCHA.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay @@ -77,7 +78,12 @@ class CAPTCHA extends \App\Libraries\Aauth $answer = json_decode($request, true); - if (trim($answer['success']) !== true) + if (ENVIRONMENT === 'testing' && $response === 'testing') + { + $answer = ['success' => true]; + } + + if ($answer['success'] !== true) { return [ 'success' => false, @@ -136,7 +142,7 @@ class CAPTCHA extends \App\Libraries\Aauth * @param string $url URL path to CAPTCHA server. * @param array $data Array of parameters to be sent. * - * @return array response + * @return string */ private function _submitGet($url, $data) { @@ -156,7 +162,7 @@ class CAPTCHA extends \App\Libraries\Aauth * @param string $url URL path to CAPTCHA server. * @param array $data Array of parameters to be sent. * - * @return array response + * @return string */ private function _submitPost($url, $data) { diff --git a/app/Libraries/Aauth/TOTP.php b/app/Libraries/Aauth/TOTP.php index 0670c12..131d0d5 100644 --- a/app/Libraries/Aauth/TOTP.php +++ b/app/Libraries/Aauth/TOTP.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay @@ -95,35 +96,32 @@ class TOTP extends \App\Libraries\Aauth * Verify user TOTP Code * * @param integer $totpCode TOTP Code - * @param string $userId User Id + * @param integer $userId User Id * * @return boolean */ - public function verifyUserTotpCode(int $totpCode, string $userId = null) + public function verifyUserTotpCode(int $totpCode, int $userId = null) { - if ($this->isTotpRequired()) + if (! $userId) { - if (! $userId) - { - $userId = (int) @$this->session->user['id']; - } - - $userVariableModel = new UserVariableModel(); + $userId = (int) @$this->session->user['id']; + } - if ($totpSecret = $userVariableModel->find($userId, 'totp_secret', true)) - { - $totp = OTPHP_TOTP::create($totpSecret); + $userVariableModel = new UserVariableModel(); - if (! $totp->verify($totpCode)) - { - return false; - } + if ($totpSecret = $userVariableModel->find($userId, 'totp_secret', true)) + { + $totp = OTPHP_TOTP::create($totpSecret); - unset($_SESSION['user']['totp_required']); + if ($totp->verify($totpCode)) + { + return true; } + + unset($_SESSION['user']['totp_required']); } - return true; + return false; } /** diff --git a/app/Models/Aauth/GroupModel.php b/app/Models/Aauth/GroupModel.php index 343056e..603eb9f 100644 --- a/app/Models/Aauth/GroupModel.php +++ b/app/Models/Aauth/GroupModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/GroupToGroupModel.php b/app/Models/Aauth/GroupToGroupModel.php index 9980622..95885bd 100644 --- a/app/Models/Aauth/GroupToGroupModel.php +++ b/app/Models/Aauth/GroupToGroupModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/GroupToUserModel.php b/app/Models/Aauth/GroupToUserModel.php index 031a4ac..4c09dad 100644 --- a/app/Models/Aauth/GroupToUserModel.php +++ b/app/Models/Aauth/GroupToUserModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/GroupVariableModel.php b/app/Models/Aauth/GroupVariableModel.php index 9362ef3..1242b79 100644 --- a/app/Models/Aauth/GroupVariableModel.php +++ b/app/Models/Aauth/GroupVariableModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/LoginAttemptModel.php b/app/Models/Aauth/LoginAttemptModel.php index 78c7201..44932c8 100644 --- a/app/Models/Aauth/LoginAttemptModel.php +++ b/app/Models/Aauth/LoginAttemptModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay @@ -77,11 +78,22 @@ class LoginAttemptModel * * @return void */ - public function __construct(ConnectionInterface &$db = null) + public function __construct(ConnectionInterface &$db = null, $config = null, $response = null) { - $this->config = new AauthConfig(); - $this->DBGroup = $this->config->dbProfile; - $this->table = $this->config->dbTableLoginAttempts; + if (is_null($config)) + { + $config = new AauthConfig(); + } + + if (is_null($response)) + { + $response = service('response'); + } + + $this->response = $response; + $this->config = $config; + $this->DBGroup = $this->config->dbProfile; + $this->table = $this->config->dbTableLoginAttempts; if ($db instanceof ConnectionInterface) { @@ -107,11 +119,11 @@ class LoginAttemptModel if ($this->config->loginAttemptCookie) { helper('cookie'); - $cookieName = $this->config->loginAttemptCookie === true ? 'logins' : $this->config->lologinAttemptCookie; + $cookieName = $this->config->loginAttemptCookie === true ? 'logins' : $this->config->loginAttemptCookie; - if ($cookie === get_cookie($cookieName)) + if ($cookie = $this->response->getCookie($cookieName)) { - return $cookie; + return empty($cookie['value']) ? 0 : $cookie['value']; } } else @@ -143,14 +155,16 @@ class LoginAttemptModel if ($this->config->loginAttemptCookie) { helper('cookie'); - $cookieName = $this->config->loginAttemptCookie === true ? 'logins' : $this->config->lologinAttemptCookie; + $cookieName = $this->config->loginAttemptCookie === true ? 'logins' : $this->config->loginAttemptCookie; $expire = strtotime($this->config->loginAttemptLimitTimePeriod) - strtotime('now'); - if ($cookie = get_cookie($cookieName)) + if ($cookie = $this->response->getCookie($cookieName)) { - set_cookie($cookieName, $cookie + 1, $expire); + $this->response->deleteCookie($cookieName); + (int) $cookie['value']++; + $this->response->setCookie($cookieName, $cookie['value'], $expire); - if ($cookie >= $this->config->loginAttemptLimit) + if ($cookie['value'] >= $this->config->loginAttemptLimit) { return false; } @@ -161,7 +175,7 @@ class LoginAttemptModel } else { - set_cookie($cookieName, 1, $expire); + $this->response->setCookie($cookieName, 1, $expire); return true; } @@ -219,8 +233,8 @@ class LoginAttemptModel if ($this->config->loginAttemptCookie) { helper('cookie'); - $cookieName = $this->config->loginAttemptCookie === true ? 'logins' : $this->config->lologinAttemptCookie; - delete_cookie($cookieName); + $cookieName = $this->config->loginAttemptCookie === true ? 'logins' : $this->config->loginAttemptCookie; + $this->response->deleteCookie($cookieName); } else { diff --git a/app/Models/Aauth/LoginTokenModel.php b/app/Models/Aauth/LoginTokenModel.php index aad1f34..7d45bb7 100644 --- a/app/Models/Aauth/LoginTokenModel.php +++ b/app/Models/Aauth/LoginTokenModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/PermModel.php b/app/Models/Aauth/PermModel.php index e1d17f0..aff1db1 100644 --- a/app/Models/Aauth/PermModel.php +++ b/app/Models/Aauth/PermModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/PermToGroupModel.php b/app/Models/Aauth/PermToGroupModel.php index 25d2b22..014cb08 100644 --- a/app/Models/Aauth/PermToGroupModel.php +++ b/app/Models/Aauth/PermToGroupModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/PermToUserModel.php b/app/Models/Aauth/PermToUserModel.php index dd8c082..f56a723 100644 --- a/app/Models/Aauth/PermToUserModel.php +++ b/app/Models/Aauth/PermToUserModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/UserModel.php b/app/Models/Aauth/UserModel.php index 405b1c4..5acfbe5 100644 --- a/app/Models/Aauth/UserModel.php +++ b/app/Models/Aauth/UserModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/app/Models/Aauth/UserSessionModel.php b/app/Models/Aauth/UserSessionModel.php index 9752f2e..e513ba1 100644 --- a/app/Models/Aauth/UserSessionModel.php +++ b/app/Models/Aauth/UserSessionModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay @@ -144,6 +145,53 @@ class UserSessionModel // Utility //-------------------------------------------------------------------- + /** + * Sets the return type of the results to be as an associative array. + * + * @return Model + */ + public function asArray() + { + $this->tempReturnType = $this->returnType = '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 = $this->returnType = $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. * diff --git a/app/Models/Aauth/UserVariableModel.php b/app/Models/Aauth/UserVariableModel.php index 51cba55..b34d784 100644 --- a/app/Models/Aauth/UserVariableModel.php +++ b/app/Models/Aauth/UserVariableModel.php @@ -8,6 +8,7 @@ * access management, public access etc.. * * @package CodeIgniter-Aauth + * @since 3.0.0 * @author Emre Akay * @author Raphael "REJack" Jackstadt * @copyright 2014-2019 Emre Akay diff --git a/tests/Aauth/Database/LoginAttemptModelTest.php b/tests/Aauth/Database/LoginAttemptModelTest.php index 464d965..b6e2cd6 100644 --- a/tests/Aauth/Database/LoginAttemptModelTest.php +++ b/tests/Aauth/Database/LoginAttemptModelTest.php @@ -1,6 +1,8 @@ response = new \CodeIgniter\HTTP\Response(new App()); + $this->model = new LoginAttemptModel($this->db); } @@ -53,6 +57,40 @@ class LoginAttemptModelTest extends CIDatabaseTestCase $this->assertEquals(0, $this->model->find()); } + public function testFindCookie() + { + $config = new \Config\Aauth(); + $config->loginAttemptCookie = true; + $this->model = new LoginAttemptModel($this->db, $config); + $loginAttempt = $this->model->find(); + $this->assertEquals(0, $loginAttempt); + } + + public function testSaveCookie() + { + $config = new \Config\Aauth(); + $config->loginAttemptCookie = true; + $this->model = new LoginAttemptModel($this->db, $config, $this->response); + + $this->assertTrue($this->model->save()); + $this->assertEquals(1, $this->model->find()); + + $this->assertTrue($this->model->save()); + } + + public function testDeleteCookie() + { + $config = new Aauth(); + $config->loginAttemptCookie = true; + $this->model = new LoginAttemptModel($this->db, $config, $this->response); + + $this->model->save(); + $this->assertEquals(1, $this->model->find()); + + $this->model->delete(); + $this->assertEquals(0, $this->model->find()); + } + public function testConfigDBGroup() { $this->model = new LoginAttemptModel(); diff --git a/tests/Aauth/Database/UserSessionModelTest.php b/tests/Aauth/Database/UserSessionModelTest.php index f7ec14a..06bc8c1 100644 --- a/tests/Aauth/Database/UserSessionModelTest.php +++ b/tests/Aauth/Database/UserSessionModelTest.php @@ -36,18 +36,53 @@ class UserSessionModelTest extends CIDatabaseTestCase $this->seeNumRecords(0, $this->config->dbTableUserSessions, []); } - public function testDBInsert() + public function testAsArrayFirst() { + $this->hasInDatabase($this->config->dbTableUserSessions, [ + 'id' => md5(time()), + 'ip_address' => '127.0.0.1', + 'timestamp' => time(), + 'data' => 'user|', + ]); + $userSession = $this->model->asArray()->findAll(); + $this->assertInternalType('array', $userSession[0]); + } + + public function testAsObjectFirst() + { + $this->hasInDatabase($this->config->dbTableUserSessions, [ + 'id' => md5(time()), + 'ip_address' => '127.0.0.1', + 'timestamp' => time(), + 'data' => 'user|', + ]); + $userSession = $this->model->asObject()->findAll(); + $this->assertInternalType('object', $userSession[0]); + } + + public function testConfigDBGroup() + { + $this->model = new UserSessionModel(); + $this->hasInDatabase($this->config->dbTableUserSessions, [ + 'id' => md5(time()), + 'ip_address' => '127.0.0.1', + 'timestamp' => time(), + 'data' => 'user|', + ]); + $userSession = $this->model->asObject()->where(['ip_address' => '127.0.0.1'])->first(); + $this->assertInternalType('object', $userSession); + } + + public function testDBCall() + { + $this->assertEquals(0, $this->model->insertID()); $this->seeNumRecords(0, $this->config->dbTableUserSessions, []); - $id = md5(time()); $this->model->insert([ - 'id' => $id, + 'id' => md5(time()), 'ip_address' => '127.0.0.1', 'timestamp' => time(), - 'data' => '', + 'data' => 'user|', ]); - $this->seeNumRecords(1, $this->config->dbTableUserSessions, []); - $this->assertEquals(1, $this->model->affectedRows()); - $this->assertEquals(1, $this->model->countAll()); + $this->assertCount(1, $this->model->asObject()->findAll()); } } diff --git a/tests/Aauth/Libraries/Aauth/CAPTCHATest.php b/tests/Aauth/Libraries/Aauth/CAPTCHATest.php index 2bdb47d..c13d43f 100644 --- a/tests/Aauth/Libraries/Aauth/CAPTCHATest.php +++ b/tests/Aauth/Libraries/Aauth/CAPTCHATest.php @@ -83,13 +83,16 @@ class CAPTCHATest extends CIDatabaseTestCase $this->library->login('admina@example.com', 'password123456'); $this->library->login('admina@example.com', 'password123456'); $this->library->login('admina@example.com', 'password123456'); + $_POST['g-recaptcha-response'] = '0123456789'; $this->library->login('admina@example.com', 'password123456'); $this->assertContains('https://www.google.com/recaptcha', $this->library->generateCaptchaHtml()); - $config->captchaType = 'hcaptcha'; - $this->library = new Aauth($config, true); - + $config->captchaType = 'hcaptcha'; + $this->library = new Aauth($config, true); + $_POST['h-recaptcha-response'] = '0123456789'; + $this->library->login('admina@example.com', 'password123456'); + $this->assertEquals(lang('Aauth.invalidCaptcha'), $this->library->getErrorsArray()[0]); $this->assertContains('https://hcaptcha.com/1', $this->library->generateCaptchaHtml()); } @@ -105,5 +108,9 @@ class CAPTCHATest extends CIDatabaseTestCase $config->captchaType = 'hcaptcha'; $this->library = new Aauth($config, true); $this->assertContains('invalid-input-response', $this->library->verifyCaptchaResponse('0123456789')['errorCodes']); + + $config->captchaType = 'hcaptcha'; + $this->library = new Aauth($config, true); + $this->assertTrue($this->library->verifyCaptchaResponse('testing')['success']); } } diff --git a/tests/Aauth/Libraries/Aauth/LoginTest.php b/tests/Aauth/Libraries/Aauth/LoginTest.php index 23b0cd5..c2c6816 100644 --- a/tests/Aauth/Libraries/Aauth/LoginTest.php +++ b/tests/Aauth/Libraries/Aauth/LoginTest.php @@ -86,8 +86,25 @@ class LoginTest extends CIDatabaseTestCase $this->seeInDatabase($config->dbTableLoginTokens, [ 'user_id' => 1, ]); + $this->assertTrue($this->response->hasCookie('remember')); + $this->hasInDatabase($config->dbTableUserSessions, [ + 'id' => md5(time()), + 'ip_address' => '127.0.0.1', + 'timestamp' => time(), + 'data' => '__ci_last_regenerate|i:' . time() . ';user|a:4:{s:2:"id";s:1:"1";s:8:"username";s:5:"admin";s:5:"email";s:17:"admin@example.com";s:8:"loggedIn";b:1;}', + 'data' => '__ci_last_regenerate|i:1551553466;user|a:4:{s:2:"id";s:1:"1";s:8:"username";s:5:"admin";s:5:"email";s:17:"admin@example.com";s:8:"loggedIn";b:1;}', + ]); + + $config->loginSingleMode = true; + $this->library = new Aauth($config, $session); + + $this->assertTrue($this->library->login('admin', 'password123456')); + + $config->loginSingleMode = false; + $this->library = new Aauth($config, $session); + $this->assertFalse($this->library->login('admin', 'passwor')); $this->assertEquals(lang('Aauth.loginFailedUsername'), $this->library->getErrorsArray()[0]); @@ -95,6 +112,7 @@ class LoginTest extends CIDatabaseTestCase $this->assertFalse($this->library->login('admin', 'password1234')); $this->assertEquals(lang('Aauth.loginFailedAll'), $this->library->getErrorsArray()[0]); + $config->loginSingleMode = false; $config->loginAccurateErrors = true; $this->library = new Aauth($config, $session); $this->library->clearErrors(); @@ -104,7 +122,6 @@ class LoginTest extends CIDatabaseTestCase $this->library->clearErrors(); $this->assertFalse($this->library->login('user99', 'password123456')); $this->assertEquals(lang('Aauth.notFoundUser'), $this->library->getErrorsArray()[0]); - // $config->loginUseUsername = false; $this->library = new Aauth(null, $session); $this->assertTrue($this->library->login('admin@example.com', 'password123456')); diff --git a/tests/Aauth/Libraries/Aauth/TOTPTest.php b/tests/Aauth/Libraries/Aauth/TOTPTest.php index ba423d0..c29380a 100644 --- a/tests/Aauth/Libraries/Aauth/TOTPTest.php +++ b/tests/Aauth/Libraries/Aauth/TOTPTest.php @@ -8,6 +8,7 @@ use Tests\Support\Session\MockSession; use CodeIgniter\Session\Handlers\FileHandler; use CodeIgniter\Test\CIDatabaseTestCase; use App\Libraries\Aauth; +use App\Models\Aauth\UserModel; use App\Models\Aauth\UserVariableModel; use OTPHP\TOTP; @@ -60,6 +61,65 @@ class TOTPTest extends CIDatabaseTestCase //-------------------------------------------------------------------- + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + + public function testLogin() + { + $config = new AauthConfig(); + $config->totpEnabled = true; + $session = $this->getInstance(); + $this->library = new Aauth($config, $session); + + $this->hasInDatabase($this->config->dbTableUserVariables, [ + 'user_id' => 1, + 'data_key' => 'totp_secret', + 'data_value' => 'JBSWY3DPEHPK3PXP', + 'system' => true, + ]); + + $this->assertTrue($this->library->login('admin@example.com', 'password123456')); + + $config->totpLogin = true; + $this->library = new Aauth($config, $session); + + $this->assertFalse($this->library->login('admin@example.com', 'password123456', null, '000001')); + $this->assertEquals(lang('Aauth.invalidTOTPCode'), $this->library->getErrorsArray()[0]); + $this->library = new Aauth($config, $session); + $this->assertFalse($this->library->login('admin@example.com', 'password123456', null)); + $this->assertEquals(lang('Aauth.requiredTOTPCode'), $this->library->getErrorsArray()[0]); + $this->library = new Aauth($config, $session); + + $totp = TOTP::create('JBSWY3DPEHPK3PXP'); + $totpCode = $totp->now(); + usleep(1000); + $this->assertTrue($this->library->login('admin@example.com', 'password123456', null, $totpCode)); + + $userModel = new UserModel(); + $userModel->protect(false)->update(1, ['last_ip_address' => '99.99.99.99']); + + $config->totpOnIpChange = true; + + $this->assertFalse($this->library->login('admin@example.com', 'password123456', null, '000001')); + $this->assertEquals(lang('Aauth.invalidTOTPCode'), $this->library->getErrorsArray()[0]); + $this->library = new Aauth($config, $session); + $this->assertFalse($this->library->login('admin@example.com', 'password123456', null)); + $this->assertEquals(lang('Aauth.requiredTOTPCode'), $this->library->getErrorsArray()[0]); + $this->library = new Aauth($config, $session); + + $this->library = new Aauth($config, $session); + $this->assertTrue($this->library->login('admin@example.com', 'password123456', null, $totpCode)); + + $userModel->protect(false)->update(1, ['last_ip_address' => '99.99.99.99']); + $config->totpOnIpChange = true; + $config->totpLogin = false; + $this->library = new Aauth($config, $session); + + $this->assertTrue($this->library->login('admin@example.com', 'password123456')); + } + /** * @runInSeparateProcess * @preserveGlobalState disabled @@ -124,16 +184,6 @@ class TOTPTest extends CIDatabaseTestCase $config->totpEnabled = true; $this->library = new Aauth($config, $session); - $this->assertTrue($this->library->verifyUserTotpCode('999000', 1)); - - $this->library = new Aauth($config, $session); - $session->set('user', [ - 'id' => 1, - 'loggedIn' => true, - ]); - - $this->assertTrue($this->library->verifyUserTotpCode('999000')); - $session = $this->getInstance(); $this->library = new Aauth($config, $session); $session->set('user', [ @@ -142,9 +192,6 @@ class TOTPTest extends CIDatabaseTestCase 'totp_required' => true, ]); - $this->assertTrue($this->library->verifyUserTotpCode('999000')); - $this->assertTrue($this->library->verifyUserTotpCode('999000', 1)); - $this->hasInDatabase($this->config->dbTableUserVariables, [ 'user_id' => 1, 'data_key' => 'totp_secret', @@ -157,6 +204,7 @@ class TOTPTest extends CIDatabaseTestCase $totpCode = $totp->now(); usleep(1000); + $this->assertTrue($this->library->verifyUserTotpCode($totpCode)); $this->assertTrue($this->library->verifyUserTotpCode($totpCode, 1)); }