From 8ab1a6b69f91a68c628b817120102c3c680ce96d Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Tue, 22 Oct 2019 16:52:45 +0200 Subject: [PATCH] OTA PART 1 --- app/class/UserManager.php | 152 +++++++---------- app/controls/login.php | 43 +++++ app/templates/login.phtml | 30 ++-- app/templates/part/loginForm.phtml | 20 +++ app/templates/part/loginOta.phtml | 13 ++ app/templates/setting.phtml | 7 + app/vendor/GoogleAuthenticator.php | 252 +++++++++++++++++++++++++++++ index.php | 9 +- 8 files changed, 407 insertions(+), 119 deletions(-) create mode 100644 app/controls/login.php create mode 100644 app/templates/part/loginForm.phtml create mode 100644 app/templates/part/loginOta.phtml create mode 100644 app/vendor/GoogleAuthenticator.php diff --git a/app/class/UserManager.php b/app/class/UserManager.php index c3b7864..3ea2ff8 100644 --- a/app/class/UserManager.php +++ b/app/class/UserManager.php @@ -29,9 +29,9 @@ class UserManager setcookie ("rememberMe", $this->setEncryptedCookie($user['username']), time () + (30 * 24 * 60 * 60 * 1000), BASEDIR, $_SERVER['HTTP_HOST'], 1); } $_SESSION['user']['id'] = $user['user_id']; - $page = "./index.php"; + $page = "home"; if ($user["startPage"] == 1) { - $page = "./dashboard.php"; + $page = "dashboard"; } unset($_POST['login']); return $page; @@ -101,12 +101,14 @@ class UserManager return false; } - public static function getUserData ($type) { + public static function getUserData ($type, $userId = '') { if (isset($_SESSION['user']['id'])) { - $user = Db::loadOne ('SELECT ' . $type . ' FROM users WHERE user_id=?', array ($_SESSION['user']['id'])); - return $user[$type]; + $userId = $_SESSION['user']['id']; + } else { + return ""; } - return ""; + $user = Db::loadOne ('SELECT ' . $type . ' FROM users WHERE user_id=?', array ($userId)); + return $user[$type]; } public function setUserData ($type, $value) { @@ -121,94 +123,54 @@ class UserManager return $hashPassword; } - public function ulozitObrazek ($file, $path = "", $name = "") { - if (!@is_array (getimagesize($file['tmp_name']))) { - throw new ChybaUzivatele("Formát obrázku ". $file['name'] ." není podporován!"); - } else { - $extension = strtolower(strrchr($file['name'], '.')); - switch ($extension) { - case '.jpg': - case '.jpeg': - $img = @imagecreatefromjpeg($file['tmp_name']); - break; - case '.gif': - $img = @imagecreatefromgif($file['tmp_name']); - break; - case '.png': - $img2 = @imagecreatefrompng($file['tmp_name']); - break; - case '.ico': - $img3 = @$file['tmp_name']; - break; - default: - $img = false; - break; - } - if($name == ""){ - $nazev = substr($file['name'], 0, strpos($file['name'], ".")) ."_". round(microtime(true) * 1000); - }else{ - $nazev = $name; - } - if(!file_exists($path)){ - mkdir($path, 0777, true); - } - if (@$img) { - if (!imagejpeg ($img, $path . $nazev .".jpg", 95)) { - throw new ChybaUzivatele ("Obrázek neuložen!"); - } - imagedestroy ($img); - } else if (@$img2) { - if (!imagepng ($img2, $path . $nazev .".jpg")) { - throw new ChybaUzivatele ("Obrázek neuložen!"); - } - imagedestroy ($img2); - } else if (@$img3) { - if (!copy($img3, $path . $nazev .'.ico')) { - throw new ChybaUzivatele ("Obrázek neuložen!"); - } - } - return array('success' => true, 'url' => $path . $nazev .".jpg"); - } - } - - public function atHome($userId, $atHome){ - try { - Db::edit ('users', ['at_home' => $atHome], 'WHERE user_id = ?', array($userId)); - } catch(PDOException $error) { - echo $error->getMessage(); - die(); - } - } - - public function changePassword($oldPassword, $newPassword, $newPassword2){ - if ($newPassword == $newPassword2) { - //Password Criteria - $oldPasswordSaved = self::getUserData('password'); - if (self::getHashPassword($oldPassword) == $oldPasswordSaved) { - self::setUserData('password', self::getHashPassword($newPassword)); - } else { - throw new Exception ("old password did not match"); - } - } else { - throw new Exception ("new password arent same"); - } - } - - public function createUser($userName, $password){ - $userId = Db::loadOne('SELECT * FROM users WHERE username = ?;', array($userName))['user_id']; - if ($userId != null) { - return false; - }; - try { - $user = [ - 'username' => $userName, - 'password' => self::getHashPassword($password), - ]; - return Db::add ('users', $user); - } catch(PDOException $error) { - echo $error->getMessage(); - die(); - } + public function atHome($userId, $atHome){ + try { + Db::edit ('users', ['at_home' => $atHome], 'WHERE user_id = ?', array($userId)); + } catch(PDOException $error) { + echo $error->getMessage(); + die(); } } - ?> + + public function changePassword($oldPassword, $newPassword, $newPassword2){ + if ($newPassword == $newPassword2) { + //Password Criteria + $oldPasswordSaved = self::getUserData('password'); + if (self::getHashPassword($oldPassword) == $oldPasswordSaved) { + self::setUserData('password', self::getHashPassword($newPassword)); + } else { + throw new Exception ("old password did not match"); + } + } else { + throw new Exception ("new password arent same"); + } + } + + public function createUser($userName, $password){ + $userId = Db::loadOne('SELECT * FROM users WHERE username = ?;', array($userName))['user_id']; + if ($userId != null) { + return false; + }; + try { + $user = [ + 'username' => $userName, + 'password' => self::getHashPassword($password), + ]; + return Db::add ('users', $user); + } catch(PDOException $error) { + echo $error->getMessage(); + die(); + } + } + + public function haveOtaEnabled($userName){ + $ota = $this->getUser($userName)['ota']; + + if ($ota != ''){ + return ($ota != '' ? $ota : false); + } else { + return false; + } + } +} +?> diff --git a/app/controls/login.php b/app/controls/login.php new file mode 100644 index 0000000..57d306d --- /dev/null +++ b/app/controls/login.php @@ -0,0 +1,43 @@ +haveOtaEnabled($userName); + + $_SESSION['USERNAME'] = $userName; + $_SESSION['PASSWORD'] = $userPassword; + $_SESSION['OTA'] = $ota; +} else if ( + isset($_POST['otaCode']) && + $_POST['otaCode'] != '' +) { + + $otaCode = $_POST['otaCode']; + $otaSecret = $_POST['otaSecret']; + + $ga = new PHPGangsta_GoogleAuthenticator(); + $ota = $_SESSION['OTA']; + $userName = $_SESSION['USERNAME']; + $userPassword = $_SESSION['PASSWORD']; + unset($_SESSION['OTA']); + $checkResult = $ga->verifyCode($otaSecret, $otaCode, 6); // 2 = 2*30sec clock tolerance + if ($checkResult) { + $landingPage = $userManager->login($userName, $userPassword); + header('Location: ' . BASEDIR . $landingPage); + echo 'OK'; + } else { + echo 'FAILED'; + } + //TODO: upravi a ověřit jeslti ja zabezpečené + //TODO: + die(); +} diff --git a/app/templates/login.phtml b/app/templates/login.phtml index 6c37b8b..ae0636b 100644 --- a/app/templates/login.phtml +++ b/app/templates/login.phtml @@ -8,26 +8,16 @@ <?php echo $TITLE ?> - + prepare('ota',$ota); + $partial->render(); + } else { + $partial = new Partial('loginForm'); + $partial->render(); + } + ?> + + diff --git a/app/templates/part/loginOta.phtml b/app/templates/part/loginOta.phtml new file mode 100644 index 0000000..3e8d294 --- /dev/null +++ b/app/templates/part/loginOta.phtml @@ -0,0 +1,13 @@ + diff --git a/app/templates/setting.phtml b/app/templates/setting.phtml index 1f7c808..0b11493 100644 --- a/app/templates/setting.phtml +++ b/app/templates/setting.phtml @@ -76,6 +76,13 @@ +
+

+ +
+ +
+

echo('t_createuser') ?>

diff --git a/app/vendor/GoogleAuthenticator.php b/app/vendor/GoogleAuthenticator.php new file mode 100644 index 0000000..bf7d116 --- /dev/null +++ b/app/vendor/GoogleAuthenticator.php @@ -0,0 +1,252 @@ +_getBase32LookupTable(); + + // Valid secret lengths are 80 to 640 bits + if ($secretLength < 16 || $secretLength > 128) { + throw new Exception('Bad secret length'); + } + $secret = ''; + $rnd = false; + if (function_exists('random_bytes')) { + $rnd = random_bytes($secretLength); + } elseif (function_exists('mcrypt_create_iv')) { + $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong); + if (!$cryptoStrong) { + $rnd = false; + } + } + if ($rnd !== false) { + for ($i = 0; $i < $secretLength; ++$i) { + $secret .= $validChars[ord($rnd[$i]) & 31]; + } + } else { + throw new Exception('No source of secure random'); + } + + 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 + * @param array $params + * + * @return string + */ + public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array()) + { + $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200; + $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200; + $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M'; + + $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.''); + if (isset($title)) { + $urlencoded .= urlencode('&issuer='.urlencode($title)); + } + + return "https://api.qrserver.com/v1/create-qr-code/?data=$urlencoded&size=${width}x${height}&ecc=$level"; + } + + /** + * 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); + } + + if (strlen($code) != 6) { + return false; + } + + for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { + $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); + if ($this->timingSafeEquals($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; + } + + /** + * 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 + ); + } + + /** + * A timing safe equals comparison + * more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html. + * + * @param string $safeString The internal (safe) value to be checked + * @param string $userString The user submitted (unsafe) value + * + * @return bool True if the two strings are identical + */ + private function timingSafeEquals($safeString, $userString) + { + if (function_exists('hash_equals')) { + return hash_equals($safeString, $userString); + } + $safeLen = strlen($safeString); + $userLen = strlen($userString); + + if ($userLen != $safeLen) { + return false; + } + + $result = 0; + + for ($i = 0; $i < $userLen; ++$i) { + $result |= (ord($safeString[$i]) ^ ord($userString[$i])); + } + + // They are only identical strings if $result is exactly 0... + return $result === 0; + } +} diff --git a/index.php b/index.php index 5dc8647..e10f582 100644 --- a/index.php +++ b/index.php @@ -13,7 +13,7 @@ mb_internal_encoding ("UTF-8"); //Autoloader -foreach (["class", "views"] as $dir) { +foreach (["vendor","class", "views"] as $dir) { $files = scandir('./app/'.$dir.'/'); $files = array_diff($files, array('.', '..', 'app')); @@ -47,9 +47,10 @@ Db::connect (DBHOST, DBUSER, DBPASS, DBNAME); //TODO: Přesunout do Login Pohledu $userManager = new UserManager(); -if (isset($_POST['username']) && isset($_POST['password']) ) { - $userManager->login($_POST['username'], $_POST['password'], (isset ($_POST['remember']) ? $_POST['remember'] : 'false')); -} + +// if (isset($_POST['username']) && isset($_POST['password']) ) { +// $userManager->login($_POST['username'], $_POST['password'], (isset ($_POST['remember']) ? $_POST['remember'] : 'false')); +// } //Logs $logManager = new LogManager();