File: /var/www/vhosts/creativefellows.nl/jhtaxatie.creativefellows.nl/classes/LoginController.php
<?php
class LoginController {
private $password_hash;
var $passwordFields = array(
array("userid", "userid", "hidden", "userid"),
array("redirect", "redirect", "hidden", "redirect"),
array("current", "Huidig wachtwoord", "password", "Huidig wachtwoord",null,null,null,true,null),
array("hash", "Nieuw wachtwoord", "password", "Voer wachtwoord in",null,null,null,true,null,array("not_equal_to","current")),
array("hash_repeat","Herhaal wachtwoord", "password", "Herhaal wachtwoord",null,null,null,true,"hash")
);
public function __construct($settings,$db,$router,$view,$logger)
{
$this->settings = $settings;
$this->db = $db;
$this->router = $router;
$this->view = $view;
$this->logger = $logger;
}
protected function getRouter()
{
return $this->router;
}
public function showLogin($request,$response)
{
$data = array(
'router' => $this->getRouter(),
'loginError' => $request->getAttribute('loginError'),
'loginAttemps' => $request->getAttribute('loginAttemps')
);
return $this->view->render($response, 'login.php',$data);
}
public function showReset($request,$response)
{
$data = array(
'router' => $this->getRouter(),
'loginError' => $request->getAttribute('loginError'),
'loginAttemps' => $request->getAttribute('loginAttemps')
);
return $this->view->render($response, 'reset.php',$data);
}
public function resetPassword($request,$response)
{
// get form data
$post = $request->getParsedBody();
$email = $post["email"];
$sth = $this->db->prepare("SELECT * FROM users WHERE email=:email AND status = 1");
$sth->execute(array(
"email" => $email
));
// query results
$user_data = $sth->fetch();
if($user_data){
/*
* Set user
*/
$user = new JhUser( $user_data );
/*
* new email
*/
$email = new TaxationEmail();
/*
* get reset key
*/
$resetKey = $this->addLoginKey( $user->getUserId() );
/*
* reset url
*/
$reset_url = $this->getSiteUrl() . $this->router->pathFor('password.new',['key'=> $resetKey]);
/*
* set email template
*/
$email->setTemplate("resetpassword",["/{advisor}/" => $user->getFullName(), "/{route}/" => $reset_url]);
$email->setSubject("Wachtwoord herstellen");
$email->setReceiver( $user->getEmailAddress() );
$email->setFromAddress( $this->getDefaultToEmail() );
$email->sendEmail();
//echo $email->getBody();
$request = $request->withAttribute('loginError','E-mail verstuurd');
return $this->showLogin($request,$response);
}
$attempts = $request->getAttribute('loginAttemps');
// Set error to response
$request = $request->withAttribute('loginError','Wachtwoord reset mislukt');
$request = $request->withAttribute('loginAttemps',$attempts);
return $this->showReset($request,$response);
}
public function viewResetForm($request,$response,$args)
{
// validate key
$key_is_valid = $this->validateKey($args["key"]);
// key not valid
if($key_is_valid === false)
{
$request = $request->withAttribute('loginError','Url niet geldig');
return $this->showReset($request,$response);
}
else{
$request = $request->withAttribute('userid',$this->user_id);
$request = $request->withAttribute('key',$args["key"]);
return $this->newPassword($request,$response,$args);
}
}
public function newPassword($request,$response,$args)
{
$password_fields = $this->getPasswordFields();
unset($password_fields[1]);
unset($password_fields[2]);
$data = array(
'router' => $this->getRouter(),
'userid' => $request->getAttribute('userid'),
'fields' => $password_fields,
'key' => $args["key"]
);
return $this->view->render($response, 'new-password.php',$data);
}
public function updatePassword($request,$response,$args)
{
$post = $request->getParsedBody();
$request = $request->withAttribute('loginError',$post['message']);
//$request = $request->withAttribute('redirect',$post['redirect']);
$this->patchKeyStatus($post['key'],0);
$this->patchPassword($request,$response,$args);
}
private function patchKeyStatus($key,$status)
{
$query = "UPDATE password_reset SET status = :status WHERE code = :key";
$sth = $this->db->prepare($query);
$sth->execute(array(
"status" => $status,
"key" => $key
));
}
private function validateKey($key)
{
$sth = $this->db->prepare("SELECT * FROM password_reset WHERE code = :code AND valid_until > :currenttime AND status = 1");
$sth->execute(array(
"code" => $key,
"currenttime" => date('Y-m-d h:m:s')
));
$data = $sth->fetch();
$this->user_id = $data["user_id"];
return $sth->rowCount() == 1 ? true : false;
}
private function addLoginKey($user_id)
{
$resetKey = $this->randomString();
$sth = $this->db->prepare("INSERT INTO password_reset (user_id,code,valid_until) values (:user_id,:code,:valid_until)");
$sth->execute(array(
"user_id" => $user_id,
"code" => $resetKey,
"valid_until" => date('Y-m-d h:m:s', strtotime('+1 day'))
));
return $resetKey;
}
private function randomString()
{
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
$pass = array(); //remember to declare $pass as an array
$alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
for ($i = 0; $i < 50; $i++) {
$n = rand(0, $alphaLength);
$pass[] = $alphabet[$n];
}
return implode($pass);
}
public function getDefaultToEmail()
{
return $this->settings['default_to_email'];
}
public function getSiteUrl()
{
return $this->settings['siteUrl'];
}
public function setAdminLogin($request,$response,$args)
{
$user_id = $args["id"];
$post_hash = $args["hash"];
// check referere
$this->validReferer($request,$response,$args);
$sth = $this->db->prepare("SELECT * FROM users WHERE user_id=:id AND status = 1 AND password_valid_until > NOW()");
$sth->execute(array(
"id" => $user_id
));
// query results
$user_data = $sth->fetch();
$hash_check = hash("sha256",$user_data["hash"]);
// login is ok
if($sth->rowCount() == 1 && $hash_check == $post_hash)
{
$user_permissions = $this->getPermission($user_data["role"]);
$_SESSION['user_id'] = $user_data["user_id"];
$_SESSION['role'] = $user_data["role"];
$_SESSION['full_name'] = $user_data["full_name"];
$_SESSION['reset_password'] = $user_data["password_valid_until"] < date("Y-m-d") ? true : false;
$_SESSION['user_access'] = $user_permissions;
$_SESSION['home_page'] = "/".$user_permissions[0]["route"];
$user_browser = $_SERVER['HTTP_USER_AGENT'];
$login_str = hash('sha512',$password . $user_browser);
$this->user_id = $user_data["user_id"];
/*
* Login succesfull
*/
$this->logger->addInfo("Admin Login ok", array('username' => $user_data['username'], "IP" => $_SERVER["REMOTE_ADDR"]));
/*
* Login hash
*/
$_SESSION['session_id'] = $login_str;
//return $response->withHeader('Location',"/");
/*
* Redirect to homepage
*/
return $response->withHeader('Location',$_SESSION['home_page']);
}
else{
return $response->withHeader('Location',$this->router->pathFor("admin.viewuser",["id" => $user_id,"msg"=>"Inloggen+mislukt"]));
}
}
private function validReferer($request,$response,$args){
//echo $_SERVER['HTTP_REFERER'];
//echo '<br />' . $this->settings["siteUrl"] ;
if( strpos($_SERVER['HTTP_REFERER'], $this->settings["siteUrl"]) === false) die("bad request");
}
public function setLogin($request,$response)
{
/*
* Get post data
*/
$postData = $request->getParsedBody();
//die("do captcha");
/*
* Csrf protection
*/
if(isset($postData["csrf"]) && $postData["csrf"] != $_SESSION["csrf_token"])
{
return $this->failedLogin($request,$response);
}
/*
* Check if user is blocked
*/
$is_blocked = $this->confirmIPAddress($_SERVER["REMOTE_ADDR"]);
/*
* Post has a google Captcha
if($this->loginAttempt() > 0)
{
$recaptcha = new \ReCaptcha\ReCaptcha($this->settings['captcha_secret']);
$resp = $recaptcha->verify($postData["g-recaptcha-response"], $_SERVER['REMOTE_ADDR']);
//print_r($resp);
/*
* Captcha not verfied
if (!$resp->isSuccess())
{
// Update login attempts on IP
$attempts = $this->addLoginAttempt($_SERVER["REMOTE_ADDR"]);
// Set attempts to request
$request = $request->withAttribute('loginAttemps',$attempts);
// Log failed login
$this->logger->addInfo("Login failed (captcha)", array('username' => $postData['username'], "IP" => $_SERVER["REMOTE_ADDR"]));
// Return to login page
return $this->failedLogin($request,$response);
}
} */
/*
* User is blocked on IP address
*/
if($is_blocked == 1)
{
$request = $request->withAttribute('loginError','Geen toegang voor 10 minuten');
$request = $request->withAttribute('loginAttemps',0);
return $this->showLogin($request,$response);
}
/*
* Verify post login data
*/
$user = $this->getLogin($postData['username'],$postData['password']);
/*
* Check if there is a redirect in the post
*/
$redirect = $postData['redirect'] != null ? $postData['redirect'] : "/".$_SESSION['home_page'];
/*
* Login failed
*/
if (empty($user)) {
// Update login attempts on IP
$attempts = $this->addLoginAttempt($_SERVER["REMOTE_ADDR"]);
// Set attempts to request
$request = $request->withAttribute('loginAttemps',$attempts);
// Log failed login
$this->logger->addInfo("Login failed", array('username' => $postData['username'],"IP" => $_SERVER["REMOTE_ADDR"]));
// Return to login page
return $this->failedLogin($request,$response);
}
/*
* Login succesfull
*/
$this->logger->addInfo("Login ok", array('username' => $postData['username'], "IP" => $_SERVER["REMOTE_ADDR"]));
/*
* Login hash
*/
$_SESSION['session_id'] = $user;
/*
* Prompt reset password
*/
if($_SESSION['reset_password'] === true)
{
$request = $request->withAttribute('userid',$this->user_id);
$request = $request->withAttribute('redirect',$redirect);
return $this->getPassword($request,$response);
}
/*
* Redirect to homepage
*/
return $response->withHeader('Location',$redirect);
}
public function getPassword($request,$response,$args=null)
{
$data = array(
'router' => $this->router,
'userid' => $request->getAttribute('userid'),
'redirect' => $request->getAttribute('redirect'),
'fields' => $this->getPasswordFields()
);
return $this->view->render($response, 'reset-password.php',$data);
}
private function getPasswordFields()
{
$elements = [];
foreach( $this->passwordFields as $groupName => $field)
{
$elements[] = new HtmlElement($field);
}
return $elements;
}
public function patchPassword($request,$response)
{
// get form data
$post = $request->getParsedBody();
// update data
$sth = $this->db->prepare(
"UPDATE users
SET
hash = :hash,
password_valid_until = :valid_until
WHERE user_id = :userid"
);
// execure query
$sth->execute(
array(
"hash" => password_hash($post["hash"],PASSWORD_BCRYPT),
"valid_until" => date('Y-m-d', strtotime("+3 months", strtotime(date("Y-m-d")))),
"userid" => $post["userid"]
)
);
$_SESSION['reset_password'] = false;
$this->showLogin($request,$response);
//return $response->withRedirect( $post["redirect"] );
}
private function failedLogin($request,$response)
{
// Clear the session data
$_SESSION['session_id'] = null;
$_SESSION['user_id'] = null;
$_SESSION['role'] = null;
$attempts = $request->getAttribute('loginAttemps');
// Set error to response
$request = $request->withAttribute('loginError','Inloggen mislukt');
$request = $request->withAttribute('loginAttemps',$attempts);
// return the login view
return $this->showLogin($request,$response);
}
private function loginAttempt()
{
return $this->currentAttempt;
}
private function confirmIPAddress($ipaddress)
{
$query = "SELECT attempts, (CASE when lastlogin is not NULL and DATE_ADD(lastlogin, INTERVAL 10 MINUTE) > NOW() then 1 else 0 end) as denied FROM login_attempts WHERE ip = :ipaddr";
$sth = $this->db->prepare($query);
$sth->execute(array(
"ipaddr" => $ipaddress
));
$data = $sth->fetch();
//Verify that at least one login attempt is in database
if (!$data) {
$this->currentAttempt = 0;
return 0;
}
// set current login attempt
$this->currentAttempt = $data["attempts"];
// check if user has been blocked
if ($data["attempts"] >= 5)
{
if($data["denied"] == 1) return 1;
else
{
$this->clearLoginAttempts($ipaddress);
return 0;
}
}
return 0;
}
private function clearLoginAttempts($ipaddress)
{
$query = "UPDATE login_attempts SET attempts = 0 WHERE ip = :ipaddr";
$sth = $this->db->prepare($query);
$sth->execute(array(
"ipaddr" => $ipaddress
));
}
private function addLoginAttempt($ipaddress)
{
$sth = $this->db->prepare("SELECT * FROM login_attempts WHERE ip = :ipaddr");
$sth->execute(array(
"ipaddr" => $ipaddress
));
$data = $sth->fetch();
if($data)
{
$attempts = $data["attempts"]+1;
if($attempts == 3){
$q = "UPDATE login_attempts SET attempts=". $attempts .", lastlogin=NOW() WHERE ip = :ipaddr";
}
else {
$q = "UPDATE login_attempts SET attempts=". $attempts ." WHERE ip = :ipaddr";
}
}
else{
$q = "INSERT INTO login_attempts (attempts,IP,lastlogin) values (1,:ipaddr,NOW())";
$attempts = 1;
}
$sth = $this->db->prepare($q);
$sth->execute(array(
"ipaddr" => $ipaddress
));
return $attempts;
}
private function getLogin($username,$password)
{
//die()
//die( password_hash($user_data["hash"],PASSWORD_BCRYPT) );
$sth = $this->db->prepare("SELECT * FROM users WHERE username=:username AND status = 1");
$sth->execute(array(
"username" => $username
));
// query results
$user_data = $sth->fetch();
//echo $password;
//echo '<br />';
//die( password_hash($password,PASSWORD_BCRYPT) );
// login is ok
if($sth->rowCount() == 1 && password_verify($password, $user_data['hash']))
{
$user_permissions = $this->getPermission($user_data["role"]);
$routes_sorted = $user_permissions;
$_SESSION['user_id'] = $user_data["user_id"];
$_SESSION['role'] = $user_data["role"];
$_SESSION['full_name'] = $user_data["full_name"];
$_SESSION['reset_password'] = $user_data["password_valid_until"] < date("Y-m-d") ? true : false;
$_SESSION['user_access'] = $user_permissions;
$first_route = reset($routes_sorted);
$_SESSION['home_page'] = $first_route["route"];
$user_browser = $_SERVER['HTTP_USER_AGENT'];
$login_str = hash('sha512',$password . $user_browser);
$this->user_id = $user_data["user_id"];
return $login_str;
}
else return;
}
private function getPermission($role_id)
{
$sth = $this->db->prepare("SELECT * FROM role_permissions RIGHT JOIN app_routes ON role_permissions.route_id = app_routes.route_id WHERE role_id = :role_id OR app_routes.static = 1 ORDER BY app_routes.position ASC");
$sth->execute(array(
"role_id" => $role_id
));
$rows = $sth->fetchAll();
$new = [];
foreach($rows as $i => $route)
{
$route["subs"] = [];
if($route["sub_of"] == 0) $new[$route["route_id"]] = $route;
else{
$new[ $route["sub_of"] ]["subs"][] = $route;
}
}
return $new;
}
public function setLogoff($request,$response)
{
unset($_SESSION['session_id']);
unset($_SESSION['user_id']);
unset($_SESSION['role']);
//$response->withHeader('Location','/');
return $response->withRedirect("/");
}
}
?>