|
<?php |
|
class Forgot { |
|
// (A) PROPERTIES |
|
private $valid = 900; // 15 mins = 900 secs |
|
private $plen = 8; // random password length |
|
private $pdo = null; // pdo object |
|
private $stmt = null; // sql statement |
|
public $error = ""; // errors, if any |
|
|
|
// (B) CONSTRUCTOR - CONNECT TO DATABASE |
|
function __construct () { |
|
$this->pdo = new PDO( |
|
"mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET, |
|
DB_USER, DB_PASSWORD, [ |
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, |
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC |
|
]); |
|
} |
|
|
|
// (C) DESTRUCTOR - CLOSE DATABASE CONNECTION |
|
function __destruct () { |
|
if ($this->stmt!==null) { $this->stmt = null; } |
|
if ($this->pdo!==null) { $this->pdo = null; } |
|
} |
|
|
|
// (D) HELPER - EXECUTE SQL QUERY |
|
function query ($sql, $data=null) : void { |
|
$this->stmt = $this->pdo->prepare($sql); |
|
$this->stmt->execute($data); |
|
} |
|
|
|
// (E) HELPER - SEND EMAIL |
|
function mail ($to, $subject, $body) { |
|
$head = implode("\r\n", ["MIME-Version: 1.0", "Content-type: text/html; charset=utf-8"]); |
|
return mail($to, $subject, $body, $head); |
|
} |
|
|
|
// (F) GET USER BY ID OR EMAIL |
|
function getUser ($id) { |
|
$this->query("SELECT * FROM `users` WHERE `user_".(is_numeric($id)?"id":"email")."`=?", [$id]); |
|
return $this->stmt->fetch(); |
|
} |
|
|
|
// (G) GET PASSWORD RESET REQUEST |
|
function getReq ($id) { |
|
$this->query("SELECT * FROM `password_reset` WHERE `user_id`=?", [$id]); |
|
return $this->stmt->fetch(); |
|
} |
|
|
|
// (H) PASSWORD RESET REQUEST |
|
function request ($email) { |
|
// (H1) ALREADY SIGNED IN - @CHANGE THIS TO YOUR OWN |
|
// ASSUMING YOU USE $_SESSION["USER"] |
|
if (isset($_SESSION["user"])) { |
|
$this->error = "You are already signed in."; |
|
return false; |
|
} |
|
|
|
// (H2) CHECK IF VALID USER |
|
$user = $this->getUser($email); |
|
if (!is_array($user)) { |
|
$this->error = "$email is not registered."; |
|
return false; |
|
} |
|
|
|
// (H3) CHECK PREVIOUS REQUEST (PREVENT SPAM) |
|
$req = $this->getReq($user["user_id"]); |
|
if (is_array($req)) { |
|
$expire = strtotime($req["reset_time"]) + $this->valid; |
|
$now = strtotime("now"); |
|
$left = $now - $expire; |
|
if ($left <0) { |
|
$this->error = "Please wait another ".abs($left)." seconds."; |
|
return false; |
|
} |
|
} |
|
|
|
// (H4) CHECKS OK - CREATE NEW RESET REQUEST |
|
$now = strtotime("now"); |
|
$hash = md5($user["user_email"] . $now); // random hash |
|
$this->query( |
|
"REPLACE INTO `password_reset` (`user_id`, `reset_hash`, `reset_time`) VALUES (?,?,?)", |
|
[$user["user_id"], $hash, date("Y-m-d H:i:s")] |
|
); |
|
|
|
// (H5) SEND EMAIL TO USER - @CHANGE EMAIL TEMPLATE |
|
$mail = "<html><body><a href='http://localhost/3b-reset.php?i={$user["user_id"]}&h={$hash}'>Click to reset</a></body></html>"; |
|
if ($this->mail($user["user_email"], "Password Reset", $mail)) { return true; } |
|
else { |
|
$this->error = "Error sending mail"; |
|
return false; |
|
} |
|
} |
|
|
|
// (I) PROCESS PASSWORD RESET |
|
function reset ($id, $hash) { |
|
// (I1) ALREADY SIGNED IN - @CHANGE THIS TO YOUR OWN |
|
// ASSUMING YOU USE $_SESSION["USER"] |
|
if (isset($_SESSION["user"])) { |
|
$this->error = "You are already signed in."; |
|
return false; |
|
} |
|
|
|
// (I2) CHECK REQUEST |
|
$req = $this->getReq($id); |
|
$pass = is_array($req); |
|
|
|
// (I3) CHECK EXPIRE |
|
if ($pass) { |
|
$expire = strtotime($req["reset_time"]) + $this->valid; |
|
$now = strtotime("now"); |
|
$pass = $now <= $expire; |
|
} |
|
|
|
// (I4) CHECK HASH |
|
if ($pass) { $pass = $hash==$req["reset_hash"]; } |
|
|
|
// (I5) GET USER |
|
if ($pass) { |
|
$user = $this->getUser($id); |
|
$pass = is_array($user); |
|
} |
|
|
|
// (I6) CHECK FAIL - INVALID REQUEST |
|
if (!$pass) { |
|
$this->error = "Invalid request."; |
|
return false; |
|
} |
|
|
|
// (I7) UPDATE USER PASSWORD - @CHANGE ENCRYPT PASSWORD |
|
$this->pdo->beginTransaction(); |
|
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=+?"; |
|
$password = substr(str_shuffle($chars), 0, $this->plen); |
|
$this->query("UPDATE `users` SET `user_password`=? WHERE `user_id`=?", [$password, $id]); |
|
|
|
// (I8) REMOVE REQUEST |
|
$this->query("DELETE FROM `password_reset` WHERE `user_id`=?", [$id]); |
|
|
|
// (I9) EMAIL TO USER - @CHANGE EMAIL TEMPLATE |
|
$mail = "<html><body>$password</body></html>"; |
|
if ($this->mail($user["user_email"], "Password Reset", $mail)) { |
|
$this->pdo->commit(); |
|
return true; |
|
} else { |
|
$this->error = "Error sending mail"; |
|
$this->pdo->rollBack(); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
// (J) DATABASE SETTINGS - @CHANGE TO YOUR OWN |
|
define("DB_HOST", "localhost"); |
|
define("DB_NAME", "test"); |
|
define("DB_CHARSET", "utf8mb4"); |
|
define("DB_USER", "root"); |
|
define("DB_PASSWORD", ""); |
|
|
|
// (K) NEW PASSWORD RECOVER OBJECT |
|
$_FORGOT = new Forgot(); |