diff --git a/.gitignore b/.gitignore index 4dff4e4..4e79763 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ # Created by .gitignore support plugin (hsz.mobi) -.idea/ \ No newline at end of file +.idea/ diff --git a/README.md b/README.md index 09e87fc..44ce482 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ +![CodeIgniter-Aauth-Logo](https://cloud.githubusercontent.com/assets/2417212/8925689/add409ea-34be-11e5-8e50-845da8f5b1b0.png) + + *** -Aauth is a User Authorization Library for CodeIgniter 2.x, which aims to make easy some essential jobs such as login, permissions and access operations. Despite its ease of use, it has also very advanced features like private messages, groupping, access management, and public access. +Aauth is a User Authorization Library for CodeIgniter 2.x and 3.x, which aims to make easy some essential jobs such as login, permissions and access operations. Despite its ease of use, it has also very advanced features like private messages, groupping, access management, and public access. **This is Quick Start page. You can also take a look at the [detailed Documentation Wiki](https://github.com/emreakay/CodeIgniter-Aauth/wiki/_pages) to learn about other great Features** @@ -25,6 +28,7 @@ Aauth is a User Authorization Library for CodeIgniter 2.x, which aims to make ea * Login DDoS Protection * Updated functions (check documentation for details) * Bugs fixes +* TOTP (Time-based One-time Password) ### Migration *** diff --git a/application/config/aauth.php b/application/config/aauth.php index c1061be..ef1cb27 100644 --- a/application/config/aauth.php +++ b/application/config/aauth.php @@ -1,79 +1,129 @@ - FALSE, + + 'admin_group' => 'admin', + 'default_group' => 'default', + 'public_group' => 'public', + + 'db_profile' => 'default', + + 'users' => 'aauth_users', + 'groups' => 'aauth_groups', + 'user_to_group' => 'aauth_user_to_group', + 'perms' => 'aauth_perms', + 'perm_to_group' => 'aauth_perm_to_group', + 'perm_to_user' => 'aauth_perm_to_user', + 'pms' => 'aauth_pms', + 'system_variables' => 'aauth_system_variables', + 'user_variables' => 'aauth_user_variables', + + 'remember' => ' +3 days', + + 'max' => 13, + 'min' => 5, + + 'valid_chars' => array(), + + 'ddos_protection' => true, + + 'recaptcha_active' => false, + 'recaptcha_login_attempts' => 4, + 'recaptcha_siteKey' => '', + 'recaptcha_secret' => '', + + 'totp_active' => false, + 'totp_only_on_ip_change' => false, + 'totp_reset_over_reset_password' => false, + + 'max_login_attempt' => 10, + + 'login_with_name' => false, + + 'use_cookies' => true, + + 'email' => 'admin@admin.com', + 'name' => 'Emre Akay', + + 'verification' => false, + 'verification_link' => '/account/verification/', + 'reset_password_link' => '/account/reset_password/', + + 'hash' => 'sha256' +); + +$config['aauth'] = $config_aauth['default']; /* End of file aauth.php */ -/* Location: ./application/config/aauth.php */ +/* Location: ./application/config/aauth.php */ \ No newline at end of file diff --git a/application/helpers/googleauthenticator_helper.php b/application/helpers/googleauthenticator_helper.php new file mode 100644 index 0000000..7424d0b --- /dev/null +++ b/application/helpers/googleauthenticator_helper.php @@ -0,0 +1,208 @@ +_getBase32LookupTable(); + unset($validChars[32]); + + $secret = ''; + for ($i = 0; $i < $secretLength; $i++) { + $secret .= $validChars[array_rand($validChars)]; + } + return $secret; + } + + /** + * Calculate the code, with given secret and point in time + * + * @param string $secret + * @param int|null $timeSlice + * @return string + */ + public function getCode($secret, $timeSlice = null) + { + if ($timeSlice === null) { + $timeSlice = floor(time() / 30); + } + + $secretkey = $this->_base32Decode($secret); + + // Pack time into binary string + $time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice); + // Hash it with users secret key + $hm = hash_hmac('SHA1', $time, $secretkey, true); + // Use last nipple of result as index/offset + $offset = ord(substr($hm, -1)) & 0x0F; + // grab 4 bytes of the result + $hashpart = substr($hm, $offset, 4); + + // Unpak binary value + $value = unpack('N', $hashpart); + $value = $value[1]; + // Only 32 bits + $value = $value & 0x7FFFFFFF; + + $modulo = pow(10, $this->_codeLength); + return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT); + } + + /** + * Get QR-Code URL for image, from google charts + * + * @param string $name + * @param string $secret + * @param string $title + * @return string + */ + public function getQRCodeGoogleUrl($name, $secret, $title = null) { + $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.''); + if(isset($title)) { + $urlencoded .= urlencode('&issuer='.urlencode($title)); + } + return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.''; + } + + /** + * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now + * + * @param string $secret + * @param string $code + * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after) + * @param int|null $currentTimeSlice time slice if we want use other that time() + * @return bool + */ + public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) + { + if ($currentTimeSlice === null) { + $currentTimeSlice = floor(time() / 30); + } + + for ($i = -$discrepancy; $i <= $discrepancy; $i++) { + $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); + if ($calculatedCode == $code ) { + return true; + } + } + + return false; + } + + /** + * Set the code length, should be >=6 + * + * @param int $length + * @return PHPGangsta_GoogleAuthenticator + */ + public function setCodeLength($length) + { + $this->_codeLength = $length; + return $this; + } + + /** + * Helper class to decode base32 + * + * @param $secret + * @return bool|string + */ + protected function _base32Decode($secret) + { + if (empty($secret)) return ''; + + $base32chars = $this->_getBase32LookupTable(); + $base32charsFlipped = array_flip($base32chars); + + $paddingCharCount = substr_count($secret, $base32chars[32]); + $allowedValues = array(6, 4, 3, 1, 0); + if (!in_array($paddingCharCount, $allowedValues)) return false; + for ($i = 0; $i < 4; $i++){ + if ($paddingCharCount == $allowedValues[$i] && + substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) return false; + } + $secret = str_replace('=','', $secret); + $secret = str_split($secret); + $binaryString = ""; + for ($i = 0; $i < count($secret); $i = $i+8) { + $x = ""; + if (!in_array($secret[$i], $base32chars)) return false; + for ($j = 0; $j < 8; $j++) { + $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); + } + $eightBits = str_split($x, 8); + for ($z = 0; $z < count($eightBits); $z++) { + $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:""; + } + } + return $binaryString; + } + + /** + * Helper class to encode base32 + * + * @param string $secret + * @param bool $padding + * @return string + */ + protected function _base32Encode($secret, $padding = true) + { + if (empty($secret)) return ''; + + $base32chars = $this->_getBase32LookupTable(); + + $secret = str_split($secret); + $binaryString = ""; + for ($i = 0; $i < count($secret); $i++) { + $binaryString .= str_pad(base_convert(ord($secret[$i]), 10, 2), 8, '0', STR_PAD_LEFT); + } + $fiveBitBinaryArray = str_split($binaryString, 5); + $base32 = ""; + $i = 0; + while ($i < count($fiveBitBinaryArray)) { + $base32 .= $base32chars[base_convert(str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)]; + $i++; + } + if ($padding && ($x = strlen($binaryString) % 40) != 0) { + if ($x == 8) $base32 .= str_repeat($base32chars[32], 6); + elseif ($x == 16) $base32 .= str_repeat($base32chars[32], 4); + elseif ($x == 24) $base32 .= str_repeat($base32chars[32], 3); + elseif ($x == 32) $base32 .= $base32chars[32]; + } + return $base32; + } + + /** + * Get array with all 32 characters for decoding from/encoding to base32 + * + * @return array + */ + protected function _getBase32LookupTable() + { + return array( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23 + 'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31 + '=' // padding char + ); + } +} diff --git a/application/language/english/aauth_lang.php b/application/language/english/aauth_lang.php index b6b3f3a..437a69d 100644 --- a/application/language/english/aauth_lang.php +++ b/application/language/english/aauth_lang.php @@ -25,6 +25,9 @@ $lang['aauth_error_email_invalid'] = 'Invalid e-mail address'; $lang['aauth_error_password_invalid'] = 'Invalid password'; $lang['aauth_error_username_invalid'] = 'Invalid Username'; $lang['aauth_error_username_required'] = 'Username required'; +$lang['aauth_error_totp_code_required'] = 'TOTP Code required'; +$lang['aauth_error_totp_code_invalid'] = 'Invalid TOTP Code'; + // Account update errors $lang['aauth_error_update_email_exists'] = 'Email address already exists on the system. Please enter a different email address.'; @@ -35,10 +38,10 @@ $lang['aauth_error_update_username_exists'] = "Username already exists on the sy $lang['aauth_error_no_access'] = 'Sorry, you do not have access to the resource you requested.'; $lang['aauth_error_login_failed_email'] = 'E-mail Address and Password do not match.'; $lang['aauth_error_login_failed_name'] = 'Username and Password do not match.'; +$lang['aauth_error_login_failed_all'] = 'E-mail, Username or Password do not match.'; $lang['aauth_error_login_attempts_exceeded'] = 'You have exceeded your login attempts, your account has now been locked.'; $lang['aauth_error_recaptcha_not_correct'] = 'Sorry, the reCAPTCHA text entered was incorrect.'; - // Misc. errors $lang['aauth_error_no_user'] = 'User does not exist'; $lang['aauth_error_account_not_verified'] = 'Your account has not been verified. Please check your e-mail and verify your account.'; diff --git a/application/language/french/aauth_lang.php b/application/language/french/aauth_lang.php new file mode 100644 index 0000000..31e7257 --- /dev/null +++ b/application/language/french/aauth_lang.php @@ -0,0 +1,56 @@ +CI->load->helper('email'); $this->CI->load->helper('language'); $this->CI->load->helper('recaptchalib'); + $this->CI->load->helper('googleauthenticator_helper'); $this->CI->lang->load('aauth'); // config/aauth.php @@ -111,8 +111,8 @@ class Aauth { $this->aauth_db = $this->CI->load->database($this->config_vars['db_profile'], TRUE); // load error and info messages from flashdata (but don't store back in flashdata) - $this->errors = $this->CI->session->flashdata('errors'); - $this->infos = $this->CI->session->flashdata('infos'); + $this->errors = $this->CI->session->flashdata('errors') ?: array(); + $this->infos = $this->CI->session->flashdata('infos') ?: array(); } @@ -129,27 +129,30 @@ class Aauth { * @param bool $remember * @return bool Indicates successful login. */ - public function login($identifier, $pass, $remember = FALSE) { + public function login($identifier, $pass, $remember = FALSE, $totp_code = NULL) { - // Remove cookies first - $cookie = array( - 'name' => 'user', - 'value' => '', - 'expire' => time()-3600, - 'path' => '/', - ); + if($this->config_vars['use_cookies'] == TRUE){ + // Remove cookies first + $cookie = array( + 'name' => 'user', + 'value' => '', + 'expire' => -3600, + 'path' => '/', + ); + $this->CI->input->set_cookie($cookie); + } - $this->CI->input->set_cookie($cookie); if( $this->config_vars['login_with_name'] == TRUE){ - if( !$identifier OR strlen($pass) < 5 OR strlen($pass) > $this->config_vars['max'] ) + + if( !$identifier OR strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] ) { $this->error($this->CI->lang->line('aauth_error_login_failed_name')); return FALSE; } $db_identifier = 'name'; }else{ - if( !valid_email($identifier) OR strlen($pass) < 5 OR strlen($pass) > $this->config_vars['max'] ) + if( !valid_email($identifier) OR strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] ) { $this->error($this->CI->lang->line('aauth_error_login_failed_email')); return FALSE; @@ -183,13 +186,17 @@ class Aauth { $query = $this->aauth_db->get($this->config_vars['users']); $row = $query->row(); if($query->num_rows() > 0 && $this->config_vars['ddos_protection'] && $this->config_vars['recaptcha_active'] && $row->login_attempts >= $this->config_vars['recaptcha_login_attempts']){ - $reCAPTCHA_cookie = array( - 'name' => 'reCAPTCHA', - 'value' => 'true', - 'expire' => time()+7200, - 'path' => '/', - ); - $this->CI->input->set_cookie($reCAPTCHA_cookie); + if($this->config_vars['use_cookies'] == TRUE){ + $reCAPTCHA_cookie = array( + 'name' => 'reCAPTCHA', + 'value' => 'true', + 'expire' => 7200, + 'path' => '/', + ); + $this->CI->input->set_cookie($reCAPTCHA_cookie); + }else{ + $this->CI->session->set_tempdata('reCAPTCHA', 'true', 7200); + } } // if user is not verified @@ -209,12 +216,69 @@ class Aauth { $query = $this->aauth_db->get($this->config_vars['users']); if($query->num_rows() == 0){ - $this->error($this->CI->lang->line('aauth_error_login_failed')); + $this->error($this->CI->lang->line('aauth_error_no_user')); return FALSE; } $user_id = $query->row()->id; - + if($this->config_vars['recaptcha_active']){ + if( ($this->config_vars['use_cookies'] == TRUE && $this->CI->input->cookie('reCAPTCHA', TRUE) == 'true') || ($this->config_vars['use_cookies'] == FALSE && $this->CI->session->tempdata('reCAPTCHA') == 'true') ){ + $reCaptcha = new ReCaptcha( $this->config_vars['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; + } + } + } + + if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_only_on_ip_change'] == FALSE){ + $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)){ + $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($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_only_on_ip_change'] == 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; + $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 ){ + $this->error($this->CI->lang->line('aauth_error_totp_code_required')); + return FALSE; + } + }else { + if(!empty($totp_secret)){ + if($ip_address != $current_ip_address ){ + $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; + } + } + } + } + } + $query = null; $query = $this->aauth_db->where($db_identifier, $identifier); @@ -225,18 +289,9 @@ class Aauth { $query = $this->aauth_db->get($this->config_vars['users']); $row = $query->row(); - if($this->CI->input->cookie('reCAPTCHA', TRUE) == 'true'){ - $reCaptcha = new ReCaptcha( $this->config_vars['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; - } - } - // if email and pass matches and not banned - if ( $query->num_rows() > 0 ) { + if ( $query->num_rows() != 0 ) { // If email and pass matches // create session @@ -257,24 +312,32 @@ class Aauth { $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' => time() + 99*999*999, - 'path' => '/', - ); - - $this->CI->input->set_cookie($cookie); + if($this->config_vars['use_cookies'] == TRUE){ + $cookie = array( + 'name' => 'user', + 'value' => $row->id . "-" . $random_string, + 'expire' => 99*999*999, + 'path' => '/', + ); + + $this->CI->input->set_cookie($cookie); + }else{ + $this->CI->session->set_userdata('remember', $row->id . "-" . $random_string); + } } if($this->config_vars['recaptcha_active']){ - $reCAPTCHA_cookie = array( - 'name' => 'reCAPTCHA', - 'value' => 'false', - 'expire' => time()-3600, - 'path' => '/', - ); - $this->CI->input->set_cookie($reCAPTCHA_cookie); + if($this->config_vars['use_cookies'] == TRUE){ + $reCAPTCHA_cookie = array( + 'name' => 'reCAPTCHA', + 'value' => 'false', + 'expire' => -3600, + 'path' => '/', + ); + $this->CI->input->set_cookie($reCAPTCHA_cookie); + }else{ + $this->CI->session->unset_tempdata('reCAPTCHA'); + } } // update last login @@ -287,7 +350,7 @@ class Aauth { // if not matches else { - $this->error($this->CI->lang->line('aauth_error_login_failed')); + $this->error($this->CI->lang->line('aauth_error_login_failed_all')); return FALSE; } } @@ -305,37 +368,67 @@ class Aauth { // cookie control else { - if( ! $this->CI->input->cookie('user', TRUE) ){ - return FALSE; - } else { - $cookie = explode('-', $this->CI->input->cookie('user', TRUE)); - if(!is_numeric( $cookie[0] ) OR strlen($cookie[1]) < 13 ){return FALSE;} - else{ - $query = $this->aauth_db->where('id', $cookie[0]); - $query = $this->aauth_db->where('remember_exp', $cookie[1]); - $query = $this->aauth_db->get($this->config_vars['users']); - - $row = $query->row(); - - if ($query->num_rows() < 1) { - $this->update_remember($cookie[0]); - return FALSE; - }else{ - - if(strtotime($row->remember_time) > strtotime("now") ){ - $this->login_fast($cookie[0]); - return TRUE; + if($this->config_vars['use_cookies'] == TRUE){ + if( ! $this->CI->input->cookie('user', TRUE) ){ + return FALSE; + } else { + $cookie = explode('-', $this->CI->input->cookie('user', TRUE)); + if(!is_numeric( $cookie[0] ) OR strlen($cookie[1]) < 13 ){return FALSE;} + else{ + $query = $this->aauth_db->where('id', $cookie[0]); + $query = $this->aauth_db->where('remember_exp', $cookie[1]); + $query = $this->aauth_db->get($this->config_vars['users']); + + $row = $query->row(); + + if ($query->num_rows() < 1) { + $this->update_remember($cookie[0]); + return FALSE; + }else{ + + if(strtotime($row->remember_time) > strtotime("now") ){ + $this->login_fast($cookie[0]); + return TRUE; + } + // if time is expired + else { + return FALSE; + } } - // if time is expired - else { + } + } + }else{ + if(!isset($_SESSION['remember'])){ + return FALSE; + }else{ + $session = explode('-', $this->CI->session->userdata('remember')); + if(!is_numeric( $session[0] ) OR strlen($session[1]) < 13 ){return FALSE;} + else{ + $query = $this->aauth_db->where('id', $session[0]); + $query = $this->aauth_db->where('remember_exp', $session[1]); + $query = $this->aauth_db->get($this->config_vars['users']); + + $row = $query->row(); + + if ($query->num_rows() < 1) { + $this->update_remember($session[0]); return FALSE; + }else{ + + if(strtotime($row->remember_time) > strtotime("now") ){ + $this->login_fast($session[0]); + return TRUE; + } + // if time is expired + else { + return FALSE; + } } } } } } - return FALSE; } @@ -375,15 +468,16 @@ class Aauth { */ public function logout() { - $cookie = array( - 'name' => 'user', - 'value' => '', - 'expire' => time()-3600, - 'path' => '/', - ); - - $this->CI->input->set_cookie($cookie); - + if($this->config_vars['use_cookies'] == TRUE){ + $cookie = array( + 'name' => 'user', + 'value' => '', + 'expire' => -3600, + 'path' => '/', + ); + $this->CI->input->set_cookie($cookie); + } + return $this->CI->session->sess_destroy(); } @@ -486,6 +580,10 @@ class Aauth { 'pass' => $this->hash_password($pass, $user_id) ); + if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_reset_over_reset_password'] == TRUE){ + $data['totp_secret'] = NULL; + } + $row = $query->row(); $email = $row->email; @@ -615,11 +713,12 @@ class Aauth { $this->error($this->CI->lang->line('aauth_error_email_exists')); $valid = FALSE; } - if (!valid_email($email)){ + $valid_email = (bool) filter_var($email, FILTER_VALIDATE_EMAIL); + if (!$valid_email){ $this->error($this->CI->lang->line('aauth_error_email_invalid')); $valid = FALSE; } - if ( strlen($pass) < 5 OR strlen($pass) > $this->config_vars['max'] ){ + if ( strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] ){ $this->error($this->CI->lang->line('aauth_error_password_invalid')); $valid = FALSE; } @@ -689,7 +788,8 @@ class Aauth { $this->error($this->CI->lang->line('aauth_error_update_email_exists')); $valid = FALSE; } - if (!valid_email($email)){ + $valid_email = (bool) filter_var($email, FILTER_VALIDATE_EMAIL); + if (!$valid_email){ $this->error($this->CI->lang->line('aauth_error_email_invalid')); $valid = FALSE; } @@ -697,7 +797,7 @@ class Aauth { } if ($pass != FALSE) { - if ( strlen($pass) < 5 OR strlen($pass) > $this->config_vars['max'] ){ + if ( strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] ){ $this->error($this->CI->lang->line('aauth_error_password_invalid')); $valid = FALSE; } @@ -856,12 +956,10 @@ class Aauth { * Delete user * Delete a user from database. WARNING Can't be undone * @param int $user_id User id to delete + * @return bool Delete fails/succeeds */ public function delete_user($user_id) { - $this->aauth_db->where('id', $user_id); - $this->aauth_db->delete($this->config_vars['users']); - // delete from perm_to_user $this->aauth_db->where('user_id', $user_id); $this->aauth_db->delete($this->config_vars['perm_to_user']); @@ -873,6 +971,11 @@ class Aauth { // delete user vars $this->aauth_db->where('user_id', $user_id); $this->aauth_db->delete($this->config_vars['user_variables']); + + // delete user + $this->aauth_db->where('id', $user_id); + return $this->aauth_db->delete($this->config_vars['users']); + } //tested @@ -1042,7 +1145,7 @@ class Aauth { function hash_password($pass, $userid) { $salt = md5($userid); - return hash('sha256', $salt.$pass); + return hash($this->config_vars['hash'], $salt.$pass); } ######################## @@ -1054,9 +1157,10 @@ class Aauth { * Create group * Creates a new group * @param string $group_name New group name + * @param string $definition Description of the group * @return int|bool Group id or FALSE on fail */ - public function create_group($group_name, $definition) { + public function create_group($group_name, $definition = '') { $query = $this->aauth_db->get_where($this->config_vars['groups'], array('name' => $group_name)); @@ -1110,16 +1214,19 @@ class Aauth { $group_id = $this->get_group_id($group_par); - $this->aauth_db->where('id',$group_id); - $query = $this->aauth_db->get($this->config_vars['groups']); - if ($query->num_rows() == 0){ - return FALSE; - } + $this->aauth_db->where('id',$group_id); + $query = $this->aauth_db->get($this->config_vars['groups']); + if ($query->num_rows() == 0){ + return FALSE; + } // bug fixed // now users are deleted from user_to_group table $this->aauth_db->where('group_id', $group_id); $this->aauth_db->delete($this->config_vars['user_to_group']); + + $this->aauth_db->where('group_id', $group_id); + $this->aauth_db->delete($this->config_vars['perm_to_group']); $this->aauth_db->where('id', $group_id); return $this->aauth_db->delete($this->config_vars['groups']); @@ -1339,7 +1446,7 @@ class Aauth { // deletes from perm_to_user table $this->aauth_db->where('perm_id', $perm_id); - $this->aauth_db->delete($this->config_vars['perm_to_group']); + $this->aauth_db->delete($this->config_vars['perm_to_user']); // deletes from permission table $this->aauth_db->where('id', $perm_id); @@ -1356,24 +1463,36 @@ class Aauth { */ public function is_allowed($perm_par, $user_id=FALSE){ - $perm_id = $this->get_perm_id($perm_par); - if( $user_id == FALSE){ $user_id = $this->CI->session->userdata('id'); } + if($this->is_admin($user_id)) + { + return true; + } + + $perm_id = $this->get_perm_id($perm_par); + $query = $this->aauth_db->where('perm_id', $perm_id); $query = $this->aauth_db->where('user_id', $user_id); $query = $this->aauth_db->get( $this->config_vars['perm_to_user'] ); if( $query->num_rows() > 0){ - return TRUE; - } elseif ($this->is_group_allowed($perm_id)) { - return TRUE; + return TRUE; } else { - return FALSE; - } - + if( $user_id===FALSE){ + return $this->is_group_allowed($perm_id); + } else { + $g_allowed=FALSE; + foreach( $this->get_user_groups($user_id) as $group ){ + if ( $this->is_group_allowed($perm_id, $group->id) ){ + $g_allowed=TRUE; + } + } + return $g_allowed; + } + } } /** @@ -1547,7 +1666,7 @@ class Aauth { $query = $this->aauth_db->get($this->config_vars['perms']); if ($query->num_rows() == 0) - return NULL; + return FALSE; $row = $query->row(); return $row->id; @@ -1602,7 +1721,7 @@ class Aauth { 'receiver_id' => $receiver_id, 'title' => $title, 'message' => $message, - 'date' => date('Y-m-d H:i:s') + 'date_sent' => date('Y-m-d H:i:s') ); return $query = $this->aauth_db->insert( $this->config_vars['pms'], $data ); @@ -1650,11 +1769,12 @@ class Aauth { if ($query->num_rows() < 1) { $this->error( $this->CI->lang->line('aauth_error_no_pm') ); + return FALSE; } if ($set_as_read) $this->set_as_read_pm($pm_id); - return $query->result(); + return $query->row(); } //tested @@ -1683,7 +1803,7 @@ class Aauth { } $query = $this->aauth_db->where('receiver_id', $receiver_id); - $query = $this->aauth_db->where('read', 0); + $query = $this->aauth_db->where('date_read', NULL); $query = $this->aauth_db->get( $this->config_vars['pms'] ); return $query->num_rows(); @@ -1698,7 +1818,7 @@ class Aauth { public function set_as_read_pm($pm_id){ $data = array( - 'read' => 1, + 'date_read' => date('Y-m-d H:i:s') ); $this->aauth_db->update( $this->config_vars['pms'], $data, "id = $pm_id"); @@ -1754,15 +1874,7 @@ class Aauth { */ public function get_errors_array() { - - if (!count($this->errors)==0) - { - return $this->errors; - } - else - { - return array(); - } + return $this->errors; } /** @@ -1849,14 +1961,7 @@ class Aauth { */ public function get_infos_array() { - if (!count($this->infos)==0) - { - return $this->infos; - } - else - { - return array(); - } + return $this->infos; } @@ -1926,7 +2031,7 @@ class Aauth { if ($this->get_user_var($key,$user_id) ===FALSE) { $data = array( - 'key' => $key, + 'data_key' => $key, 'value' => $value, 'user_id' => $user_id ); @@ -1937,12 +2042,12 @@ class Aauth { else { $data = array( - 'key' => $key, + 'data_key' => $key, 'value' => $value, 'user_id' => $user_id ); - $this->aauth_db->where( 'key', $key ); + $this->aauth_db->where( 'data_key', $key ); $this->aauth_db->where( 'user_id', $user_id); return $this->aauth_db->update( $this->config_vars['user_variables'], $data); @@ -1967,7 +2072,7 @@ class Aauth { return FALSE; } - $this->aauth_db->where('key', $key); + $this->aauth_db->where('data_key', $key); $this->aauth_db->where('user_id', $user_id); return $this->aauth_db->delete( $this->config_vars['user_variables'] ); @@ -1993,7 +2098,7 @@ class Aauth { } $query = $this->aauth_db->where('user_id', $user_id); - $query = $this->aauth_db->where('key', $key); + $query = $this->aauth_db->where('data_key', $key); $query = $this->aauth_db->get( $this->config_vars['user_variables'] ); @@ -2025,7 +2130,7 @@ class Aauth { if ( ! $this->get_user($user_id)){ return FALSE; } - $query = $this->aauth_db->select('key'); + $query = $this->aauth_db->select('data_key'); $query = $this->aauth_db->where('user_id', $user_id); @@ -2057,7 +2162,7 @@ class Aauth { if ($this->get_system_var($key) === FALSE) { $data = array( - 'key' => $key, + 'data_key' => $key, 'value' => $value, ); @@ -2068,11 +2173,11 @@ class Aauth { else { $data = array( - 'key' => $key, + 'data_key' => $key, 'value' => $value, ); - $this->aauth_db->where( 'key', $key ); + $this->aauth_db->where( 'data_key', $key ); return $this->aauth_db->update( $this->config_vars['system_variables'], $data); } @@ -2086,7 +2191,7 @@ class Aauth { */ public function unset_system_var( $key ) { - $this->aauth_db->where('key', $key); + $this->aauth_db->where('data_key', $key); return $this->aauth_db->delete( $this->config_vars['system_variables'] ); } @@ -2100,7 +2205,7 @@ class Aauth { */ public function get_system_var( $key ){ - $query = $this->aauth_db->where('key', $key); + $query = $this->aauth_db->where('data_key', $key); $query = $this->aauth_db->get( $this->config_vars['system_variables'] ); @@ -2121,7 +2226,7 @@ class Aauth { */ public function list_system_var_keys(){ - $query = $this->aauth_db->select('key'); + $query = $this->aauth_db->select('data_key'); $query = $this->aauth_db->get( $this->config_vars['system_variables'] ); // if variable not set if ($query->num_rows() < 1) { return FALSE;} @@ -2132,14 +2237,46 @@ class Aauth { public function generate_recaptcha_field(){ $content = ''; - if($this->config_vars['ddos_protection'] && $this->config_vars['recaptcha_active'] && $this->CI->input->cookie('reCAPTCHA', TRUE) == 'true'){ - $content .= ""; - $siteKey = $this->config_vars['recaptcha_siteKey']; - $content .= "
"; + if($this->config_vars['ddos_protection'] && $this->config_vars['recaptcha_active']){ + if( ($this->config_vars['use_cookies'] == TRUE && $this->CI->input->cookie('reCAPTCHA', TRUE) == 'true') || ($this->config_vars['use_cookies'] == FALSE && $this->CI->session->tempdata('reCAPTCHA') == 'true') ){ + $content .= ""; + $siteKey = $this->config_vars['recaptcha_siteKey']; + $content .= "
"; + } } return $content; } + public function update_user_totp_secret($user_id = FALSE, $secret) { + + if ($user_id == FALSE) + $user_id = $this->CI->session->userdata('id'); + + $data['totp_secret'] = $secret; + + $this->aauth_db->where('id', $user_id); + return $this->aauth_db->update($this->config_vars['users'], $data); + } + + public function generate_unique_totp_secret(){ + $ga = new PHPGangsta_GoogleAuthenticator(); + $stop = false; + while (!$stop) { + $secret = $ga->createSecret(); + $query = $this->aauth_db->where('totp_secret', $secret); + $query = $this->aauth_db->get($this->config_vars['users']); + if ($query->num_rows() == 0) { + return $secret; + $stop = true; + } + } + } + + public function generate_totp_qrcode($secret){ + $ga = new PHPGangsta_GoogleAuthenticator(); + return $ga->getQRCodeGoogleUrl($this->config_vars['name'], $secret); + } + } // end class // $this->CI->session->userdata('id') @@ -2246,4 +2383,4 @@ return FALSE; /* End of file Aauth.php */ -/* Location: ./application/libraries/Aauth.php */ \ No newline at end of file +/* Location: ./application/libraries/Aauth.php */ diff --git a/sql/Aauth_v2.sql b/sql/Aauth_v2.sql index 008d6ad..e5f20d3 100644 --- a/sql/Aauth_v2.sql +++ b/sql/Aauth_v2.sql @@ -75,10 +75,10 @@ CREATE TABLE `aauth_pms` ( `receiver_id` int(11) unsigned NOT NULL, `title` varchar(255) NOT NULL, `message` text, - `date` datetime DEFAULT NULL, - `read` tinyint(1) DEFAULT '0', + `date_sent` datetime DEFAULT NULL, + `date_read` datetime DEFAULT NULL, PRIMARY KEY (`id`), - KEY `full_index` (`id`,`sender_id`,`receiver_id`,`read`) + KEY `full_index` (`id`,`sender_id`,`receiver_id`,`date_read`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- @@ -91,7 +91,7 @@ CREATE TABLE `aauth_pms` ( DROP TABLE IF EXISTS `aauth_system_variables`; CREATE TABLE `aauth_system_variables` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `key` varchar(100) NOT NULL, + `data_key` varchar(100) NOT NULL, `value` text, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -117,6 +117,7 @@ CREATE TABLE `aauth_users` ( `remember_time` datetime DEFAULT NULL, `remember_exp` text COLLATE utf8_general_ci, `verification_code` text COLLATE utf8_general_ci, + `totp_secret` varchar(16) COLLATE utf8_general_ci DEFAULT NULL, `ip_address` text COLLATE utf8_general_ci, `login_attempts` int(11) DEFAULT '0', PRIMARY KEY (`id`) @@ -125,7 +126,7 @@ CREATE TABLE `aauth_users` ( -- ---------------------------- -- Records of aauth_users -- ---------------------------- -INSERT INTO `aauth_users` VALUES ('1', 'admin@example.com', 'dd5073c93fb477a167fd69072e95455834acd93df8fed41a2c468c45b394bfe3', 'Admin', '0', null, null, null, null, null, null, null, null, '0'); +INSERT INTO `aauth_users` VALUES ('1', 'admin@example.com', 'dd5073c93fb477a167fd69072e95455834acd93df8fed41a2c468c45b394bfe3', 'Admin', '0', null, null, null, null, null, null, null, null, null, '0'); -- ---------------------------- -- Table structure for `aauth_user_to_group` @@ -150,7 +151,7 @@ DROP TABLE IF EXISTS `aauth_user_variables`; CREATE TABLE `aauth_user_variables` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(11) unsigned NOT NULL, - `key` varchar(100) NOT NULL, + `data_key` varchar(100) NOT NULL, `value` text, PRIMARY KEY (`id`), KEY `user_id_index` (`user_id`)