PaperBag/application/Auth.php

261 lines
8.6 KiB
PHP

<?php
class Auth {
private static $instance = null;
private $isLoggedIn = false;
private $userid = false;
private function __construct(){
session_start();
}
public static function getInstance(): Auth {
if(self::$instance == null){
self::$instance = new Auth();
}
return self::$instance;
}
public static function loginWithCredentials($email, $pass, bool $stayLoggedIn = false){
session_destroy();
self::$instance = null;
$Auth = Auth::getInstance();
$err = [];
if(strlen($email) < 3){
$err[] = __("Please enter a valid email.");
}
if(strlen($pass) < 3){
$err[] = __("Please enter a valid password. Hint: The passwords need to have at least 6 characters.");
}
if(!empty($err)){ return $err; }
// get user from database
$getUserSQL = "SELECT pwd, user_id, user_email FROM user WHERE user_email = ? LIMIT 1;";
$getUserRes = DB::query($getUserSQL, $email);
$dbPass = "missingPassword";
$dbUserId = 0;
$dbUserName = "Unknown";
if($getUserRes->num_rows == 1){
// Verify password
list($dbPass, $dbUserId, $dbUserName) = $getUserRes->fetch_row();
}
if(password_verify(PwdGen($pass), $dbPass)){
session_regenerate_id();
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
$userKey = GenKey();
$shaUserKey = sha1($userKey);
$expire = $stayLoggedIn?30:0; // if "stay logged in", stay logged in for 30 days.
$ctime = time();
$updateUserSQL = "INSERT INTO user_login (user_id, ckey, ctime, expire, agent) VALUES (?, ?, ?, ?, ?);";
try {
DB::query($updateUserSQL, $dbUserId, $userKey, $ctime, $expire, $md5agent);
$_SESSION['user_id'] = $dbUserId;
$_SESSION['user_name'] = $dbUserName;
$_SESSION['user_agent'] = $md5agent;
$_SESSION['user_key'] = $shaUserKey;
$_SESSION['user_ctime'] = $ctime;
if($stayLoggedIn){
setcookie("auth", $shaUserKey.".".$dbUserId, time() + (30 * 24*60*60), "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
}
return true;
}
catch(DatabaseException $e){
$err[] = __("Failed to login.")."\n".$e;
}
}
else {
$err[] = __("The provided combination of username and password is not recognized in the system. Verify your details or create an account.");
}
return $err;
}
public static function loginFromAuthKey(): bool {
if(isset($_COOKIE['auth'])){
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
$cookieSplode = explode('.', $_COOKIE['auth']);
if(count($cookieSplode) == 2){
$tkey = $cookieSplode[0];
$tuid = $cookieSplode[1];
$verifyLoginCookieRes = DB::query("
SELECT ul.user_id, ul.ckey, ul.ctime, ul.expire, ul.agent, u.full_name
FROM user_login ul
INNER JOIN user u ON ul.user_id = u.user_id
WHERE ul.user_id = ?
ORDER BY ctime DESC", $tuid);
while($row = $verifyLoginCookieRes->fetch_assoc()){
if($row['expire'] == 0){ $row['expire'] = 1; }
if(
sha1($row['ckey']) == $tkey &&
$row['agent'] == $md5agent &&
($row['ctime']+($row['expire']*24*60*60) > time())
){
session_regenerate_id();
$_SESSION['user_id'] = $row['user_id'];
$_SESSION['user_name'] = $row['full_name'];
$_SESSION['user_agent'] = $md5agent;
$_SESSION['user_key'] = sha1($row['ckey']);
return true;
}
}
}
}
return false;
}
public static function logout($everywhere = false){
if(Auth::checkLogin()){
$logoutSql = "DELETE FROM user_login WHERE user_id = ?";
$sqlparams[] = $_SESSION['user_id'];
if(!$everywhere){
// $logoutSql .= " AND agent = ?";
// $sqlparams[] = md5($_SERVER['HTTP_USER_AGENT']);
$logoutSql .= " AND ctime = ?";
$sqlparams[] = $_SESSION['user_ctime'];
}
DB::query($logoutSql, $sqlparams);
}
// Delete session-cookie
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
setcookie('auth', '', time() - 42000, "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
// Finally, destroy the session.
session_destroy();
}
public static function register($userEmail, $userPass, $metadata = []){
$err = [];
$userName = $metadata['newName'] ?? '';
if(strlen($userPass) < 6){
$err[] = __("Password is too short. Password needs to be at least 6 characters!");
}
else if(strlen($userPass) > 30){
$err[] = __("Password is too long. Max length is set to 30 characters. If you believe it should be higher please contact the developers.");
}
if(strlen($userEmail) > 220){
$err[] = __("Your email-address is too long. Can you please register with another email?");
}
if(strlen($userName) > 200){
$err[] = __("Your name seems to be too long to fit this system. Maybe you can short it down somehow?");
}
if(empty($err)){
// HASH PASSWORD
$newPass = PwdGen($userPass, true);
$createUserSQL = "INSERT INTO user SET user_email = ?, pwd = ?, full_name = ?;";
try {
DB::query($createUserSQL, $userEmail, $newPass, $userName);
$newID = DB::insert_id();
if(empty($newID)){ return false; }
$updateUserSQL = "UPDATE user SET md5_id = ? WHERE user_id = ?;";
DB::query($updateUserSQL, md5($newID), $newID);
return true;
} catch (DatabaseException $e) {
$err[] = __("Something went wrong").":<br>" . $e;
}
}
return $err;
}
public static function checkLogin($strict = false): bool {
$Auth = Auth::getInstance();
if(
($Auth->isLoggedIn && !$strict) ||
$Auth->checkSessionLogin() ||
$Auth->loginFromAuthKey()
) {
return true;
}
unset($_SESSION['user_id']);
return false;
}
private function checkSessionLogin(): bool {
if(isset($_SESSION['user_id'])){
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
if($md5agent == @$_SESSION['user_agent']){
$verifyLoginSessionRes = DB::query("
SELECT expire, ctime, ckey
FROM `user_login`
WHERE user_id = ?
AND agent = ?",
$_SESSION['user_id'], $md5agent);
while ($row = $verifyLoginSessionRes->fetch_assoc()){
if(
(sha1($row['ckey']) == $_SESSION['user_key']) &&
($row['expire'] == 0 || $row['ctime']+($row['expire']*24*60*60) > time())
){
return true;
}
}
}
}
return false;
}
public static function currentUserId(){
$a = Auth::getInstance();
return $_SESSION['user_id'];
}
}
function PwdGen($pass, $returnHashed = false): string {
$pepper = Config::get('security', 'pepper', 'IAmBadAtSecurity');
$pwd_peppered = hash_hmac("sha256", $pass, $pepper);
if(!$returnHashed){
return $pwd_peppered;
}
return password_hash($pwd_peppered, PASSWORD_ARGON2ID, ['threads' => 2]);
}
function GenKey($length = 21): string{
$password = "";
$possible = "0123456789abcdefghijkmnopqrstuvwxyz";
$i = 0;
while($i < $length){
$char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
if(!strstr($password, $char)){
$password .= $char;
$i++;
}
}
return $password;
}