* */ /* * Includes */ require("lmStates.php"); require("lmConfig.php"); require("lmHandler.php"); require("lmPassword.php"); require("lmTwoFactor.php"); require("lmUtils.php"); /* * Class */ class loginManager{ //constructor /** * building... * @param lmConfig $_config configuration for login Manager * @param lmHandler $_eventHandler handler of events * @param lmPassword $_passwordEngine engine for verifying passwords * @return void */ public function __construct($_config, $_eventHandler, $_passwordEngine, $_twoFactor){ $this->config=$_config; $this->eventHandler=$_eventHandler; $this->passwordEngine=$_passwordEngine; $this->twoFactor=$_twoFactor; return; } //settings private $config; private $eventHandler; private $passwordEngine; private $twoFactor; //frontend functions /** * initialize session and set its lifetime * @return bool */ public function init(){ session_set_cookie_params($this->config->getSessionLifetime()); return session_start(); } /** * prepare for login. Run this on the top of your login page! * @return void */ public function loginPrepare(){ $this->passFailedAttempts(); return; } /** * lets start here! * @param int/string @identifier id or username of user * @param string @password cleartext password from input * @param bool $remember save user fot further use * @return void */ public function login($identifier, $password, $remember=false){ global $lm_force_captcha; if($this->passFailedAttempts()){ //not banned if(isset($lm_force_captcha)){ //check captcha if(!isset($_POST['g-recaptcha-response'])){ $captcha_failed=true; $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); $this->eventHandler->handle(lmStates::CAPTCHA_FAILED); return; } else{ if(!lmUtils::validateCaptcha($this->config->getCaptchaSecretkey(), $_POST['g-recaptcha-response'])){ $captcha_failed=true; $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); $this->eventHandler->handle(lmStates::CAPTCHA_FAILED); return; } } } if(!isset($captcha_failed)){ if($this->config->isRememberEnabled()){ //check if remembering is enabled if($this->isRememberingUser() && $this->twoFactor->secondFactor($this->isRememberingUser())){ //remembering. $this->permitLogin($this->isRememberingUser()); //good to go! return; } } //proceed with normal login if($this->config->getAuthType()==lmStates::AUTH_UNAME){ //username based authentication $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count, id, password FROM users WHERE username=:identifier and id<>1"); } else{ $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count, id, password FROM users WHERE id=:identifier and id<>1"); } $sql->execute(array(":identifier"=>$identifier)); $res=$sql->fetch(PDO::FETCH_ASSOC); if($res['count']==0){ //user not existing $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); $this->eventHandler->handle(lmStates::LOGIN_FAILED); return; } else{ if($this->passwordEngine->verifyPassword($password, $res['password']) && $this->twoFactor->secondFactor($res['id'])){ if($this->config->isRememberEnabled()){ //remember... if he wants to be insecure if($remember){ $this->rememberUser($res['id']); } } $this->permitLogin($res['id']); //good to go! return; } else{ $this->addLoginHistory($res['id'], lmStates::LOGIN_FAILED); $this->eventHandler->handle(lmStates::LOGIN_FAILED); return; } } } } return; } /** * finish it up! * @return void */ public function logout(){ $_SESSION=array(); session_destroy(); setcookie("lm_login_random", NULL, -1); $this->eventHandler->handle(lmStates::LOGOUT_DONE); return; } /** * just some formal checking * @return bool */ public function validateLogin(){ if(!isset($_SESSION['lm_id'])){ return false; } else{ $sql=$this->config->getPDO()->prepare("SELECT auth_token FROM login_history WHERE user=:id and success=1 ORDER BY id DESC LIMIT 1"); $sql->execute(array(":id"=>$_SESSION['lm_id'])); $res=$sql->fetch(PDO::FETCH_ASSOC); if($res['auth_token']==$this->getSessionKey()){ return true; } else{ return false; } } } /** * do i know you? * @return int */ public function isRememberingUser(){ if(!$this->config->isRememberEnabled()){ return NULL; } if(is_null($this->getRememberKey())){ return NULL; } else{ $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count, user FROM login_remember WHERE remember_token=:token and until>:until"); $sql->execute(array(":token"=>$this->getRememberKey(), ":until"=>date("Y-m-d H:i:s"))); $res=$sql->fetch(PDO::FETCH_ASSOC); if($res['count']!=1){ $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); return NULL; } else{ return $res['user']; } } } /** * i don't know you anymore! * @return void */ public function forgetUser(){ $sql=$this->config->getPDO()->prepare("UPDATE login_remember SET until=0 WHERE remember_token=:token"); $sql->execute(array(":token"=>$this->getRememberKey())); setcookie("lm_login_remember", NULL, -1); $this->eventHandler->handle(lmStates::FORGET_DONE); return; } /** * print captcha html code if needed * @param bool $dark use the dark theme, default false * @return void */ public function printCaptcha($dark=false){ if($this->config->isCaptchaEnabled()){ global $lm_force_captcha; if(isset($lm_force_captcha)){ if($dark){ echo "
config->getCaptchaSitekey()."\" data-theme=\"dark\">
"; } else{ echo "
config->getCaptchaSitekey()."\">
"; } return; } else{ return; } } return; } //backend functions protected function generateSessionKey(){ $random=lmUtils::randomString(32); setcookie("lm_login_random", $random, time()+$this->config->getSessionLifetime()); $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$random); return $hash; } protected function getSessionKey(){ if(!isset($_COOKIE['lm_login_random'])){ return NULL; } else{ $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$_COOKIE['lm_login_random']); return $hash; } } protected function passFailedAttempts(){ //check if no limitations are enabled if(!$this->config->isCaptchaEnabled() && !$this->config->isBanEnabled()){ return true; //nothing to do } //check if is already banned if($this->config->isBanEnabled()){ $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count FROM login_bans WHERE id=:ip and until>:until"); $sql->execute(array(":ip"=>$_SERVER['REMOTE_ADDR'], ":until"=>date("Y-m-d H:i:s"))); $res=$sql->fetch(PDO::FETCH_ASSOC); if($res['count']!=0){ $this->eventHandler->handle(lmStates::BANNED); return false; } } //count failed attempts $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count FROM login_history WHERE ip=:ip and date>:date and success=0"); $sql->execute(array(":ip"=>$_SERVER['REMOTE_ADDR'], ":date"=>date("Y-m-d H:i:s", time()-$this->config->getLook()))); $res=$sql->fetch(PDO::FETCH_ASSOC); //force captcha if case if($res['count']>=$this->config->getCaptchaAfter() && $this->config->isCaptchaEnabled()){ global $lm_force_captcha; $lm_force_captcha=true; } //bann if case if($res['count']>=$this->config->getBanAfter() && $this->config->isBanEnabled()){ $sql=$this->config->getPDO()->prepare("INSERT INTO login_bans (ip, until) VALUES (:ip, :until)"); $sql->execute(array(":ip"=>$_SERVER['REMOTE_ADDR'], ":until"=>date("Y-m-d H:i:s", time()+$this->config->getBanTime()))); global $lm_banned; $lm_banned=true; $this->eventHandler->handle(lmStates::BANNED); return false; } return true; } protected function addLoginHistory($uid, $success=lmStates::LOGIN_FAILED, $token=""){ $sql=$this->config->getPDO()->prepare("INSERT INTO login_history (user, date, ip, auth_token, user_agent, success) VALUES (:user, :date, :ip, :auth_token, :user_agent, :success)"); $sql->execute(array(":user"=>$uid, ":date"=>date("Y-m-d H:i:s"), ":ip"=>$_SERVER['REMOTE_ADDR'], ":auth_token"=>$token, ":user_agent"=>$_SERVER['HTTP_USER_AGENT'], ":success"=>$success)); return; } protected function permitLogin($uid){ $token=$this->generateSessionKey(); $this->addLoginHistory($uid, lmStates::LOGIN_OK, $token); $_SESSION=array(); $_SESSION['lm_id']=$uid; $this->eventHandler->handle(lmStates::LOGIN_OK, $uid); return; } //functions for remembering protected function generateRememberKey(){ $random=lmUtils::randomString(32); setcookie("lm_login_remember", $random, time()+(86000*$this->config->getRememberTime())); $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$random); return $hash; } protected function getRememberKey(){ if(!isset($_COOKIE['lm_login_remember'])){ return NULL; } else{ $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$_COOKIE['lm_login_remember']); return $hash; } } protected function rememberUser($uid){ $sql=$this->config->getPDO()->prepare("INSERT INTO login_remember (user, remember_token, until) VALUES (:user, :token, :until)"); $sql->execute(array(":user"=>$uid, ":token"=>$this->generateRememberKey(), ":until"=>date("Y-m-d H:i:s", time()+(86400*$this->config->getRememberTime())))); return; } }