diff --git a/classes/Authentication.php b/classes/Authentication.php
new file mode 100644
index 0000000..eae3973
--- /dev/null
+++ b/classes/Authentication.php
@@ -0,0 +1,393 @@
+settings = new SimpleXMLElement((file_exists("xml/settings.xml") ? file_get_contents("xml/settings.xml") : "
";
+ print_r($object);
+ exit();
+ }
+
+ // ==============
+ // Reset Password
+ // ==============
+
+ public function resetPassword() {
+
+ $username = filter_input(INPUT_POST, "user_name") ?: "";
+
+ if (empty($username)) {
+ return false;
+ }
+
+ $exists = $this->userExists($username);
+
+ if (!$exists) {
+ return false;
+ }
+
+ $user = $this->getUser($username);
+
+ $password = $this->resetUsersPassword($user->record->user_serial);
+
+ $this->emailUserPasswordResetLink($user, $password);
+ $this->journalEntry("{$username} reset their password.");
+
+ return true;
+ }
+
+ // ===========
+ // User Exists
+ // ===========
+
+ private function userExists($username) {
+
+ $SQL = 'select user_name from view_users where user_name = :username';
+
+ $statement = $this->ghrconnection->prepare($SQL);
+ $statement->bindValue(':username', $username, PDO::PARAM_STR);
+ $statement->execute();
+
+ $user = $statement->fetch(PDO::FETCH_ASSOC);
+
+ return ($user !== false);
+ }
+
+ // =================
+ // Get a User Object
+ // =================
+
+ private function getUser($username) {
+
+ $SQL = 'select user_serial, user_name, user_email from view_users where user_name = :username';
+
+ $statement = $this->ghrconnection->prepare($SQL);
+ $statement->bindValue(':username', $username, PDO::PARAM_STR);
+ $statement->execute();
+
+ $user = $statement->fetch(PDO::FETCH_ASSOC);
+
+ $xml = new SimpleXMLElement(' ');
+ $record = $xml->addChild('record');
+
+ $this->arrayToXml($user, $record);
+
+ return $xml;
+ }
+
+ private function arrayToXml(array $data, SimpleXMLElement $xml): void {
+ foreach ($data as $key => $value) {
+ $key = is_numeric($key) ? 'item' : $key;
+
+ if (is_array($value)) {
+ $child = $xml->addChild($key);
+ $this->arrayToXml($value, $child);
+ } else {
+ $xml->addChild($key, htmlspecialchars((string) $value));
+ }
+ }
+ }
+
+ // ========================
+ // Reset the Users Password
+ // ========================
+
+ private function resetUsersPassword($user_serial) {
+
+ $user_random_password = $this->generateRandomPassword();
+ $user_temp_password = password_hash($user_random_password, PASSWORD_DEFAULT);
+
+ $SQL = "update users
+ set user_password = :user_password,
+ user_change_password = :user_change_password
+ where user_serial = :user_serial";
+
+ $statement = $this->ghrconnection->prepare($SQL);
+
+ $statement->bindValue(":user_serial", $user_serial);
+ $statement->bindValue(":user_password", $user_temp_password);
+ $statement->bindValue(":user_change_password", 1);
+
+ $statement->execute();
+
+ return $user_random_password;
+ }
+
+ // =================================
+ // Email a users Password Reset Link
+ // =================================
+
+ public function emailUserPasswordResetLink($user, $password) {
+
+ if (!$user instanceof SimpleXMLElement) {
+ return;
+ }
+
+ // Prepare the email.
+
+ $XSLParms['USER-TEMP-PASSWORD'] = $password;
+
+ $to = $user->record->user_email;
+ $subject = "DEC International DMS - Password Reset Request";
+ $message = $this->applyXSL($user, $this->getXSL("emailPasswordResetLink"), $XSLParms);
+
+ // Send the Email
+ $this->sendEmail($to, $subject, $message);
+ }
+
+ // =======================================
+ // Send an email with optional attachments
+ // =======================================
+
+ public function sendEmail($to = "", $subject = "", $message = "", $attachments = "", $attachment_names = "unknown") {
+
+ if (empty($to) or empty($subject) or empty($message)) {
+
+ return false;
+ }
+
+ $SmtpHost = $this->settings->SmtpServer;
+ $SmtpUsername = $this->settings->SmtpUserName;
+ $SmtpPassword = $this->settings->SmtpPassword;
+ $SmtpPort = $this->settings->SmtpPort;
+
+ $email = new PHPMailer(true);
+
+ // Only for development
+
+ $message = "" . $message . "";
+
+ try {
+
+ //Server settings
+ $email->isSMTP();
+ $email->Host = $SmtpHost;
+ $email->SMTPAuth = true;
+ $email->Username = $SmtpUsername; // Your Gmail address
+ $email->Password = $SmtpPassword; // App password from Google
+// $email->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // or 'tls'
+ $email->Port = $SmtpPort;
+
+ $email->isHTML(true);
+ $email->SetFrom("info@dec-international.com", "DEC-International");
+
+ if (is_array($to)) {
+ foreach ($to as $address) {
+ $address = trim(str_replace(" ", "", $address));
+ if (empty($address)) {
+ continue;
+ }
+ $email->addAddress($address);
+ }
+ } else {
+ $email->addAddress($to);
+ }
+
+ $email->Subject = $subject;
+ $email->Body = $message;
+
+ if (is_array($attachments)) {
+ foreach ($attachments as $index => $item) {
+ if (!empty($item)) {
+ $email->addStringAttachment($item, ($attachment_names[$index] ?? "unknown"));
+ }
+ }
+ } else {
+ if (!empty($attachments)) {
+ $email->addStringAttachment($attachments, $attachment_names);
+ }
+ }
+
+ $success = $email->Send();
+ } catch (phpMailerException $e) {
+
+ $this->journalEntry($e->errorMessage());
+ $success = false;
+ } catch (Exception $e) {
+
+ $this->journalEntry($e->getMessage());
+ $success = false;
+ }
+
+ return $success;
+ }
+
+ // ======================
+ // Create a Journal Entry
+ // ======================
+
+ public function journalEntry($journal_entry = "") {
+
+ if (empty($journal_entry)) {
+ return;
+ }
+
+ $SQL = "insert into journal
+
+ ( journal_origin,
+ journal_ip,
+ journal_entry )
+
+ VALUES ( :journal_origin,
+ :journal_ip,
+ :journal_entry )";
+
+ $statement = $this->ghrconnection->prepare($SQL);
+
+ $statement->bindValue(":journal_origin", "Authentication Class");
+ $statement->bindValue(":journal_ip", $_SERVER["REMOTE_ADDR"] ?? "");
+ $statement->bindValue(":journal_entry", $journal_entry);
+
+ $statement->execute();
+ }
+
+ // ==========================================
+ // Apply an XSL Stylesheet to an XML Document
+ // ==========================================
+
+ public function applyXSL($XMLString = "", $XSLString = "", $XSLParms = "") {
+
+ if ($XMLString instanceof SimpleXMLElement) {
+ $XMLString = $XMLString->asXML();
+ }
+
+ if (empty($XSLString)) {
+ trigger_error("No XSL Stylesheet provided.");
+ }
+
+ if (!is_array($XSLParms)) {
+ $XSLParms = array();
+ }
+
+ $XSLParms["RANDOM_NUMBER"] = rand();
+ $XSLParms["TODAY_YYYYMMDD"] = date("Y-m-d");
+ $XSLParms["TODAY_MMDDYYYY"] = date("m/d/Y");
+ $XSLParms["TODAY_HHMM"] = date("Hi");
+ $XSLParms["COPYRIGHT"] = date("Y");
+
+ $XML = new DOMDocument();
+ $XSL = new DOMDocument();
+ $XSLT = new XSLTProcessor();
+
+ if ($XMLString) {
+ $XML->loadXML($XMLString);
+ }
+
+ if ($XSLString) {
+ $XSL->loadXML($XSLString);
+ }
+
+ $XSLT->importStylesheet($XSL);
+
+ if ($XSLParms) {
+ $XSLT->setParameter("", $XSLParms);
+ }
+
+ $document = $XSLT->transformToXml($XML);
+
+ return $document;
+ }
+
+ // ========================
+ // Return an XSL Stylesheet
+ // ========================
+
+ public function getXSL($document = "") {
+
+ if (trim($document) == "") {
+ return "";
+ }
+
+ $document = "xsl/" . trim($document) . ".xsl";
+ $XSL = (file_exists($document) ? file_get_contents($document) : "");
+
+ return $XSL;
+ }
+}
diff --git a/classes/Base.php b/classes/Base.php
index 15b902c..c3d6c96 100644
--- a/classes/Base.php
+++ b/classes/Base.php
@@ -266,6 +266,18 @@ class Base {
$_SESSION[self::TODAY] = date("Y-m-d");
+ // Get the requested Action
+
+ $action = filter_input(INPUT_POST, "action") ?: "";
+ $Xaction = filter_input(INPUT_GET, "Xaction") ?: "";
+
+ $action = (empty($Xaction) ? $action : $Xaction);
+
+ if ($Xaction == 'resetpassword') {
+ $this->resetpassword();
+ }
+
+
// Login
if (($_SESSION[self::USER_AUTHENTICATED] ?? false) === false) {
@@ -784,6 +796,23 @@ class Base {
exit();
}
+
+ // =====================
+ // Forgot/Reset Password
+ // =====================
+
+ public function resetpassword() {
+
+ include_once 'classes/Authentication.php';
+
+ $reset = (new Authentication())->resetPassword() ? true : false;
+
+ $XSLParms["BTN_SUBMIT"] = "main.php?action=Users.signIn&step=authenticate";
+ $XSLParms["RESET_PASSWORD_SUCCESSFUL"] = "TRUE";
+
+ echo $this->applyXSL("", $this->getXSL("signIn"), $XSLParms);
+ die();
+ }
}
?>
diff --git a/js/signIn.js b/js/signIn.js
index d681c14..4d47e7f 100644
--- a/js/signIn.js
+++ b/js/signIn.js
@@ -53,6 +53,36 @@ $(document).ready(function () {
});
+ $(".btnForgot").on("click", function (e) {
+
+ e.preventDefault();
+ $("#reset-password").submit();
+
+ });
+
+ // ---------------
+ // Validate Inputs
+ // ---------------
+
+ $("#reset-password").submit(function (e) {
+
+ clearInputErrors();
+
+ var user_name = $("#user_name");
+
+ user_name.val(user_name.val().replace(/\s+/g, " ").trim());
+
+ if (isBlank(user_name)) {
+ createInputError(user_name, "User Name/Email is required to reset password");
+ return false;
+ }
+
+ $("#reset_user_name").val(user_name.val());
+
+ return true;
+
+ });
+
});
// --------------------------------
diff --git a/xsl/resetPasswordNotice.xsl b/xsl/resetPasswordNotice.xsl
new file mode 100644
index 0000000..a304989
--- /dev/null
+++ b/xsl/resetPasswordNotice.xsl
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ 2.0
+
+ notice
+
+
+
+
+
+
+
+
+
+ System Message
+
+
+
+
+
+ Message:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xsl/signIn.xsl b/xsl/signIn.xsl
index 5fbb5c5..2c3830b 100644
--- a/xsl/signIn.xsl
+++ b/xsl/signIn.xsl
@@ -7,7 +7,9 @@
Georgia Housing Report Sign In
signIn
+ ?Xaction=resetpassword
+
<!DOCTYPE html>
@@ -22,6 +24,8 @@
+
+
@@ -52,6 +56,12 @@
+
+
+ If an account exists for the email address provided, you will receive an email containing a temporary password and a link to reset your password.
+
+
+
@@ -71,10 +81,14 @@
+
+
-
+
@@ -86,7 +100,7 @@