/core/XAuthentication.class.php

https://github.com/Xirt/XirtCMS · PHP · 246 lines · 117 code · 64 blank · 65 comment · 22 complexity · 5d5773e78bb87ad8ccd51949ccbeaa51 MD5 · raw file

  1. <?php
  2. /**
  3. * Utility Class to handle authentication procedures
  4. *
  5. * @author A.G. Gideonse
  6. * @version 2.0
  7. * @copyright XirtCMS 2010 - 2014
  8. * @package XirtCMS
  9. */
  10. class XAuthentication {
  11. /**
  12. * Returns the userId of the visitor
  13. *
  14. * @return mixed Returns the current userId or null on failure
  15. */
  16. public static function getUserId() {
  17. if (isset($_SESSION["id"])) {
  18. return intval($_SESSION["id"]);
  19. }
  20. if (isset($_COOKIE["id"])) {
  21. return intval($_COOKIE["id"]);
  22. }
  23. return null;
  24. }
  25. /**
  26. * Checks whether the current visiter is authenticated or not
  27. *
  28. * @return mixed Return true if visiter is authenticated, false otherwhise
  29. */
  30. public static function check() {
  31. $ip = $_SERVER["REMOTE_ADDR"];
  32. $uId = isset($_COOKIE["id"]) ? $_COOKIE["id"] : null;
  33. $uId = isset($_SESSION["id"]) ? $_SESSION["id"] : $uId;
  34. $pass = isset($_COOKIE["pass"]) ? $_COOKIE["pass"] : null;
  35. $pass = isset($_SESSION["pass"]) ? $_SESSION["pass"] : $pass;
  36. $hash = isset($_COOKIE["hash"]) ? $_COOKIE["hash"] : null;
  37. $hash = isset($_SESSION["hash"]) ? $_SESSION["hash"] : $hash;
  38. $authStr = $ip . $uId . $pass . XConfig::get("AUTH_SECRET");
  39. $candidate = hash(XConfig::get("AUTH_HASH_TYPE"), $authStr);
  40. return ($candidate == $hash);
  41. }
  42. /**
  43. * Attempts to authenticate the current visitor
  44. *
  45. * @param $username The username of the user to authenticate
  46. * @param $password The password of the user to authenticate
  47. * @param $cookies Toggles the use of cookies (defaults to false)
  48. * @return int 1 on success, error code otherwise
  49. */
  50. public static function create($username, $password, $cookies = false) {
  51. global $xDb;
  52. XAuthentication::destroy();
  53. session_regenerate_id();
  54. if (!($out = XAuthentication::_verify($username, $password)) || $out < 1) {
  55. self::_track($username);
  56. return $out;
  57. }
  58. $password = self::hash($password);
  59. $ip = $_SERVER["REMOTE_ADDR"];
  60. $authStr = $ip . $out . $password . XConfig::get("AUTH_SECRET");
  61. $hash = hash(XConfig::get("AUTH_HASH_TYPE"), $authStr);
  62. if ($cookies) {
  63. setcookie("id" , $out , time() + 31536000);
  64. setcookie("pass" , $password, time() + 31536000);
  65. setcookie("hash" , $hash , time() + 31536000);
  66. }
  67. $_SESSION["id"] = $out;
  68. $_SESSION["pass"] = $password;
  69. $_SESSION["hash"] = $hash;
  70. self::_track($username, true);
  71. return 1;
  72. }
  73. /**
  74. * Update account to track (failed) login attempts
  75. *
  76. * @param $username The username of the account to update
  77. * @param $resetCounter Toggles resetting of the tracker (default: false)
  78. */
  79. private static function _track($username, $resetCounter = false) {
  80. global $xDb;
  81. if ($resetCounter) {
  82. // Reset the counter (login success)
  83. $query = "UPDATE #__users " .
  84. "SET dt_login = NOW(), " .
  85. " login_attempts = 0 " .
  86. "WHERE username LIKE BINARY :username ";
  87. } else {
  88. // Update the counter (login failure)
  89. $query = "UPDATE #__users " .
  90. "SET dt_login = NOW(), " .
  91. " login_attempts = login_attempts + 1 " .
  92. "WHERE username LIKE BINARY :username ";
  93. }
  94. // Query execution
  95. $stmt = $xDb->prepare($query);
  96. $stmt->bindParam(":username", $username, PDO::PARAM_STR);
  97. $stmt->execute();
  98. }
  99. /**
  100. * Destroys authentication
  101. *
  102. * @return boolean true
  103. */
  104. public static function destroy() {
  105. setcookie("id" , 0, time() - 3600);
  106. setcookie("pass", 0, time() - 3600);
  107. setcookie("hash", 0, time() - 3600);
  108. unset($_SESSION["id"]);
  109. unset($_SESSION["pass"]);
  110. unset($_SESSION["hash"]);
  111. return true;
  112. }
  113. /**
  114. * Verifies a username / password combination
  115. *
  116. * @param $username String containing the username
  117. * @param $password String containing the password
  118. * @return int User ID on success, error code otherwise
  119. */
  120. protected static function _verify($username, $password) {
  121. global $xDb;
  122. if (!XValidate::isSimpleChars($username)) {
  123. return -1;
  124. }
  125. // Database query
  126. $query = "SELECT id, yubikey, password, salt, dt_login, login_attempts " .
  127. "FROM #__users " .
  128. "WHERE username LIKE BINARY :username ";
  129. // Data retrieval
  130. $stmt = $xDb->prepare($query);
  131. $stmt->bindParam(":username", $username, PDO::PARAM_STR);
  132. $stmt->execute();
  133. if (!$auth = $stmt->fetch(PDO::FETCH_OBJ)) {
  134. return -1;
  135. }
  136. /*******************
  137. * LOCKED ACCOUNTS *
  138. *******************/
  139. $timeConstraint = time() - strtotime($auth->dt_login) < XConfig::get("AUTH_LOCKTIME");
  140. $countConstraint = $auth->login_attempts >= XConfig::get("AUTH_MAX_ATTEMPTS");
  141. if ($timeConstraint && $countConstraint) {
  142. return -2;
  143. }
  144. /**************************
  145. * METHOD 1 :: DB Details *
  146. **************************/
  147. if (self::hash($password, $auth->salt) == $auth->password) {
  148. return $auth->id;
  149. }
  150. /***********************
  151. * METHOD 2 :: YUBIKEY *
  152. ***********************/
  153. if (XConfig::get("AUTH_YUBI_ENABLED") && $auth->yubikey) {
  154. if (XConfig::get("AUTH_YUBI_API") && XConfig::get("AUTH_YUBI_KEY")) {
  155. $yubikey = new Yubikey(XConfig::get("AUTH_YUBI_API"), XConfig::get("AUTH_YUBI_KEY"));
  156. if ($yubikey->verify($password)) {
  157. return $auth->id;
  158. }
  159. } else {
  160. trigger_error("XAuthentication :: Yubikey not configured", E_USER_WARNING);
  161. }
  162. }
  163. return 0;
  164. }
  165. /**
  166. * Generates a random hash based on the given password and salt
  167. *
  168. * @param $password The password to use for the hash
  169. * @param $salt The salt to use for the hash (optional)
  170. * @return String The generated hash
  171. */
  172. public static function hash($password, $salt = null) {
  173. if (!CRYPT_BLOWFISH) {
  174. trigger_error("XAuthentication :: Blowfish unavailable in crypt()", E_USER_ERROR);
  175. }
  176. if (is_null($salt)) {
  177. $salt = substr(md5(uniqid(rand(), true)), 0, 21);
  178. }
  179. return crypt($password, "$2a$08$" . $salt . '$');
  180. }
  181. }
  182. ?>