diff --git a/Libraries/Aauth.php b/Libraries/Aauth.php
index 8dcbfa6..1839dc5 100644
--- a/Libraries/Aauth.php
+++ b/Libraries/Aauth.php
@@ -1,20 +1,108 @@
config = new Magefly\Aauth\Config\Aauth();
+/**
+ * Aauth is a User Authorization Library for CodeIgniter 4.x, which aims to make
+ * easy some essential jobs such as login, permissions and access operations.
+ * Despite ease of use, it has also very advanced features like groupping,
+ * access management, public access etc..
+ *
+ * @package Aauth
+ * @author Magefly Team
+ * @copyright 2018 Magefly
+ * @copyright 2014-2018 British Columbia Institute of Technology (https://bcit.ca/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://github.com/magefly/CodeIgniter-Aauth
+ * @version 3.0.0-pre
+ *
+ * @todo separate (on some level) the unvalidated users from the "banned" users
+ */
+class Aauth
+{
+ /**
+ * Variable for loading the config array into
+ *
+ * @var object
+ */
+ private $config;
+
+ /**
+ * Variable for loading the session service into
+ *
+ * @var object
+ */
+ private $session;
+
+ /**
+ * Array to store error messages
+ *
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * Local temporary storage for current flash errors
+ *
+ * Used to update current flash data list since flash data is only available on the next page refresh
+ *
+ * @var array
+ */
+ private $flashErrors = [];
+
+ /**
+ * Array to store info messages
+ *
+ * @var array
+ */
+ private $infos = [];
+
+ /**
+ * Local temporary storage for current flash infos
+ *
+ * Used to update current flash data list since flash data is only available on the next page refresh
+ *
+ * @var array
+ */
+ private $flashInfos = [];
+
+ /**
+ * Array to cache permission-ids.
+ *
+ * @var array
+ */
+ private $cachePermId = [];
+
+ /**
+ * Array to cache group-ids.
+ *
+ * @var array
+ */
+ private $cacheGroupId = [];
+
+ /**
+ * Constructor
+ *
+ * Prepares config & session variable.
+ */
+ function __construct()
+ {
+ $this->config = new \Magefly\Aauth\Config\Aauth();
$this->session = \Config\Services::session();
}
+ /**
+ * Create user
+ *
+ * Creates a new user
+ *
+ * @param string $email User's email address
+ * @param string $password User's password
+ * @param string $username User's username
+ *
+ * @return int|bool False if create fails or returns user id if successful
+ */
public function createUser($email, $password, $username = null)
{
- $user = new \Magefly\Aauth\Models\UserModel();
+ $userModel = new \Magefly\Aauth\Models\UserModel();
$data['email'] = $email;
$data['password'] = $password;
@@ -24,32 +112,43 @@ class Aauth {
$data['username'] = $username;
}
- if ($user_id = $user->insert($data))
+ if ($userId = $userModel->insert($data))
{
- return $user_id;
+ return $userId;
}
- $this->error($user->errors());
+ $this->error($userModel->errors());
return false;
}
- public function updateUser($user_id, $email = null, $password = null, $username = null)
+ /**
+ * Update user
+ *
+ * Updates existing user details
+ *
+ * @param int $user_id User id to update
+ * @param string|bool $email User's email address, or FALSE if not to be updated
+ * @param string|bool $password User's password, or FALSE if not to be updated
+ * @param string|bool $name User's name, or FALSE if not to be updated
+ *
+ * @return bool Update fails/succeeds
+ */
+ public function updateUser($userId, $email = null, $password = null, $username = null)
{
- $user = new \Magefly\Aauth\Models\UserModel();
+ $userModel = new \Magefly\Aauth\Models\UserModel();
$data = [];
- if ( ! $user->exists($user_id))
+ if ( ! $userModel->existsById($userId))
{
$this->error(lang('Aauth.notFoundUser'));
return false;
}
-
- if ( ! $email && ! $password && ! $username)
+ else if ( ! $email && ! $password && ! $username)
{
- return false;
+ return false;
}
- $data['id'] = $user_id;
+ $data['id'] = $userId;
if ($email)
{
@@ -66,149 +165,192 @@ class Aauth {
$data['username'] = $username;
}
- if ($user->update($user_id, $data))
+ if ($userModel->update($userId, $data))
{
- return $user_id;
+ return true;
}
- $this->error($user->errors());
+ $this->error($userModel->errors());
return false;
}
- public function deleteUser(int $user_id)
+ /**
+ * Delete user
+ *
+ */
+ public function deleteUser(int $userId)
{
- $user = new \Magefly\Aauth\Models\UserModel();
+ $userModel = new \Magefly\Aauth\Models\UserModel();
$data = [];
- if ( ! $user->exists($user_id))
+ if ( ! $userModel->existsById($userId))
{
$this->error(lang('Aauth.notFoundUser'));
return false;
}
-
- if ($user->delete($user_id))
+ else if ($userModel->delete($userId))
{
return true;
}
}
- public function listUsers(int $limit = 0, int $offset = 0, bool $include_banneds = null, array $order_by = null)
+ /**
+ * List users
+ *
+ * Return users as an object array
+ *
+ * @todo bool|int $group_par Specify group id to list group or FALSE for all users
+ *
+ * @param string $limit Limit of users to be returned
+ * @param bool $offset Offset for limited number of users
+ * @param bool $include_banneds Include banned users
+ * @param string $sort Order by MYSQL string (e.g. 'name ASC', 'email DESC')
+ *
+ * @return array Array of users
+ */
+ public function listUsers(int $limit = 0, int $offset = 0, bool $includeBanneds = null, array $orderBy = null)
{
- $user = new \Magefly\Aauth\Models\UserModel();
+ $userModel = new \Magefly\Aauth\Models\UserModel();
$options = [];
+ $user = $userModel->limit($limit, $offset);
// bool $group_par = null,
- if ( ! $include_banneds)
+ if ( ! $includeBanneds)
{
- $options['where'] = ['banned' => 0];
+ $user->where('banned', 0);
}
- if ($order_by)
+ if ($orderBy)
{
- $options['order_by'] = $order_by;
+ $user->orderBy($orderBy[0], $orderBy[1]);
}
- return $user->findAllExtra($limit, $offset, $options);
+ return $user->findAll();
}
- public function login(string $identifier, string $password, bool $remember = null, bool $totp_code = null)
+
+ /**
+ * Login user
+ *
+ * Check provided details against the database. Add items to error array on fail, create session if success
+ *
+ * @todo add TOTP
+ * @todo add reCAPTCHA
+ * @todo add Remeber Cookie aka LoginToken (new DB)
+ *
+ * @param string $email
+ * @param string $pass
+ * @param bool $remember
+ * @param bool $totpCode
+ *
+ * @return bool Indicates successful login.
+ */
+ public function login(string $identifier, string $password, bool $remember = null, bool $totpCode = null)
{
- $user = new \Magefly\Aauth\Models\UserModel();
- $loginAttempt = new \Magefly\Aauth\Models\LoginAttemptModel();
+ $userModel = new \Magefly\Aauth\Models\UserModel();
+ $loginAttemptModel = new \Magefly\Aauth\Models\LoginAttemptModel();
+ $userVariableModel = new \Magefly\Aauth\Models\UserVariableModel();
helper('cookie');
delete_cookie('user');
-
- if ($this->config->loginProtection && ! $loginAttempt->update())
+ if ($this->config->loginProtection && ! $loginAttemptModel->update())
{
$this->error(lang('Aauth.loginAttemptsExceeded'));
return false;
}
- // if($this->config_vars['ddos_protection'] && $this->config_vars['recaptcha_active'] && $loginAttempt->get() > $this->config_vars['recaptcha_login_attempts']){
+ // if($this->config->ddos_protection && $this->config->recaptcha_active && $loginAttempts->get() > $this->config->recaptcha_login_attempts){
// $this->CI->load->helper('recaptchalib');
- // $reCaptcha = new ReCaptcha( $this->config_vars['recaptcha_secret']);
+ // $reCaptcha = new ReCaptcha( $this->config->recaptcha_secret);
// $resp = $reCaptcha->verifyResponse( $this->CI->input->server("REMOTE_ADDR"), $this->CI->input->post("g-recaptcha-response") );
// if( ! $resp->success){
// $this->error($this->CI->lang->line('aauth_error_recaptcha_not_correct'));
- // return FALSE;
+ // return false;
// }
// }
- if ($this->config->loginUseUsername)
- {
+
+ if ($this->config->loginUseUsername)
+ {
if ( ! $identifier OR strlen($password) < $this->config->passwordMin OR strlen($password) > $this->config->passwordMax)
{
$this->error(lang('Aauth.loginFailedName'));
- return FALSE;
+ return false;
}
- $db_identifier = 'username';
-
- }else{
- $validation = \Config\Services::validation();
+ if ($user = $userModel->where('username', $identifier)->first())
+ {
+ $this->error(lang('Aauth.notFoundUser'));
+ return false;
+ }
+ }
+ else
+ {
+ $validation = \Config\Services::validation();
- if( ! $validation->check($identifier, 'valid_email') OR strlen($password) < $this->config->passwordMin OR strlen($password) > $this->config->passwordMax)
+ if ( ! $validation->check($identifier, 'valid_email') OR strlen($password) < $this->config->passwordMin OR strlen($password) > $this->config->passwordMax)
{
$this->error(lang('Aauth.loginFailedEmail'));
- return FALSE;
+ return false;
+ }
+
+ if ( ! $user = $userModel->where('email', $identifier)->first())
+ {
+ $this->error(lang('Aauth.notFoundUser'));
+ return false;
}
- $db_identifier = 'email';
- }
-
- $query = null;
- $query = $this->aauth_db->where($db_identifier, $identifier);
- $query = $this->aauth_db->where('banned', 1);
- $query = $this->aauth_db->where('verification_code !=', '');
- $query = $this->aauth_db->get($this->config_vars['users']);
- if ($query->num_rows() > 0) {
- $this->error($this->CI->lang->line('aauth_error_account_not_verified'));
- return FALSE;
}
- // to find user id, create sessions and cookies
- $query = $this->aauth_db->where($db_identifier, $identifier);
- $query = $this->aauth_db->get($this->config_vars['users']);
- if($query->num_rows() == 0){
- $this->error($this->CI->lang->line('aauth_error_no_user'));
- return FALSE;
+
+
+ if ($user['banned'] && ! empty($userVariableModel->get($user['id'], 'verification_code', true)))
+ {
+ $this->error(lang('Aauth.notVerified'));
+ return false;
}
- if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_only_on_ip_change'] == FALSE AND $this->config_vars['totp_two_step_login_active'] == TRUE){
- if($this->config_vars['totp_two_step_login_active'] == TRUE){
- $this->CI->session->set_userdata('totp_required', true);
+ else if ($user['banned'])
+ {
+ $this->error(lang('Aauth.invalidUserBanned'));
+ return false;
+ }
+
+ if ($this->config->totpEnabled && ! $this->config->totpOnIpChange && $this->config->totpLogin)
+ {
+ if ($this->config->totpLogin == true)
+ {
+ $this->session->set('totp_required', true);
}
- $query = null;
- $query = $this->aauth_db->where($db_identifier, $identifier);
- $query = $this->aauth_db->get($this->config_vars['users']);
- $totp_secret = $query->row()->totp_secret;
- if ($query->num_rows() > 0 AND !$totp_code) {
- $this->error($this->CI->lang->line('aauth_error_totp_code_required'));
- return FALSE;
- }else {
- if(!empty($totp_secret)){
+
+ $totp_secret = $userVariableModel->get($user['id'], 'totp_secret', true);
+ if ( ! empty($totp_secret) && ! $totp_code) {
+ $this->error(lang('Aauth.requiredTOTPCode'));
+ return false;
+ } else {
+ if( ! empty($totp_secret)){
$this->CI->load->helper('googleauthenticator');
$ga = new PHPGangsta_GoogleAuthenticator();
$checkResult = $ga->verifyCode($totp_secret, $totp_code, 0);
- if (!$checkResult) {
- $this->error($this->CI->lang->line('aauth_error_totp_code_invalid'));
- return FALSE;
+ if ( ! $checkResult) {
+ $this->error(lang('Aauth.invalidTOTPCode'));
+ return false;
}
}
}
- }
- if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_only_on_ip_change'] == TRUE){
+ }
+ else if ($this->config->totpEnabled && $this->config->totpOnIpChange)
+ {
$query = null;
$query = $this->aauth_db->where($db_identifier, $identifier);
- $query = $this->aauth_db->get($this->config_vars['users']);
+ $query = $this->aauth_db->get($this->config->users);
$totp_secret = $query->row()->totp_secret;
$ip_address = $query->row()->ip_address;
$current_ip_address = $this->CI->input->ip_address();
if ($query->num_rows() > 0 AND !$totp_code) {
if($ip_address != $current_ip_address ){
- if($this->config_vars['totp_two_step_login_active'] == FALSE){
+ if($this->config->totpLogin == false){
$this->error($this->CI->lang->line('aauth_error_totp_code_required'));
- return FALSE;
- } else if($this->config_vars['totp_two_step_login_active'] == TRUE){
- $this->CI->session->set_userdata('totp_required', true);
+ return false;
+ } else if($this->config->totpLogin == true){
+ $this->session->set('totp_required', true);
}
}
}else {
@@ -219,87 +361,119 @@ class Aauth {
$checkResult = $ga->verifyCode($totp_secret, $totp_code, 0);
if (!$checkResult) {
$this->error($this->CI->lang->line('aauth_error_totp_code_invalid'));
- return FALSE;
+ return false;
}
}
}
}
- }
- $query = null;
- $query = $this->aauth_db->where($db_identifier, $identifier);
- $query = $this->aauth_db->where('banned', 0);
- $query = $this->aauth_db->get($this->config_vars['users']);
- $row = $query->row();
- // if email and pass matches and not banned
- $password = ($this->config_vars['use_password_hash'] ? $password : $this->hash_password($password, $row->id));
- if ( $query->num_rows() != 0 && $this->verify_password($password, $row->password) ) {
- // If email and pass matches
- // create session
- $data = array(
- 'id' => $row->id,
- 'username' => $row->username,
- 'email' => $row->email,
- 'loggedin' => TRUE
- );
- $this->CI->session->set_userdata($data);
- if ( $remember ){
- $this->CI->load->helper('string');
- $expire = $this->config_vars['remember'];
- $today = date("Y-m-d");
- $remember_date = date("Y-m-d", strtotime($today . $expire) );
- $random_string = random_string('alnum', 16);
- $this->update_remember($row->id, $random_string, $remember_date );
- $cookie = array(
- 'name' => 'user',
- 'value' => $row->id . "-" . $random_string,
- 'expire' => 99*999*999,
- 'path' => '/',
- );
- $this->CI->input->set_cookie($cookie);
- }
- // update last login
- $this->update_last_login($row->id);
- $this->update_activity();
- if($this->config_vars['remove_successful_attempts'] == TRUE){
- $this->reset_login_attempts();
+ }
+
+ if ( ! $user['banned'] && password_verify($password, $user['password']))
+ {
+ $data = [
+ 'id' => $user['id'],
+ 'username' => $user['username'],
+ 'email' => $user['email'],
+ 'loggedin' => true
+ ];
+
+ $this->session->set($data);
+
+ // if ( $remember ){
+ // helper('text');
+ // $this->CI->load->helper('string');
+ // $expire = $this->config->loginRemember;
+ // $remember_date = date("Y-m-d", strtotime($expire) );
+ // $random_string = random_string('alnum', 16);
+ // $this->updateRemember($row->id, $random_string, $remember_date );
+ // $cookie = array(
+ // 'name' => 'user',
+ // 'value' => $row->id . "-" . $random_string,
+ // 'expire' => 99*999*999,
+ // 'path' => '/',
+ // );
+ // $this->CI->input->set_cookie($cookie);
+ // }
+
+ $userModel->updateLastLogin($user['id']);
+ $userModel->updateLastActivity($user['id']);
+
+ if ($this->config->loginAttemptRemoveSuccessful)
+ {
+ $loginAttemptModel->delete();
}
- return TRUE;
+
+ return true;
}
- // if not matches
- else {
- $this->error($this->CI->lang->line('aauth_error_login_failed_all'));
- return FALSE;
+ else
+ {
+ $this->error(lang('Aauth.loginFailedAll'));
+ return false;
}
}
+ /**
+ * Error
+ *
+ * Add message to error array and set flash data
+ *
+ * @param string $message Message to add to array
+ * @param bool $flashdata if TRUE add $message to CI flashdata (deflault: FALSE)
+ */
public function error($message = '', $flashdata = null)
{
$this->errors[] = $message;
if ($flashdata)
{
- $this->flash_errors[] = $message;
- $this->session->set('errors', $this->flash_errors);
+ $this->flashErrors[] = $message;
+ $this->session->set('errors', $this->flashErrors);
$this->session->setFlashdata('errors');
}
}
+ /**
+ * Keep Errors
+ *
+ * Keeps the flashdata errors for one more page refresh. Optionally adds the default errors into the
+ * flashdata list. This should be called last in your controller, and with care as it could continue
+ * to revive all errors and not let them expire as intended.
+ * Benefitial when using Ajax Requests
+ *
+ * @see http://ellislab.com/codeigniter/user-guide/libraries/sessions.html
+ *
+ * @param bool $include_non_flash TRUE if it should stow basic errors as flashdata (default = FALSE)
+ */
public function keepErrors($includeNonFlash = null)
{
if ($includeNonFlash)
- $this->flash_errors = array_merge($this->flash_errors, $this->errors);
+ $this->flashErrors = array_merge($this->flashErrors, $this->errors);
- $this->flash_errors = array_merge($this->flash_errors, (array)$this->session->getFlashdata('errors'));
- $this->session->set('errors', $this->flash_errors);
+ $this->flashErrors = array_merge($this->flashErrors, (array)$this->session->getFlashdata('errors'));
+ $this->session->set('errors', $this->flashErrors);
$this->session->setFlashdata('errors');
}
+ /**
+ * Get Errors Array
+ *
+ * Return array of errors
+ *
+ * @return array Array of messages, empty array if no errors
+ */
public function getErrorsArray()
{
return $this->errors;
}
+ /**
+ * Print Errors
+ *
+ * Prints string of errors separated by delimiter
+ *
+ * @param string $divider Separator for errors
+ */
public function printErrors($divider = '
', $return = null)
{
$msg = '';
@@ -322,39 +496,79 @@ class Aauth {
echo $msg;
}
+ /**
+ * Clear Errors
+ *
+ * Removes errors from error list and clears all associated flashdata
+ */
public function clearErrors()
{
- $this->errors = array();
+ $this->errors = [];
$this->session->remove('errors');
}
+ /**
+ * Info
+ *
+ * Add message to info array and set flash data
+ *
+ * @param string $message Message to add to infos array
+ * @param boolean $flashdata if TRUE add $message to CI flashdata (deflault: FALSE)
+ */
public function info($message = '', $flashdata = null)
{
$this->infos[] = $message;
if ($flashdata)
{
- $this->flash_infos[] = $message;
- $this->session->set('infos', $this->flash_infos);
+ $this->flashInfos[] = $message;
+ $this->session->set('infos', $this->flashInfos);
$this->session->setFlashdata('infos');
}
}
+ /**
+ * Keep Infos
+ *
+ * Keeps the flashdata infos for one more page refresh. Optionally adds the default infos into the
+ * flashdata list. This should be called last in your controller, and with care as it could continue
+ * to revive all infos and not let them expire as intended.
+ * Benefitial by using Ajax Requests
+ *
+ * @see http://ellislab.com/codeigniter/user-guide/libraries/sessions.html
+ *
+ * @param boolean $include_non_flash TRUE if it should stow basic infos as flashdata (default = FALSE)
+ */
public function keepInfos($includeNonFlash = null)
{
if ($includeNonFlash)
- $this->flash_infos = array_merge($this->flash_infos, $this->infos);
+ $this->flashInfos = array_merge($this->flashInfos, $this->infos);
- $this->flash_infos = array_merge($this->flash_infos, (array)$this->session->getFlashdata('infos'));
- $this->session->set('infos', $this->flash_infos);
+ $this->flashInfos = array_merge($this->flashInfos, (array)$this->session->getFlashdata('infos'));
+ $this->session->set('infos', $this->flashInfos);
$this->session->setFlashdata('infos');
}
+ /**
+ * Get Info Array
+ *
+ * Return array of infos
+ *
+ * @return array Array of messages, empty array if no errors
+ */
public function getInfosArray()
{
return $this->infos;
}
+ /**
+ * Print Info
+ *
+ * Print string of info separated by delimiter
+ *
+ * @param string $divider Separator for info
+ *
+ */
public function printInfos($divider = '
', $return = null)
{
$msg = '';
@@ -375,9 +589,14 @@ class Aauth {
echo $msg;
}
+ /**
+ * Clear Info List
+ *
+ * Removes info messages from info list and clears all associated flashdata
+ */
public function clearInfos()
{
- $this->infos = array();
+ $this->infos = [];
$this->session->remove('infos');
}
}