dj_mix_hosting_software/classes/User.php

177 lines
No EOL
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace DJMixHosting;
use Random\RandomException;
Class User{
private $db;
private $id;
private $username;
private $email;
private $location;
private $bio;
private $created;
private $updated;
private $verified;
private $role;
private $img;
private $api_key;
public function __construct($db){
$this->db = $db;
}
/**
* @throws RandomException
*/
public function newUser($username, $password, $email){
if ($this->check_existing_user($username, $email)){
throw new RandomException("User already exists");
}
$this->username = $username;
$this->email = $email;
$password2 = password_hash($password, PASSWORD_DEFAULT);
$this->password = $password2;
$this->location = "";
$this->bio = "";
$this->created = date('Y-m-d H:i:s');
$this->updated = date('Y-m-d H:i:s');
$this->verified = 0;
$this->role = "user";
$this->img = "";
$this->api_key = bin2hex(random_bytes(32));
$stmt = $this->db->prepare("INSERT INTO users (username, password, email, img) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $this->username, $this->password, $this->email, $this->img);
$stmt->execute();
$stmt->close();
}
private function check_existing_user($username, $email){
$stmt = $this->db->prepare("SELECT * FROM users WHERE username = ? OR email = ?");
$stmt->bind_param("ss", $username, $email);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
$stmt->close();
return $user;
}
public function login($email, $password)
{
// Retrieve user record by email
$stmt = $this->db->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$user_data = $result->fetch_assoc();
$stmt->close();
// Check login_attempts table for lockout status
$stmt = $this->db->prepare("SELECT * FROM login_attempts WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$attempt_data = $stmt->get_result()->fetch_assoc();
$stmt->close();
$current_time = new \DateTime();
if ($attempt_data && !empty($attempt_data['lockout_until'])) {
$lockout_until = new \DateTime($attempt_data['lockout_until']);
if ($current_time < $lockout_until) {
return "Account locked until " . $lockout_until->format('Y-m-d H:i:s') . ". Please try again later.";
}
}
// If no user record found, still update login_attempts to mitigate enumeration issues
if (!$user_data) {
$this->updateFailedAttempt($email);
return "Invalid email or password.";
}
// Verify the password using password_verify
if (password_verify($password, $user_data['password'])) {
// Successful login clear login attempts and set session variables
$this->resetLoginAttempts($email);
$_SESSION['user'] = [
'id' => $user_data['id'],
'email' => $user_data['email'],
'username' => $user_data['username'],
'role' => $user_data['isAdmin'] ? 'admin' : 'user'
];
return true;
} else {
$attempts = $this->updateFailedAttempt($email);
return "Invalid email or password. Attempt $attempts of 3.";
}
}
/**
* Update (or create) a record in the login_attempts table for a failed attempt.
* If attempts reach 3, set a lockout that doubles each time.
* Returns the current number of attempts.
*/
private function updateFailedAttempt($email)
{
// Check for an existing record
$stmt = $this->db->prepare("SELECT * FROM login_attempts WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$record = $stmt->get_result()->fetch_assoc();
$stmt->close();
$current_time = new \DateTime();
if ($record) {
$attempts = $record['attempts'] + 1;
$lockouts = $record['lockouts'];
if ($attempts >= 3) {
// Increment lockouts and calculate the new lockout duration:
// Duration in minutes = 30 * 2^(lockouts)
$lockouts++;
$duration = 30 * pow(2, $lockouts - 1);
$lockout_until = clone $current_time;
$lockout_until->modify("+{$duration} minutes");
// Reset attempts to 0 on lockout
$attempts = 0;
$stmt = $this->db->prepare("UPDATE login_attempts SET attempts = ?, lockouts = ?, last_attempt = NOW(), lockout_until = ? WHERE email = ?");
$lockout_until_str = $lockout_until->format('Y-m-d H:i:s');
$stmt->bind_param("iiss", $attempts, $lockouts, $lockout_until_str, $email);
$stmt->execute();
$stmt->close();
} else {
$stmt = $this->db->prepare("UPDATE login_attempts SET attempts = ?, last_attempt = NOW() WHERE email = ?");
$stmt->bind_param("is", $attempts, $email);
$stmt->execute();
$stmt->close();
}
return $attempts;
} else {
// Create a new record for this email
$attempts = 1;
$lockouts = 0;
$stmt = $this->db->prepare("INSERT INTO login_attempts (email, attempts, lockouts, last_attempt) VALUES (?, ?, ?, NOW())");
$stmt->bind_param("sii", $email, $attempts, $lockouts);
$stmt->execute();
$stmt->close();
return $attempts;
}
}
/**
* Reset the login_attempts record for the given email.
*/
private function resetLoginAttempts($email)
{
$stmt = $this->db->prepare("DELETE FROM login_attempts WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$stmt->close();
}
}