Compare commits
10 Commits
Bug197-Zay
...
Hui-Organi
| Author | SHA1 | Date |
|---|---|---|
|
|
246565c8e9 | |
|
|
b37955daa8 | |
|
|
4e0d97afb4 | |
|
|
b0a44c001d | |
|
|
f6833e7398 | |
|
|
2ce0aa95b2 | |
|
|
43e9cfeffe | |
|
|
b264ce4299 | |
|
|
a86c810a2d | |
|
|
8876825ef9 |
|
|
@ -1,2 +1,5 @@
|
||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
include 'NoDirectPhpAcess.php';
|
||||||
|
include 'Header.php';
|
||||||
|
include "get_mysql_credentials.php"; // Database credentials
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
|
// Connect to the database
|
||||||
|
$con = mysqli_connect("localhost", $mysql_username, $mysql_password, "lrr");
|
||||||
|
|
||||||
|
if (mysqli_connect_errno()) {
|
||||||
|
die("Connection failed: " . mysqli_connect_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is logged in
|
||||||
|
if (isset($_SESSION['email'])) {
|
||||||
|
$email = $_SESSION['email'];
|
||||||
|
|
||||||
|
// Check if form is submitted
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
// Get the submitted answers
|
||||||
|
$answer1 = strtolower(trim($_POST['answer1']));
|
||||||
|
$answer2 = strtolower(trim($_POST['answer2']));
|
||||||
|
|
||||||
|
// Fetch correct answers from the database
|
||||||
|
$sql = "SELECT user_id, answer1, answer2 FROM password_recovery_security_questions WHERE email = '$email'";
|
||||||
|
$result = mysqli_query($con, $sql);
|
||||||
|
|
||||||
|
if ($row = mysqli_fetch_assoc($result)) {
|
||||||
|
// Compare submitted answers with stored answers
|
||||||
|
if (hash_equals($row['answer1'], $answer1) && hash_equals($row['answer2'], $answer2)) {
|
||||||
|
$_SESSION['user_id'] = $row['user_id'];
|
||||||
|
header("Location: ResetPassword.php"); // Redirect to password reset page
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$error_message = "Incorrect answers. Please try again.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<div class="container mt-5"><div class="alert alert-warning" role="alert">No security questions found for this user.</div></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch security questions from the database for display
|
||||||
|
$sql = "SELECT question1, question2 FROM password_recovery_security_questions WHERE email = '$email'";
|
||||||
|
$result = mysqli_query($con, $sql);
|
||||||
|
|
||||||
|
if ($row = mysqli_fetch_assoc($result)) {
|
||||||
|
// Display the questions in a form
|
||||||
|
echo'<br/><br/><br/>';
|
||||||
|
echo '<div class="container">';
|
||||||
|
echo '<div class="row">';
|
||||||
|
echo '<div class="col-md-5"></div>';
|
||||||
|
echo '<div class="col-md-5">';
|
||||||
|
if (isset($error_message)) {
|
||||||
|
echo '<div id="alertbad" class="alert alert-danger" role="alert">' . $error_message . '</div>'; // Display error message
|
||||||
|
}
|
||||||
|
|
||||||
|
//echo '<center>';
|
||||||
|
echo '<form action="" method="POST" class="">';
|
||||||
|
echo '<legend>Answer Your Security Questions.</legend>';
|
||||||
|
|
||||||
|
// Question 1
|
||||||
|
echo '<div class="mb-3">';
|
||||||
|
echo '<label class="form-label">' . htmlspecialchars($row['question1']) . '</label>';
|
||||||
|
echo '<input type="text" class="form-control" name="answer1" required>';
|
||||||
|
echo '</div>';
|
||||||
|
echo'<br/>';
|
||||||
|
|
||||||
|
// Question 2
|
||||||
|
echo '<div class="mb-3">';
|
||||||
|
echo '<label class="form-label">' . htmlspecialchars($row['question2']) . '</label>';
|
||||||
|
echo '<input type="text" class="form-control" name="answer2" required>';
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
echo '<button id="sub" type="submit" class="btn btn-primary">Submit Answers</button>';
|
||||||
|
echo '</form>';
|
||||||
|
echo '</div></div></div>'; // Close container
|
||||||
|
} else {
|
||||||
|
echo '<div class="container mt-5"><div class="alert alert-warning" role="alert">No security questions found for this user.</div></div>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
header("Location: RecoverPassword.php"); // Redirect if session data is missing
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysqli_close($con);
|
||||||
|
?>
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<?php
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
error_reporting(0);
|
error_reporting(0);
|
||||||
|
|
@ -157,7 +162,7 @@ if (mysqli_connect_errno()) {
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if (isset($_SESSION["user_fullname"])) {
|
if (isset($_SESSION["user_fullname"])) {
|
||||||
if ($_SESSION['user_type'] == "Student" || $_SESSION['user_type'] == 'Lecturer') {
|
if ($_SESSION['user_type'] == "Student" || $_SESSION['user_type'] == 'Lecturer' || $_SESSION['user_type'] == 'TA') {
|
||||||
echo "<a class='nav-link' href='~\..\Courses.php'><i class='fa fa-book'></i> My courses </a>";
|
echo "<a class='nav-link' href='~\..\Courses.php'><i class='fa fa-book'></i> My courses </a>";
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
include 'NoDirectPhpAcess.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
include 'Header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-5"></div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<form method="post" action="Script.php">
|
||||||
|
<legend>Recover password</legend>
|
||||||
|
<input type="hidden" name="form_recover_password" value="true"/>
|
||||||
|
Email
|
||||||
|
<input type="text" name="email" placeholder="Enter your email address" class="form-control" required="required" value="<?php echo htmlspecialchars($_SESSION['user_email']); ?>"> <br/>
|
||||||
|
<button id="rec" type="submit" class="btn btn-primary">Recover</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
if(isset($_SESSION['info_recover_password'])) {
|
||||||
|
echo '<hr><div class="alert alert-danger" role="alert">'.htmlspecialchars($_SESSION['info_recover_password']).'</div>';
|
||||||
|
$_SESSION['info_recover_password'] = null;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
require_once 'Header.php';
|
||||||
|
require_once 'NoDirectPhpAcess.php';
|
||||||
|
require_once "get_mysql_credentials.php";
|
||||||
|
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
$con = mysqli_connect("localhost", $mysql_username, $mysql_password, "lrr");
|
||||||
|
|
||||||
|
if (mysqli_connect_errno()) {
|
||||||
|
error_log("Database connection failed: " . mysqli_connect_error());
|
||||||
|
die("An error occurred. Please try again later.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user_id is set in the session
|
||||||
|
if (!isset($_SESSION['email'])) {
|
||||||
|
die("Session expired. Please log in again.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = $_SESSION['email'];
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
// Handle password reset
|
||||||
|
if (isset($_POST['new_password']) && isset($_POST['confirm_password'])) {
|
||||||
|
$new_password = $_POST['new_password'];
|
||||||
|
$confirm_password = $_POST['confirm_password'];
|
||||||
|
|
||||||
|
if (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\d\s]).{8,}$/', $new_password)) {
|
||||||
|
echo '<div class="alert alert-danger">Password must be at least 8 characters long and include uppercase and lowercase letters, numbers, and special characters.</div>';
|
||||||
|
} elseif ($new_password !== $confirm_password) {
|
||||||
|
echo '<div class="alert alert-danger">Passwords do not match. Please try again.</div>';
|
||||||
|
} else {
|
||||||
|
$hashed_password = password_hash($new_password, PASSWORD_ARGON2ID);
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
$stmt = $con->prepare("UPDATE users_table SET Password = ? WHERE email = ? AND user_id = ?");
|
||||||
|
$stmt->bind_param("sss", $hashed_password, $email, $user_id);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
echo '<div class="alert alert-success">Password reset successfully. You can now log in with your new password.</div>';
|
||||||
|
unset($_SESSION['user_id']); // Clear user_id after successful password reset
|
||||||
|
header("Location: index.php");
|
||||||
|
} else {
|
||||||
|
error_log("Error updating password for user ID: $user_id");
|
||||||
|
echo '<div class="alert alert-danger">An error occurred. Please try again later.</div>';
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the reset password form
|
||||||
|
echo '
|
||||||
|
<br/><br/><br/>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-5"></div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<form action="" method="POST" class="">
|
||||||
|
<legend>Reset Your Password</legend><br/>
|
||||||
|
New Password <label class="form-text">Must include uppercase and lowercase letters, digits and special characters.</label>
|
||||||
|
<input type="password" name="new_password" placeholder=" Enter New Password" class="form-control" required>
|
||||||
|
<br/>
|
||||||
|
Confirm New Password
|
||||||
|
<input type="password" name="confirm_password" placeholder="Confirm New Password" class="form-control" required>
|
||||||
|
<br/>
|
||||||
|
<button id="butt" type="submit" class="btn btn-primary">Reset Password</button>
|
||||||
|
</form>
|
||||||
|
</div></div></div>
|
||||||
|
<style>
|
||||||
|
.guideline { display: none;}
|
||||||
|
#newPassword:focus + .guideline {display: block;}
|
||||||
|
';
|
||||||
|
|
||||||
|
|
||||||
|
mysqli_close($con);
|
||||||
|
?>
|
||||||
258
Script.php
258
Script.php
|
|
@ -1,11 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
include 'NoDirectPhpAcess.php';
|
include 'NoDirectPhpAcess.php';
|
||||||
|
|
||||||
use PHPMailer\PHPMailer\PHPMailer;
|
|
||||||
use PHPMailer\PHPMailer\SMTP;
|
|
||||||
use PHPMailer\PHPMailer\Exception;
|
|
||||||
|
|
||||||
require 'vendor/autoload.php'; // Load Composer's autoloader
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -183,7 +177,14 @@ if (!empty($_POST["form_signup"])) {
|
||||||
$_SESSION['user_fullname'] =$_SESSION['user_fullname_temp'];
|
$_SESSION['user_fullname'] =$_SESSION['user_fullname_temp'];
|
||||||
|
|
||||||
if ($con->query($sql) === TRUE) {
|
if ($con->query($sql) === TRUE) {
|
||||||
header("Location: Courses.php");
|
// Get the newly inserted user's ID
|
||||||
|
$user_id = $con->insert_id;
|
||||||
|
|
||||||
|
//Set the user_id in the session
|
||||||
|
$_SESSION['user_id'] = $user_id;
|
||||||
|
|
||||||
|
//Redirect to SecurityQuestions.php
|
||||||
|
header("Location: SecurityQuestions.php");
|
||||||
} else {
|
} else {
|
||||||
echo "Something really bad (SQL insertion error) happened during sign up.";
|
echo "Something really bad (SQL insertion error) happened during sign up.";
|
||||||
}
|
}
|
||||||
|
|
@ -230,20 +231,50 @@ if (!empty($_POST["form_login"])) {
|
||||||
$_SESSION['user_type'] = $row['UserType'];
|
$_SESSION['user_type'] = $row['UserType'];
|
||||||
$_SESSION['user_fullname'] = $row['Full_Name'];
|
$_SESSION['user_fullname'] = $row['Full_Name'];
|
||||||
|
|
||||||
|
// Check if the user is a student and has not set up their password recovery yet
|
||||||
|
|
||||||
if ($_SESSION['user_type'] == "Student") {
|
if ($_SESSION['user_type'] == "Student") {
|
||||||
|
// Query to check if the student has completed the password recovery setup
|
||||||
|
$recovery_result = mysqli_query($con, "SELECT * FROM password_recovery_security_questions WHERE Student_ID = '" . $row['Student_ID'] . "'");
|
||||||
|
if (mysqli_num_rows($recovery_result) == 0) {
|
||||||
|
// If the student has not set up password recovery, redirect to the setup page
|
||||||
|
header("Location: SecurityQuestions.php");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
// If the student has set up password recovery, redirect to the Courses page
|
||||||
header("Location: Courses.php");
|
header("Location: Courses.php");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect other user types to their respective pages
|
||||||
|
|
||||||
if ($_SESSION['user_type'] == "Lecturer") {
|
if ($_SESSION['user_type'] == "Lecturer") {
|
||||||
header("Location: Courses.php");
|
$recovery_result = mysqli_query($con, "SELECT * FROM password_recovery_security_questions WHERE user_id = '" . $row['User_ID'] . "' AND user_type = 'Lecturer'");
|
||||||
|
if (mysqli_num_rows($recovery_result) == 0) {
|
||||||
|
header("Location: SecurityQuestions.php");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
header("Location: Courses.php"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_SESSION['user_type'] == "TA") {
|
if ($_SESSION['user_type'] == "TA") {
|
||||||
header("Location: Courses.php");
|
$recovery_result = mysqli_query($con, "SELECT * FROM password_recovery_security_questions WHERE user_id = '" . $row['User_ID'] . "' AND user_type = 'TA'");
|
||||||
|
if (mysqli_num_rows($recovery_result) == 0) {
|
||||||
|
header("Location: SecurityQuestions.php");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
header("Location: Courses.php"); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_SESSION['user_type'] == "Admin") {
|
if ($_SESSION['user_type'] == "Admin") {
|
||||||
header("Location: Admin.php");
|
$recovery_result = mysqli_query($con, "SELECT * FROM password_recovery_security_questions WHERE user_id = '" . $row['User_ID'] . "' AND user_type = 'Admin'");
|
||||||
|
if (mysqli_num_rows($recovery_result) == 0) {
|
||||||
|
header("Location: SecurityQuestions.php");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
header("Location: Admin.php"); }
|
||||||
|
|
||||||
}
|
}
|
||||||
// report wrong pass if not correct
|
// report wrong pass if not correct
|
||||||
return;
|
return;
|
||||||
|
|
@ -256,184 +287,79 @@ if (!empty($_POST["form_login"])) {
|
||||||
header("Location: index.php");
|
header("Location: index.php");
|
||||||
exit(); // Add this line to prevent further execution after redirect
|
exit(); // Add this line to prevent further execution after redirect
|
||||||
}
|
}
|
||||||
// Add the following line to reset the session variable when needed
|
// Reset the session variable when needed
|
||||||
unset($_SESSION["failed_login_user"]);
|
unset($_SESSION["failed_login_user"]);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ################################ Recover Password #####################################
|
// ################################ Recover Password #####################################
|
||||||
|
|
||||||
if (!empty($_POST["form_recover_password"])) {
|
if (!empty($_POST["form_recover_password"])) {
|
||||||
|
$email = mysqli_real_escape_string($con, $_POST["email"]);
|
||||||
|
|
||||||
$student_id = trim(mysqli_real_escape_string($con, $_POST["sno"]));
|
// Validate email
|
||||||
$email = trim(mysqli_real_escape_string($con, $_POST["email"]));
|
|
||||||
|
|
||||||
// validate student number
|
|
||||||
if (strlen($student_id) != 12 || !is_numeric($student_id)) { // Basic validation
|
|
||||||
$_SESSION["info_recover_password"] = "Invalid student number format.";
|
|
||||||
header("Location: recover_password.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate email
|
|
||||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
$_SESSION["info_recover_password"] = "Invalid email address format.";
|
$_SESSION["info_recover_password"] = "Invalid email address.";
|
||||||
header("Location: recover_password.php");
|
header("Location: RecoverPassword.php");
|
||||||
exit;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user exists and get User_ID
|
// Check if user exists in the database
|
||||||
$user_check_query = mysqli_query($con, "SELECT User_ID FROM users_table WHERE Email='$email' and Student_ID='$student_id'");
|
$result = mysqli_query($con, "SELECT * FROM users_table WHERE Email='$email'");
|
||||||
if (mysqli_num_rows($user_check_query) == 0) {
|
if (mysqli_num_rows($result) == 0) {
|
||||||
$_SESSION["info_recover_password"] = "Student ID or Email not found in our records. Please check your details or contact support.";
|
$_SESSION["info_recover_password"] = "Email address is not recognized.";
|
||||||
header("Location: recover_password.php");
|
header("Location: RecoverPassword.php");
|
||||||
exit;
|
|
||||||
} else {
|
} else {
|
||||||
$user_data = mysqli_fetch_assoc($user_check_query);
|
// Store the student ID and email in the session
|
||||||
$user_id = $user_data['User_ID'];
|
$_SESSION['email'] = $email;
|
||||||
|
header("Location: AnswerSecurityQuestions.php");
|
||||||
// Check daily request limit (max 5 tokens per day for this user_id)
|
|
||||||
$today_start = date("Y-m-d 00:00:00");
|
|
||||||
$today_end = date("Y-m-d 23:59:59");
|
|
||||||
$limit_query_str = "SELECT COUNT(*) as count FROM password_reset_tokens WHERE user_id='$user_id' AND created_at BETWEEN '$today_start' AND '$today_end'";
|
|
||||||
|
|
||||||
$limit_query = mysqli_query($con, $limit_query_str);
|
|
||||||
if (!$limit_query) {
|
|
||||||
// Log error: mysqli_error($con)
|
|
||||||
$_SESSION["info_recover_password"] = "Server error checking request limit. Please try again later.";
|
|
||||||
header("Location: recover_password.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$limit_row = mysqli_fetch_assoc($limit_query);
|
|
||||||
|
|
||||||
if ($limit_row['count'] >= 5) {
|
|
||||||
$_SESSION["info_recover_password"] = "You have reached the maximum number of password reset requests for today (5). Please try again tomorrow.";
|
|
||||||
header("Location: recover_password.php");
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a unique token
|
|
||||||
try {
|
|
||||||
$token = bin2hex(random_bytes(32)); // PHP 7+
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// Fallback for older PHP if random_bytes is not available (less secure)
|
|
||||||
$token = bin2hex(openssl_random_pseudo_bytes(32));
|
|
||||||
}
|
|
||||||
$expires_at = date('Y-m-d H:i:s', strtotime('+10 minutes'));
|
|
||||||
|
|
||||||
// Store the token
|
|
||||||
$insert_token_sql = "INSERT INTO password_reset_tokens (user_id, token, expires_at, created_at) VALUES ('$user_id', '$token', '$expires_at', NOW())";
|
|
||||||
if (mysqli_query($con, $insert_token_sql)) {
|
|
||||||
// Send email with the reset link
|
|
||||||
$reset_link = "http://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "/reset_password_form.php?token=" . $token;
|
|
||||||
|
|
||||||
$mail = new PHPMailer(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
//Server settings
|
|
||||||
// $mail->SMTPDebug = SMTP::DEBUG_SERVER; // Enable verbose debug output for troubleshooting
|
|
||||||
$mail->isSMTP();
|
|
||||||
$mail->Host = 'smtp.163.com';
|
|
||||||
$mail->SMTPAuth = true;
|
|
||||||
$mail->Username = '13175521169@163.com'; // IMPORTANT: Replace with your 163.com email
|
|
||||||
$mail->Password = 'VAwKtaNiZCUQzmPv'; // IMPORTANT: Replace with your 163.com password or authorization code
|
|
||||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // Enable SSL encryption
|
|
||||||
$mail->Port = 465; // TCP port to connect to for SSL (common for 163.com)
|
|
||||||
// Or use PHPMailer::ENCRYPTION_STARTTLS and Port 587/25 if SMTPS doesn't work
|
|
||||||
|
|
||||||
//Recipients
|
|
||||||
$mail->setFrom('13175521169@163.com', 'LRR Password Recovery'); // IMPORTANT: Replace with your 163.com email
|
|
||||||
$mail->addAddress($email); // Add a recipient
|
|
||||||
|
|
||||||
// Content
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = 'Password Reset Request - LRR';
|
|
||||||
$mail_body_html = "Hello,<br><br>";
|
|
||||||
$mail_body_html .= "You (or someone else) requested a password reset for your LRR account associated with this email address.<br>";
|
|
||||||
$mail_body_html .= "If this was you, please click the following link to reset your password. This link is valid for 10 minutes:<br>";
|
|
||||||
$mail_body_html .= "<a href='" . $reset_link . "'>" . $reset_link . "</a><br><br>";
|
|
||||||
$mail_body_html .= "If you did not request this password reset, please ignore this email. Your account is still secure.<br><br>";
|
|
||||||
$mail_body_html .= "Thanks,<br>The LRR Team";
|
|
||||||
$mail->Body = $mail_body_html;
|
|
||||||
|
|
||||||
$mail_body_alt = "Hello,\n\n";
|
|
||||||
$mail_body_alt .= "You (or someone else) requested a password reset for your LRR account associated with this email address.\n";
|
|
||||||
$mail_body_alt .= "If this was you, please copy and paste the following link into your browser to reset your password. This link is valid for 10 minutes:\n";
|
|
||||||
$mail_body_alt .= $reset_link . "\n\n";
|
|
||||||
$mail_body_alt .= "If you did not request this password reset, please ignore this email. Your account is still secure.\n\n";
|
|
||||||
$mail_body_alt .= "Thanks,\nThe LRR Team";
|
|
||||||
$mail->AltBody = $mail_body_alt; $mail->send();
|
|
||||||
$_SESSION["info_recover_password"] = "Success! A password reset link has been sent to your email address (" . htmlspecialchars($email) . "). Please check your inbox and spam folder. The link will expire in 10 minutes.";
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$_SESSION["info_recover_password"] = "Message could not be sent. Mailer Error: " . $mail->ErrorInfo . ". Please contact support or try again later.";
|
|
||||||
// Log the detailed error for server-side review
|
|
||||||
error_log("PHPMailer Error for " . $email . ": " . $mail->ErrorInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header("Location: recover_password.php");
|
// ################################ RESET Password #####################################
|
||||||
exit;
|
|
||||||
} else {
|
|
||||||
$_SESSION["info_recover_password"] = "Could not process your request due to a server error (token storage failed: " . mysqli_error($con) . "). Please try again later.";
|
|
||||||
error_log("LRR Password Recovery: DB error storing token - " . mysqli_error($con)); // Log DB error
|
|
||||||
header("Location: recover_password.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ################################ PROCESS PASSWORD RESET FORM ######################################
|
|
||||||
if (!empty($_POST["form_reset_password"])) {
|
if (!empty($_POST["form_reset_password"])) {
|
||||||
|
$password = mysqli_real_escape_string($con, $_POST["password"]);
|
||||||
$token = mysqli_real_escape_string($con, $_POST["token"]);
|
$token = mysqli_real_escape_string($con, $_POST["token"]);
|
||||||
$new_password = mysqli_real_escape_string($con, $_POST["new_password"]);
|
$email = mysqli_real_escape_string($con, $_POST["email"]);
|
||||||
$confirm_password = mysqli_real_escape_string($con, $_POST["confirm_password"]);
|
$result = mysqli_query(
|
||||||
|
$con,
|
||||||
|
"SELECT * FROM Users_Table WHERE email='$email'"
|
||||||
|
);
|
||||||
|
if (mysqli_num_rows($result) == 0) {
|
||||||
|
|
||||||
// Password validation
|
echo "invalid email";
|
||||||
if ($new_password !== $confirm_password) {
|
return;
|
||||||
$_SESSION["info_reset_password"] = "Password and confirm password do not match. Please try again.";
|
|
||||||
header("Location: reset_password_form.php?token=" . htmlspecialchars($token));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if token is valid and get associated user_id
|
|
||||||
$token_check_sql = "SELECT user_id, expires_at FROM password_reset_tokens WHERE token='$token' AND used=0";
|
|
||||||
$token_check_result = mysqli_query($con, $token_check_sql);
|
|
||||||
|
|
||||||
if (!$token_check_result || mysqli_num_rows($token_check_result) === 0) {
|
|
||||||
$_SESSION["info_reset_password"] = "Invalid or expired token. Please request a new password reset link.";
|
|
||||||
header("Location: reset_password_form.php?token=" . htmlspecialchars($token));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$token_data = mysqli_fetch_assoc($token_check_result);
|
|
||||||
$user_id = $token_data['user_id'];
|
|
||||||
$expires_at = $token_data['expires_at'];
|
|
||||||
|
|
||||||
// Check if token has expired
|
|
||||||
if (strtotime($expires_at) <= strtotime(date('Y-m-d H:i:s'))) {
|
|
||||||
$_SESSION["info_reset_password"] = "This password reset link has expired. Please request a new one.";
|
|
||||||
header("Location: reset_password_form.php?token=" . htmlspecialchars($token));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the new password
|
|
||||||
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT); // Update the user's password in the users_table
|
|
||||||
$update_password_sql = "UPDATE users_table SET Password = '$hashed_password' WHERE User_ID = '$user_id'";
|
|
||||||
if (mysqli_query($con, $update_password_sql)) {
|
|
||||||
// Mark the token as used in password_reset_tokens table
|
|
||||||
$mark_used_sql = "UPDATE password_reset_tokens SET used = 1 WHERE token = '$token'";
|
|
||||||
mysqli_query($con, $mark_used_sql); // Important to mark as used
|
|
||||||
|
|
||||||
$_SESSION["info_login"] = "Success! Your password has been reset successfully. You can now log in with your new password.";
|
|
||||||
unset($_SESSION['info_recover_password']); // Clear any old messages
|
|
||||||
header("Location: index.php"); // Redirect to sign-in page for immediate login
|
|
||||||
exit;
|
|
||||||
} else {
|
} else {
|
||||||
error_log("LRR Password Reset: DB error updating password - " . mysqli_error($con));
|
while ($row = mysqli_fetch_assoc($result)) {
|
||||||
$_SESSION["info_reset_password"] = "An error occurred while updating your password. Please try again.";
|
|
||||||
header("Location: reset_password_form.php?token=" . htmlspecialchars($token));
|
$userid = $row['User_ID'];
|
||||||
exit;
|
|
||||||
|
$email = $row['Email'];
|
||||||
|
$id = $row['Student_ID'];
|
||||||
|
|
||||||
|
$user_token = $userid * $userid * $userid + $userid * 0.00343;
|
||||||
|
if ($user_token == $token) {
|
||||||
|
// Password Update
|
||||||
|
|
||||||
|
// Password Update
|
||||||
|
$hashed_password = hash('sha512', $password);
|
||||||
|
$sql = "UPDATE users_table set HashPassword='$hashed_password' where User_ID='$userid';";
|
||||||
|
if ($con->query($sql) === TRUE) {
|
||||||
|
|
||||||
|
error_reporting(0);
|
||||||
|
|
||||||
|
$_SESSION["info_login"] = " Password changed successfully , you can login now with your new password ";
|
||||||
|
header("Location: index.php");
|
||||||
|
} else {
|
||||||
|
echo "Error: " . $sql . "<br>" . $con->error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Invalid Token ";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
error_reporting(0);
|
||||||
|
date_default_timezone_set('Asia/Shanghai');
|
||||||
|
|
||||||
|
include 'NoDirectPhpAcess.php';
|
||||||
|
include "get_mysql_credentials.php"; // Database credentials
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
|
$con = mysqli_connect("localhost", $mysql_username, $mysql_password, "lrr");
|
||||||
|
|
||||||
|
if (mysqli_connect_errno()) {
|
||||||
|
die("Connection failed: " . mysqli_connect_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user_id is set in the session
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
echo '<div class="alert alert-danger" role="alert">Session expired. Please log in again.</div>';
|
||||||
|
exit(); // Stop script execution if user_id is not set
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
// Get the security questions and answers from the form
|
||||||
|
$question1 = mysqli_real_escape_string($con, $_POST['security_question1']);
|
||||||
|
$answer1 = strtolower(mysqli_real_escape_string($con, $_POST['security_answer1']));
|
||||||
|
$question2 = mysqli_real_escape_string($con, $_POST['security_question2']);
|
||||||
|
$answer2 = strtolower(mysqli_real_escape_string($con, $_POST['security_answer2']));
|
||||||
|
|
||||||
|
// Get the user ID and user type from the session
|
||||||
|
$user_id = $_SESSION['user_id']; // Use user_id from session
|
||||||
|
$user_type = $_SESSION['user_type']; //Get user type from session
|
||||||
|
$email = $_SESSION['user_email'];
|
||||||
|
$student_id = isset($_SESSION['user_student_id']) ? $_SESSION['user_student_id'] : NULL; //Handle student_id for students
|
||||||
|
|
||||||
|
// Prepare SQL statement
|
||||||
|
if($user_type == 'Student') {
|
||||||
|
$sql = "INSERT INTO password_recovery_security_questions (user_id,user_type, student_id, email, question1, answer1, question2, answer2)
|
||||||
|
VALUES ('$user_id', '$user_type', '$student_id', '$email', '$question1', '$answer1', '$question2', '$answer2')
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
question1='$question1',
|
||||||
|
answer1='$answer1',
|
||||||
|
question2='$question2',
|
||||||
|
answer2='$answer2'";
|
||||||
|
} else {
|
||||||
|
// For non-students (Lecturer, TA, etc.), exclude student_id
|
||||||
|
$sql = "INSERT INTO password_recovery_security_questions (user_id, user_type, email, question1, answer1, question2, answer2)
|
||||||
|
VALUES ('$user_id', '$user_type', '$email', '$question1', '$answer1', '$question2', '$answer2')
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
question1='$question1',
|
||||||
|
answer1='$answer1',
|
||||||
|
question2='$question2',
|
||||||
|
answer2='$answer2'";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the query and check for success
|
||||||
|
if (mysqli_query($con, $sql)) {
|
||||||
|
echo '<div id="alertgood" class="alert alert-success" role="alert">Password recovery details set successfully. Please remember your answers! Redirecting to Courses page...</div>';
|
||||||
|
echo '<script>';
|
||||||
|
echo ' setTimeout(function() {';
|
||||||
|
echo ' var userType = "'. $_SESSION['user_type'] . '";';
|
||||||
|
echo ' if (userType === "Admin") {';
|
||||||
|
echo ' window.location.href = "Admin.php";';
|
||||||
|
echo ' } else {';
|
||||||
|
echo ' window.location.href = "Courses.php";';
|
||||||
|
echo ' }';
|
||||||
|
echo ' }, 2000);';
|
||||||
|
echo '</script>';
|
||||||
|
} else {
|
||||||
|
echo '<div class="alert alert-danger" role="alert">Error: ' . mysqli_error($con) . '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mysqli_close($con);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Security Questions</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary" style="padding-left:180px;padding-right:150px;margin:auto;">
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<a class="navbar-brand" href="#"> <img src="logo.png" style="width:30px;height:30px;" alt="LRR Logo"> LRR </a>
|
||||||
|
</nav>
|
||||||
|
<br/><br/><br/>
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-5"></div>
|
||||||
|
<form action="SecurityQuestions.php" method="POST">
|
||||||
|
<label> Set Password Recovery (Make sure you remember your answers) </label>
|
||||||
|
<div class="mb-3">
|
||||||
|
<br/>
|
||||||
|
<label for="security_question1" class="form-label">Select Security Question 1</label>
|
||||||
|
<select class="form-select" id="security_question1" name="security_question1" required>
|
||||||
|
<option value="">-- Select a question --</option>
|
||||||
|
<option value="What is the name of your first pet?">What is the name of your first pet?</option>
|
||||||
|
<option value="What is your mother's maiden name?">What is your mother's maiden name?</option>
|
||||||
|
<option value="What is the name of the town where you were born?">What is the name of the town where you were born?</option>
|
||||||
|
<option value="What was the name of your first best friend?">What was the name of your first best friend?</option>
|
||||||
|
<option value="What is your favorite book?">What is your favorite book?</option>
|
||||||
|
<option value="What was the make and model of your first car?">What was the make and model of your first car?</option>
|
||||||
|
<!-- Add more options if needed -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="security_answer1" class="form-label">Answer 1</label>
|
||||||
|
<input type="text" class="form-control" id="security_answer1" name="security_answer1" required>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="security_question2" class="form-label">Select Security Question 2</label>
|
||||||
|
<select class="form-select" id="security_question2" name="security_question2" required>
|
||||||
|
<option value="">-- Select a question --</option>
|
||||||
|
<option value="What was the name of your first school?">What was the name of your first school?</option>
|
||||||
|
<option value="What is your favorite movie?">What is your favorite movie?</option>
|
||||||
|
<option value="What was your childhood nickname?">What was your childhood nickname?</option>
|
||||||
|
<option value="What is the name of your favorite teacher?">What is the name of your favorite teacher?</option>
|
||||||
|
<option value="What street did you grow up on?">What street did you grow up on?</option>
|
||||||
|
<option value="What is your favorite food?">What is your favorite food?</option>
|
||||||
|
<!-- Add more options if needed -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="security_answer2" class="form-label">Answer 2</label>
|
||||||
|
<input type="text" class="form-control" id="security_answer2" name="security_answer2" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<button id="submit_recovery" type="submit" class="btn btn-primary">Save Answers</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
Contributed by Aya Boussouf 2025-05-19
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------- System Update & LAMP Stack Installation ----------------------
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
sudo apt install apache2 mysql-server php libapache2-mod-php php-mysql unzip git -y
|
||||||
|
|
||||||
|
# ---------------------- Start and Enable Apache & MySQL Services ----------------------
|
||||||
|
sudo systemctl start apache2
|
||||||
|
sudo systemctl enable apache2
|
||||||
|
|
||||||
|
sudo systemctl start mysql
|
||||||
|
sudo systemctl enable mysql
|
||||||
|
|
||||||
|
# ---------------------- Configure MySQL Root User ----------------------
|
||||||
|
sudo mysql
|
||||||
|
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
exit;
|
||||||
|
|
||||||
|
# ---------------------- Create LRR Database ----------------------
|
||||||
|
mysql -u root -p
|
||||||
|
CREATE DATABASE lrr;
|
||||||
|
exit;
|
||||||
|
|
||||||
|
# ---------------------- Clone LRR Project Repository ----------------------
|
||||||
|
cd /var/www/html/
|
||||||
|
sudo git clone http://118.25.96.118:3000/mrlan/LRR.git
|
||||||
|
sudo chown -R $USER:$USER /var/www/html/LRR
|
||||||
|
|
||||||
|
# ---------------------- Switch to Hui-Organize Branch ----------------------
|
||||||
|
cd /var/www/html/LRR
|
||||||
|
git branch
|
||||||
|
git checkout -b Hui-Organize
|
||||||
|
git pull origin Hui-Organize
|
||||||
|
|
||||||
|
# ---------------------- Import SQL Data into MySQL ----------------------
|
||||||
|
sudo mysql -u root -p lrr < /var/www/html/LRR/lrr_database.sql
|
||||||
|
|
||||||
|
# ---------------------- Create Submission Folder for Assignments ----------------------
|
||||||
|
cd /var/www/
|
||||||
|
sudo mkdir lrr_submission
|
||||||
|
sudo chown -R www-data:www-data lrr_submission
|
||||||
|
sudo chmod -R g+rw lrr_submission
|
||||||
|
|
||||||
|
# ---------------------- Configure Database Credentials in KeepItSafe.txt ----------------------
|
||||||
|
sudo nano /var/www/lrr_submission/KeepItSafe.txt
|
||||||
|
# Add: root,root (username,password for MySQL connection)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------- Configure Apache Virtual Host for LRR ----------------------
|
||||||
|
sudo nano /etc/apache2/sites-available/LRR.conf
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName localhost
|
||||||
|
DocumentRoot /var/www/html
|
||||||
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||||
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
|
</VirtualHost>
|
||||||
|
|
||||||
|
# ---------------------- Enable Apache Config and Rewrite Module ----------------------
|
||||||
|
sudo a2ensite LRR
|
||||||
|
sudo a2enmod rewrite
|
||||||
|
sudo systemctl reload apache2
|
||||||
|
|
||||||
|
# ---------------------- Install Google Chrome (for Selenium Tests) ----------------------
|
||||||
|
cd ~/Downloads
|
||||||
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
|
sudo apt install ./google-chrome-stable_current_amd64.deb
|
||||||
|
|
||||||
|
# ---------------------- Install ChromeDriver ----------------------
|
||||||
|
wget https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.92/linux64/chromedriver-linux64.zip
|
||||||
|
unzip chromedriver-linux64.zip
|
||||||
|
sudo mv chromedriver-linux64/chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# ---------------------- Install Python, pip and Virtual Environment ----------------------
|
||||||
|
sudo apt install python3 python3-pip -y
|
||||||
|
sudo apt install python3-venv -y
|
||||||
|
|
||||||
|
# ---------------------- Create and Activate Virtual Environment for LRR ----------------------
|
||||||
|
cd /var/www/html/LRR
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# ---------------------- Install Required Python Packages ----------------------
|
||||||
|
pip install selenium
|
||||||
|
pip install pytest
|
||||||
|
|
||||||
|
# ---------------------- Run Selenium Tests ----------------------
|
||||||
|
cd test
|
||||||
|
# Make necessary edits in `conftest.py` and `test_lrr.py`.
|
||||||
|
pytest -v SeleniumHui/test_lrr.py
|
||||||
|
|
||||||
|
|
||||||
11
index.php
11
index.php
|
|
@ -41,14 +41,15 @@ if (isset($_SESSION["user_fullname"])) {
|
||||||
<label class="form-text">Don't have an account yet?</label> <a href="signup.php" id="signup_link">Sign up</a>
|
<label class="form-text">Don't have an account yet?</label> <a href="signup.php" id="signup_link">Sign up</a>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<label class="form-text">Forget your password?</label> <a href="recover_password.php">Recover</a>
|
<label class="form-text">Forget your password?</label> <a id="goRecover" href="RecoverPassword.php">Recover</a>
|
||||||
|
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
if(isset($_SESSION['info_login'])) {
|
if(isset($_SESSION['info_login'])) {
|
||||||
// Check if it's a success message (starts with "Success")
|
echo '<hr><div class="alert alert-danger" role="alert">'.$_SESSION['info_login'].'</div>';
|
||||||
$is_success = (strpos($_SESSION['info_login'], 'Success') === 0);
|
|
||||||
$alert_class = $is_success ? 'alert-success' : 'alert-danger';
|
|
||||||
echo '<hr><div class="alert ' . $alert_class . '" role="alert">'.$_SESSION['info_login'].'</div>';
|
|
||||||
$_SESSION['info_login'] = null;
|
$_SESSION['info_login'] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ DELIMITER ;
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `courses_table`
|
-- Table structure for table `courses_table`
|
||||||
--
|
--
|
||||||
|
|
@ -260,7 +259,10 @@ INSERT INTO `students_data` (`ID`, `Student_ID`, `Passport_Number`) VALUES
|
||||||
(1, '201825800054', 'LJ7951632'),
|
(1, '201825800054', 'LJ7951632'),
|
||||||
(2, '201825800050', 'P00581929'),
|
(2, '201825800050', 'P00581929'),
|
||||||
(3, '201632120150', 'FN524516'),
|
(3, '201632120150', 'FN524516'),
|
||||||
(4, '202400000001', 'NA');
|
(4, '202400000001', 'NA'),
|
||||||
|
(5,'201932130101',''),
|
||||||
|
(6,'201920781742','');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
@ -270,7 +272,7 @@ INSERT INTO `students_data` (`ID`, `Student_ID`, `Passport_Number`) VALUES
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE TABLE `users_table` (
|
CREATE TABLE `users_table` (
|
||||||
`User_ID` int(11) NOT NULL,
|
`User_ID` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`Email` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
|
`Email` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`Password` varchar(250) CHARACTER SET utf8 DEFAULT NULL,
|
`Password` varchar(250) CHARACTER SET utf8 DEFAULT NULL,
|
||||||
`HashPassword` varchar(250) COLLATE utf8mb4_bin NOT NULL,
|
`HashPassword` varchar(250) COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
|
@ -278,10 +280,10 @@ CREATE TABLE `users_table` (
|
||||||
`UserType` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
|
`UserType` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`Student_ID` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL,
|
`Student_ID` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`Passport_Number` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
|
`Passport_Number` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`Status` varchar(30) COLLATE utf8mb4_bin NOT NULL DEFAULT 'Active'
|
`Status` varchar(30) COLLATE utf8mb4_bin NOT NULL DEFAULT 'Active',
|
||||||
|
PRIMARY KEY (`User_ID`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Dumping data for table `users_table`
|
-- Dumping data for table `users_table`
|
||||||
--
|
--
|
||||||
|
|
@ -296,8 +298,28 @@ INSERT INTO `users_table` (`User_ID`, `Email`, `Password`, `HashPassword`, `Full
|
||||||
(12, 'mehdi@qq.com', '123', '', 'El-mehdi Houzi', 'Student', '201825800054', 'LJ7951632', 'Active'),
|
(12, 'mehdi@qq.com', '123', '', 'El-mehdi Houzi', 'Student', '201825800054', 'LJ7951632', 'Active'),
|
||||||
(17, 'teecloudy@qq.com', '$2y$10$8WqSK7QI.3YCb2yoclqutOxyGxojncUvzhqLcE8zjlSvjBdcIQ18O', '', 'Ashly Tafadzwa Dhani', 'Student', '201632120150', NULL, 'Active'),
|
(17, 'teecloudy@qq.com', '$2y$10$8WqSK7QI.3YCb2yoclqutOxyGxojncUvzhqLcE8zjlSvjBdcIQ18O', '', 'Ashly Tafadzwa Dhani', 'Student', '201632120150', NULL, 'Active'),
|
||||||
(18, 'ashly@qq.com', 'Testing2', '', 'Ashly 2 Testing', 'Student', '2016321201502', NULL, 'Active'),
|
(18, 'ashly@qq.com', 'Testing2', '', 'Ashly 2 Testing', 'Student', '2016321201502', NULL, 'Active'),
|
||||||
(19, '11@11.11', 'dfdf', '760a8f4f392f1f6bc3ecb118365c6cd039b59fdce96122897d5157970d9c9c129bd73b3c402dbeedd8fe94d319df7bd7de0025c22839fec06631a025ec1e0e69', '11', 'Student', '11', '', 'Active');
|
(19, '11@11.11', 'dfdf', '760a8f4f392f1f6bc3ecb118365c6cd039b59fdce96122897d5157970d9c9c129bd73b3c402dbeedd8fe94d319df7bd7de0025c22839fec06631a025ec1e0e69', '11', 'Student', '11', '', 'Active'),
|
||||||
|
(20,'goodstd@qq.com','$2y$10$RXIjONOK.mw74pSxPrsnGuccQsFq4O4.e71vaxfGHHhtzHREtfqkG','','Good Std','Student','201932130101',NULL,'Active'),(21,'goodstd2@qq.com','$2y$10$kmqrGuZd7hCiiaHFDQr2vObpD7BgEnCKlgQ/EcLHYnsQenMbNKcHy','','Good Std2','Student','201920781742',NULL,'Active');
|
||||||
|
|
||||||
|
CREATE TABLE `password_recovery_security_questions` (
|
||||||
|
`user_id` int NOT NULL,
|
||||||
|
`user_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`student_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
|
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
|
`question1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`answer1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`question2` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`answer2` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
PRIMARY KEY (`user_id`),
|
||||||
|
CONSTRAINT `password_recovery_security_questions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users_table` (`User_ID`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||||
|
|
||||||
|
INSERT INTO `password_recovery_security_questions` VALUES
|
||||||
|
(3,'Admin',NULL,'admin@qq.com','What is the name of your first pet?','mimi','What was the name of your first school?','zjnu'),
|
||||||
|
(7,'Lecturer',NULL,'peter@qq.com','What was the name of your first best friend?','chen','What is your favorite food?','hotpot'),
|
||||||
|
(8,'Lecturer',NULL,'lanhui@qq.com','What is the name of the town where you were born?','jinhua','What is your favorite movie?','titanic'),
|
||||||
|
(9,'Student','201825800050','mohamed@qq.com','What was the make and model of your first car?','audi','What is the name of your favorite teacher?','chen'),
|
||||||
|
(21,'Student','201920781742','goodstd2@qq.com','What is the name of the town where you were born?','wenzhou','What is your favorite food?','hotpot');
|
||||||
--
|
--
|
||||||
-- Indexes for dumped tables
|
-- Indexes for dumped tables
|
||||||
--
|
--
|
||||||
|
|
@ -351,12 +373,6 @@ ALTER TABLE `lab_report_submissions`
|
||||||
ALTER TABLE `students_data`
|
ALTER TABLE `students_data`
|
||||||
ADD PRIMARY KEY (`ID`);
|
ADD PRIMARY KEY (`ID`);
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `users_table`
|
|
||||||
--
|
|
||||||
ALTER TABLE `users_table`
|
|
||||||
ADD PRIMARY KEY (`User_ID`);
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- AUTO_INCREMENT for dumped tables
|
-- AUTO_INCREMENT for dumped tables
|
||||||
--
|
--
|
||||||
|
|
@ -414,33 +430,8 @@ ALTER TABLE `students_data`
|
||||||
--
|
--
|
||||||
ALTER TABLE courses_table
|
ALTER TABLE courses_table
|
||||||
MODIFY Course_Name varchar(500);
|
MODIFY Course_Name varchar(500);
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `users_table`
|
|
||||||
--
|
|
||||||
ALTER TABLE `users_table`
|
|
||||||
MODIFY `User_ID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=20;
|
|
||||||
COMMIT;
|
|
||||||
|
|
||||||
|
|
||||||
-- Table Structure for Passwor_reset_tokens
|
|
||||||
CREATE TABLE `password_reset_tokens` (
|
|
||||||
`id` INT NOT NULL AUTO_INCREMENT,
|
|
||||||
`user_id` INT NOT NULL,
|
|
||||||
`token` VARCHAR(64) NOT NULL,
|
|
||||||
`expires_at` DATETIME NOT NULL,
|
|
||||||
`used` BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE INDEX `token_UNIQUE` (`token` ASC),
|
|
||||||
INDEX `user_id_idx` (`user_id` ASC),
|
|
||||||
INDEX `expires_at_idx` (`expires_at` ASC),
|
|
||||||
CONSTRAINT `fk_password_reset_user_id_users_table`
|
|
||||||
FOREIGN KEY (`user_id`)
|
|
||||||
REFERENCES `users_table` (`User_ID`)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
include 'NoDirectPhpAcess.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include 'Header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<br><br><br>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-5"></div>
|
|
||||||
<div class="col-md-5">
|
|
||||||
<form method="post" action="Script.php">
|
|
||||||
<legend>Recover password</legend>
|
|
||||||
<input type="hidden" name="form_recover_password" value="true"/>
|
|
||||||
Student number
|
|
||||||
<input type="text" name="sno" placeholder="Enter your student number" class="form-control" required="required" value="<?php echo htmlspecialchars($_SESSION['student_number']); ?>"> <br/>
|
|
||||||
Email
|
|
||||||
<input type="text" name="email" placeholder="Enter your email address" class="form-control" required="required" value="<?php echo htmlspecialchars($_SESSION['user_email']); ?>"> <br/>
|
|
||||||
<button type="submit" class="btn btn-primary">Recover</button>
|
|
||||||
</form>
|
|
||||||
</div> </div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
if(isset($_SESSION['info_recover_password'])) {
|
|
||||||
// Check if it's a success message by looking for "Success" at the beginning
|
|
||||||
$is_success = (strpos($_SESSION['info_recover_password'], 'Success') === 0);
|
|
||||||
$alert_class = $is_success ? 'alert-success' : 'alert-danger';
|
|
||||||
|
|
||||||
echo '<hr><div class="alert ' . $alert_class . '" role="alert">' . htmlspecialchars($_SESSION['info_recover_password']) . '</div>';
|
|
||||||
$_SESSION['info_recover_password'] = null;
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
<?php
|
|
||||||
// Simulate a referrer to bypass NoDirectPhpAcess.php's direct access check
|
|
||||||
// This allows password reset links from emails to work correctly
|
|
||||||
if (!isset($_SERVER['HTTP_REFERER'])) {
|
|
||||||
$_SERVER['HTTP_REFERER'] = 'https://' . $_SERVER['HTTP_HOST'] . '/LRR/recover_password.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
include 'NoDirectPhpAcess.php';
|
|
||||||
include 'Header.php';
|
|
||||||
|
|
||||||
// Initialize variables
|
|
||||||
$token_from_url = null;
|
|
||||||
$error_message = null;
|
|
||||||
$show_form = false;
|
|
||||||
$is_success = false; // Define this variable at the start
|
|
||||||
$current_time_str = date('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
if (isset($_GET['token'])) {
|
|
||||||
$token_from_url = mysqli_real_escape_string($con, $_GET['token']);
|
|
||||||
|
|
||||||
// Validate token: Check if it exists, is not expired, and not used
|
|
||||||
$token_validation_query_str = "SELECT * FROM password_reset_tokens WHERE token='$token_from_url'";
|
|
||||||
$token_validation_query = mysqli_query($con, $token_validation_query_str);
|
|
||||||
|
|
||||||
if ($token_validation_query && mysqli_num_rows($token_validation_query) > 0) {
|
|
||||||
$token_data = mysqli_fetch_assoc($token_validation_query);
|
|
||||||
|
|
||||||
if ($token_data['used'] == 1) {
|
|
||||||
$error_message = "This password reset link has already been used. Please request a new one if needed.";
|
|
||||||
} elseif (strtotime($token_data['expires_at']) <= strtotime($current_time_str)) {
|
|
||||||
$error_message = "This password reset link has expired. Please request a new one if needed.";
|
|
||||||
// Optionally, delete the purely expired token now to keep the table clean
|
|
||||||
// mysqli_query($con, "DELETE FROM password_reset_tokens WHERE token='$token_from_url'");
|
|
||||||
} else {
|
|
||||||
// Token is valid and can be used
|
|
||||||
$show_form = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Token was not found in the database
|
|
||||||
$error_message = "Invalid password reset token. It may not exist in our system or has been cleaned up. Please request a new one if needed.";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$error_message = "No reset token provided. Please use the link sent to your email.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set success flag if applicable
|
|
||||||
if (isset($_SESSION['info_reset_password'])) {
|
|
||||||
$is_success = (strpos(strtolower($_SESSION['info_reset_password']), 'success') !== false);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<br><br><br>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6 col-md-offset-3">
|
|
||||||
<legend>Reset Your Password</legend>
|
|
||||||
|
|
||||||
<?php if (isset($_SESSION['info_reset_password'])): ?>
|
|
||||||
<div class="alert alert-<?php echo $is_success ? 'success' : 'danger'; ?>" role="alert">
|
|
||||||
<?php echo htmlspecialchars($_SESSION['info_reset_password']); ?>
|
|
||||||
</div>
|
|
||||||
<?php if ($is_success): ?>
|
|
||||||
<p><a href="index.php">Click here to Login</a></p>
|
|
||||||
<?php else: ?>
|
|
||||||
<p><a href="recover_password.php">Request a new password reset link</a></p>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if ($error_message && !isset($_SESSION['info_reset_password'])): /* Show general token errors if no processing message exists */ ?>
|
|
||||||
<div class="alert alert-danger" role="alert"><?php echo htmlspecialchars($error_message); ?></div>
|
|
||||||
<p><a href="recover_password.php">Request a new password reset link</a></p>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if ($show_form && !isset($_SESSION['info_reset_password'])): ?>
|
|
||||||
<form method="post" action="Script.php">
|
|
||||||
<input type="hidden" name="form_reset_password" value="true"/>
|
|
||||||
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token_from_url); ?>"/>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="new_password">New Password</label>
|
|
||||||
<input type="password" name="new_password" id="new_password" class="form-control" required minlength="8" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^\w\d\s:])(?!.*\s).{8,}" title="Password must be at least 8 characters long and include uppercase letters, lowercase letters, numbers, and special characters.">
|
|
||||||
<small class="form-text text-muted">Must be at least 8 characters, include uppercase, lowercase, number, and special character.</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="confirm_password">Confirm New Password</label>
|
|
||||||
<input type="password" name="confirm_password" id="confirm_password" class="form-control" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Reset Password</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// Only unset after displaying
|
|
||||||
if (isset($_SESSION['info_reset_password'])) {
|
|
||||||
unset($_SESSION['info_reset_password']);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include 'Footer.php';
|
|
||||||
?>
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
from helper import login, logout
|
||||||
|
import time
|
||||||
|
import pytest
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
from selenium.webdriver.support.ui import Select
|
||||||
|
|
||||||
|
def test_attempt_recovery_with_unanswered_security_questions(driver, url, restore_database):
|
||||||
|
# Student goodstd@qq.com with no answered questions
|
||||||
|
driver.get(url)
|
||||||
|
driver.maximize_window()
|
||||||
|
|
||||||
|
elem = driver.find_element(By.ID, 'goRecover')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'email')
|
||||||
|
elem.send_keys("goodstd@qq.com")
|
||||||
|
elem = driver.find_element(By.ID, 'rec')
|
||||||
|
elem.click()
|
||||||
|
|
||||||
|
wait = WebDriverWait(driver, 10)
|
||||||
|
alert_elem = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "alert-warning")))
|
||||||
|
assert "no security questions found for this user." in alert_elem.text.lower()
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
def test_set_security_questions(driver, url, restore_database):
|
||||||
|
# Student goodstd@qq.com logs in
|
||||||
|
driver.get(url)
|
||||||
|
driver.maximize_window()
|
||||||
|
login(driver, url, 'goodstd@qq.com', '[123Abc]')
|
||||||
|
question1 = driver.find_element(By.ID, 'security_question1')
|
||||||
|
question2 = driver.find_element(By.ID, 'security_question2')
|
||||||
|
select_question1 = Select(question1)
|
||||||
|
select_question1.select_by_index(1)
|
||||||
|
select_question2 = Select(question2)
|
||||||
|
select_question2.select_by_index(2)
|
||||||
|
answer1 = driver.find_element(By.ID, 'security_answer1')
|
||||||
|
answer1.send_keys("mImI")
|
||||||
|
answer2 = driver.find_element(By.ID, 'security_answer2')
|
||||||
|
answer2.send_keys("TitaNic")
|
||||||
|
submit_button = driver.find_element(By.ID, 'submit_recovery')
|
||||||
|
submit_button.click()
|
||||||
|
|
||||||
|
# Assertion: Check if password recovery confirmation or success message appears
|
||||||
|
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'alertgood')))
|
||||||
|
recovery_success = driver.find_element(By.ID, 'alertgood')
|
||||||
|
assert recovery_success.is_displayed(), "Password recovery failed or confirmation message not displayed."
|
||||||
|
|
||||||
|
logout(driver)
|
||||||
|
|
||||||
|
#Log in Student account
|
||||||
|
login(driver, url, 'goodstd@qq.com', '[123Abc]')
|
||||||
|
elems = driver.find_elements(By.CLASS_NAME, 'nav-link')
|
||||||
|
assert 'Student ID' in elems[0].text
|
||||||
|
assert 'Good Std' in elems[0].text
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
def test_password_recover_wrong_answers(driver, url, restore_database):
|
||||||
|
# Student goodstd2@qq.com recover password --> wrong answers
|
||||||
|
driver.get(url)
|
||||||
|
driver.maximize_window()
|
||||||
|
elem = driver.find_element(By.ID, 'goRecover')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'email')
|
||||||
|
elem.send_keys("goodstd2@qq.com")
|
||||||
|
elem = driver.find_element(By.ID, 'rec')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'answer1')
|
||||||
|
elem.send_keys("wrong")
|
||||||
|
elem = driver.find_element(By.NAME, 'answer2')
|
||||||
|
elem.send_keys("wrong")
|
||||||
|
elem = driver.find_element(By.ID, 'sub')
|
||||||
|
elem.click()
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
def test_recover_password_with_weak_password(driver, url, restore_database):
|
||||||
|
driver.get(url)
|
||||||
|
driver.maximize_window()
|
||||||
|
elem = driver.find_element(By.ID, 'goRecover')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'email')
|
||||||
|
elem.send_keys("goodstd2@qq.com")
|
||||||
|
elem = driver.find_element(By.ID, 'rec')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'answer1')
|
||||||
|
elem.send_keys("Wenzhou")
|
||||||
|
elem = driver.find_element(By.NAME, 'answer2')
|
||||||
|
elem.send_keys("HotPot")
|
||||||
|
elem = driver.find_element(By.ID, 'sub')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'new_password')
|
||||||
|
elem.send_keys("123")
|
||||||
|
elem = driver.find_element(By.NAME, 'confirm_password')
|
||||||
|
elem.send_keys("123")
|
||||||
|
elem = driver.find_element(By.ID, "butt")
|
||||||
|
elem.click()
|
||||||
|
|
||||||
|
wait = WebDriverWait(driver, 10)
|
||||||
|
alert_elem = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "alert-danger")))
|
||||||
|
assert "password must be at least 8 characters long and include uppercase and lowercase letters, numbers, and special characters." in alert_elem.text.lower()
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
def test_recover_password_successfully(driver, url, restore_database):
|
||||||
|
# Student goodstd2@qq.com recover password ---> correct answers
|
||||||
|
driver.get(url)
|
||||||
|
driver.maximize_window()
|
||||||
|
elem = driver.find_element(By.ID, 'goRecover')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'email')
|
||||||
|
elem.send_keys("goodstd2@qq.com")
|
||||||
|
elem = driver.find_element(By.ID, 'rec')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'answer1')
|
||||||
|
elem.send_keys("wenzhou")
|
||||||
|
elem = driver.find_element(By.NAME, 'answer2')
|
||||||
|
elem.send_keys("hotpot")
|
||||||
|
elem = driver.find_element(By.ID, 'sub')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'new_password')
|
||||||
|
elem.send_keys("123Abc!!")
|
||||||
|
elem = driver.find_element(By.NAME, 'confirm_password')
|
||||||
|
elem.send_keys("123Abc!!")
|
||||||
|
elem = driver.find_element(By.ID, "butt")
|
||||||
|
elem.click()
|
||||||
|
|
||||||
|
#Log in Student account
|
||||||
|
login(driver, url, 'goodstd2@qq.com', '123Abc!!')
|
||||||
|
elems = driver.find_elements(By.CLASS_NAME, 'nav-link')
|
||||||
|
assert 'Student ID' in elems[0].text
|
||||||
|
assert 'Good Std2' in elems[0].text
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
def test_recovery_invalid_user_cannot_recover(driver, url, restore_database):
|
||||||
|
# Unrecognizable user cannot recover
|
||||||
|
driver.get(url)
|
||||||
|
driver.maximize_window()
|
||||||
|
elem = driver.find_element(By.ID, 'goRecover')
|
||||||
|
elem.click()
|
||||||
|
elem = driver.find_element(By.NAME, 'email')
|
||||||
|
elem.send_keys("goodstd3@qq.com")
|
||||||
|
elem = driver.find_element(By.ID, 'rec')
|
||||||
|
elem.click()
|
||||||
|
|
||||||
|
wait = WebDriverWait(driver, 10)
|
||||||
|
alert_elem = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "alert-danger")))
|
||||||
|
assert "email address is not recognized." in alert_elem.text.lower()
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,9 +30,12 @@ def test_admin_can_create_lecturer_account(driver, url, admin_username, admin_pa
|
||||||
|
|
||||||
# Log in Lecturer account
|
# Log in Lecturer account
|
||||||
login(driver, url, 'mrlan@qq.com', '123Abc!!')
|
login(driver, url, 'mrlan@qq.com', '123Abc!!')
|
||||||
elems = driver.find_elements(By.CLASS_NAME, 'nav-link')
|
#elems = driver.find_elements(By.CLASS_NAME, 'nav-link')
|
||||||
assert '(Lecturer)' in elems[0].text
|
#assert '(Lecturer)' in elems[0].text
|
||||||
assert 'Mr Lan' in elems[0].text
|
#assert 'Mr Lan' in elems[0].text
|
||||||
|
wait = WebDriverWait(driver, 10)
|
||||||
|
wait.until(EC.url_contains('SecurityQuestions.php'))
|
||||||
|
|
||||||
driver.quit()
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -73,7 +76,7 @@ def test_lecturer_can_post_assignment(driver, url, restore_database):
|
||||||
)
|
)
|
||||||
elem.click()
|
elem.click()
|
||||||
elem = driver.find_element(By.NAME, 'deadlinedate')
|
elem = driver.find_element(By.NAME, 'deadlinedate')
|
||||||
elem.send_keys('002024/12/30')
|
elem.send_keys('002025/05/30')
|
||||||
elem = driver.find_element(By.NAME, 'deadlinetime')
|
elem = driver.find_element(By.NAME, 'deadlinetime')
|
||||||
elem.send_keys('23:59')
|
elem.send_keys('23:59')
|
||||||
elem = driver.find_element(By.NAME, 'title')
|
elem = driver.find_element(By.NAME, 'title')
|
||||||
|
|
@ -91,7 +94,7 @@ def test_lecturer_can_post_assignment(driver, url, restore_database):
|
||||||
elem = driver.find_element(By.CLASS_NAME, 'card-title')
|
elem = driver.find_element(By.CLASS_NAME, 'card-title')
|
||||||
assert 'Take-home quiz 1 (10 Marks, Individual)' in elem.text
|
assert 'Take-home quiz 1 (10 Marks, Individual)' in elem.text
|
||||||
elem = driver.find_element(By.CLASS_NAME, 'text-muted')
|
elem = driver.find_element(By.CLASS_NAME, 'text-muted')
|
||||||
assert 'Deadline: 2024-12-30' in elem.text
|
assert 'Deadline: 2025-05-30' in elem.text
|
||||||
driver.quit()
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -157,13 +160,15 @@ def test_student_with_valid_student_number_can_sign_up(driver, url, restore_data
|
||||||
elem.send_keys('[123Abc]')
|
elem.send_keys('[123Abc]')
|
||||||
elem = driver.find_element(By.ID, 'signup_btn')
|
elem = driver.find_element(By.ID, 'signup_btn')
|
||||||
elem.click()
|
elem.click()
|
||||||
logout(driver)
|
driver.get(url + 'logout.php')
|
||||||
|
|
||||||
# Log in Student account
|
# Log in Student account
|
||||||
login(driver, url, '202400000001', '[123Abc]')
|
login(driver, url, '202400000001', '[123Abc]')
|
||||||
elems = driver.find_elements(By.CLASS_NAME, 'nav-link')
|
#elems = driver.find_elements(By.CLASS_NAME, 'nav-link')
|
||||||
assert 'Student ID' in elems[0].text
|
#assert 'Student ID' in elems[0].text
|
||||||
assert 'Good Student' in elems[0].text
|
#assert 'Good Student' in elems[0].text
|
||||||
|
wait = WebDriverWait(driver, 10)
|
||||||
|
wait.until(EC.url_contains('SecurityQuestions.php'))
|
||||||
driver.quit()
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -245,6 +250,7 @@ def test_student_can_join_course(driver, url, restore_database):
|
||||||
assert 'CSC1111' in elems[0].text
|
assert 'CSC1111' in elems[0].text
|
||||||
assert 'Project Management' in elems[0].text
|
assert 'Project Management' in elems[0].text
|
||||||
assert 'Joined' in elems[0].text
|
assert 'Joined' in elems[0].text
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
def test_student_can_submit_assignment(driver, url, restore_database):
|
def test_student_can_submit_assignment(driver, url, restore_database):
|
||||||
|
|
@ -264,7 +270,7 @@ def test_student_can_submit_assignment(driver, url, restore_database):
|
||||||
elem = driver.find_element(By.NAME, 'title')
|
elem = driver.find_element(By.NAME, 'title')
|
||||||
elem.send_keys('Assignment submission from Mohamed')
|
elem.send_keys('Assignment submission from Mohamed')
|
||||||
elem = driver.find_element(By.NAME, 'attachment1')
|
elem = driver.find_element(By.NAME, 'attachment1')
|
||||||
elem.send_keys('/home/mrlan/Downloads/test/SeleniumHui/helper.py') # attach a file
|
elem.send_keys('/var/www/html/LRR/test/SeleniumHui/helper.py') # attach a file
|
||||||
elem = driver.find_element(By.XPATH, '//form/button')
|
elem = driver.find_element(By.XPATH, '//form/button')
|
||||||
elem.click()
|
elem.click()
|
||||||
|
|
||||||
|
|
@ -278,6 +284,8 @@ def test_student_can_submit_assignment(driver, url, restore_database):
|
||||||
assert 'SUBMITTED' in elem.text
|
assert 'SUBMITTED' in elem.text
|
||||||
assert 'helper.py' in elem.text
|
assert 'helper.py' in elem.text
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
def test_student_can_request_remarking(driver, url, restore_database):
|
def test_student_can_request_remarking(driver, url, restore_database):
|
||||||
# Student logs in
|
# Student logs in
|
||||||
|
|
@ -303,6 +311,8 @@ def test_student_can_request_remarking(driver, url, restore_database):
|
||||||
elem = driver.find_element(By.XPATH, '//div[@id="menu4"]/div/div/p/span')
|
elem = driver.find_element(By.XPATH, '//div[@id="menu4"]/div/div/p/span')
|
||||||
assert 'Remarking request sent' == elem.text
|
assert 'Remarking request sent' == elem.text
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
def test_lecturer_can_mark_assignment(driver, url, restore_database):
|
def test_lecturer_can_mark_assignment(driver, url, restore_database):
|
||||||
# Lecturer lanhui@qq.com logs in
|
# Lecturer lanhui@qq.com logs in
|
||||||
|
|
@ -334,6 +344,8 @@ def test_lecturer_can_mark_assignment(driver, url, restore_database):
|
||||||
elem = driver.find_element(By.XPATH, "//div[@id='menu2']/div/b")
|
elem = driver.find_element(By.XPATH, "//div[@id='menu2']/div/b")
|
||||||
assert 'Reading 1 submission' in elem.text
|
assert 'Reading 1 submission' in elem.text
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
def test_lecturer_cannot_see_tas_not_from_his_course(driver, url, restore_database):
|
def test_lecturer_cannot_see_tas_not_from_his_course(driver, url, restore_database):
|
||||||
# Lecturer lanhui@qq.com logs in
|
# Lecturer lanhui@qq.com logs in
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,249 +0,0 @@
|
||||||
from helper import login, logout
|
|
||||||
import time
|
|
||||||
import pytest
|
|
||||||
from selenium.webdriver.common.by import By
|
|
||||||
from selenium.webdriver.support.wait import WebDriverWait
|
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
|
||||||
|
|
||||||
|
|
||||||
# def test_user_can_request_password_reset(driver, url, restore_database):
|
|
||||||
# """Test that a user can successfully request a password reset"""
|
|
||||||
# driver.maximize_window()
|
|
||||||
|
|
||||||
# # Start from the index page
|
|
||||||
# driver.get(url + "/index.php")
|
|
||||||
|
|
||||||
# # Click the "Recover" link to navigate to password recovery page
|
|
||||||
# recover_link = WebDriverWait(driver, 10).until(
|
|
||||||
# EC.element_to_be_clickable((By.LINK_TEXT, "Recover"))
|
|
||||||
# )
|
|
||||||
# recover_link.click()
|
|
||||||
|
|
||||||
# # Verify we're now on the correct page
|
|
||||||
# WebDriverWait(driver, 10).until(
|
|
||||||
# lambda d: "recover password" in d.page_source.lower()
|
|
||||||
# )
|
|
||||||
# assert "recover password" in driver.page_source.lower()
|
|
||||||
|
|
||||||
# # Fill out the recovery form with existing user credentials (mohamed@qq.com / 201825800050)
|
|
||||||
# student_number_field = WebDriverWait(driver, 10).until(
|
|
||||||
# EC.presence_of_element_located((By.NAME, "sno")) # Fixed: correct field name
|
|
||||||
# )
|
|
||||||
# student_number_field.clear()
|
|
||||||
# student_number_field.send_keys("201825800050")
|
|
||||||
|
|
||||||
# email_field = driver.find_element(By.NAME, "email")
|
|
||||||
# email_field.clear()
|
|
||||||
# email_field.send_keys("mohamed@qq.com")
|
|
||||||
|
|
||||||
# # Submit the form
|
|
||||||
# # Submit the form - with better error handling and waiting
|
|
||||||
# # Submit the form - click the button by its text content or CSS selector
|
|
||||||
# submit_button = WebDriverWait(driver, 10).until(
|
|
||||||
# EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Recover')]"))
|
|
||||||
# )
|
|
||||||
# submit_button.click()
|
|
||||||
|
|
||||||
# # Wait for and verify success message appears in the UI
|
|
||||||
# WebDriverWait(driver, 10).until(
|
|
||||||
# lambda d: "success" in d.page_source.lower() or
|
|
||||||
# "sent" in d.page_source.lower() or
|
|
||||||
# "link has been sent" in d.page_source.lower()
|
|
||||||
# )
|
|
||||||
|
|
||||||
# # Check that the page shows a success message
|
|
||||||
# page_source = driver.page_source.lower()
|
|
||||||
# assert ("success" in page_source or
|
|
||||||
# "sent" in page_source or
|
|
||||||
# "link has been sent" in page_source)
|
|
||||||
|
|
||||||
# driver.quit()
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_reset_password(driver, url, restore_database):
|
|
||||||
"""Test the complete password reset flow using a mock token"""
|
|
||||||
driver.maximize_window()
|
|
||||||
|
|
||||||
# Step 1: Test accessing reset form without token (should show error)
|
|
||||||
driver.get(url + "/index.php")
|
|
||||||
driver.get(url + "/reset_password_form.php")
|
|
||||||
|
|
||||||
# Should show error message when no token is provided
|
|
||||||
WebDriverWait(driver, 10).until(
|
|
||||||
lambda d: "token" in d.page_source.lower() or "error" in d.page_source.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
page_source = driver.page_source.lower()
|
|
||||||
assert ("no reset token" in page_source or
|
|
||||||
"invalid" in page_source or
|
|
||||||
"error" in page_source)
|
|
||||||
print("✓ Test passed: Error shown when no token provided")
|
|
||||||
|
|
||||||
# Step 2: Test accessing reset form with invalid token (should show error)
|
|
||||||
driver.get(url + "/index.php")
|
|
||||||
invalid_token = "invalid_token_12345"
|
|
||||||
reset_url = f"{url}/reset_password_form.php?token={invalid_token}"
|
|
||||||
driver.get(reset_url)
|
|
||||||
|
|
||||||
# Wait for and verify error message for invalid token
|
|
||||||
WebDriverWait(driver, 10).until(
|
|
||||||
lambda d: "invalid" in d.page_source.lower() or
|
|
||||||
"error" in d.page_source.lower() or
|
|
||||||
"token" in d.page_source.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
page_source = driver.page_source.lower()
|
|
||||||
assert ("invalid" in page_source or
|
|
||||||
"error" in page_source or
|
|
||||||
"not exist" in page_source or
|
|
||||||
"expired" in page_source)
|
|
||||||
print("✓ Test passed: Error shown for invalid token")
|
|
||||||
|
|
||||||
# Step 3: Test password reset with valid mock token
|
|
||||||
# For testing purposes, we'll create a mock token that represents a valid scenario
|
|
||||||
# In a real implementation, this token would be generated by the password reset request
|
|
||||||
driver.get(url + "/index.php")
|
|
||||||
|
|
||||||
# Mock a valid token (in real scenario, this would come from database/email)
|
|
||||||
mock_valid_token = "mock_valid_token_for_testing_123456"
|
|
||||||
reset_url_valid = f"{url}/reset_password_form.php?token={mock_valid_token}"
|
|
||||||
driver.get(reset_url_valid)
|
|
||||||
|
|
||||||
# Check if password reset form is shown or if token validation occurs
|
|
||||||
try:
|
|
||||||
# Wait for either password form fields or error message
|
|
||||||
WebDriverWait(driver, 10).until(
|
|
||||||
lambda d: ("new_password" in d.page_source.lower() and "confirm_password" in d.page_source.lower()) or
|
|
||||||
"invalid" in d.page_source.lower() or
|
|
||||||
"error" in d.page_source.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
if "new_password" in driver.page_source.lower() and "confirm_password" in driver.page_source.lower():
|
|
||||||
# Password form is shown - test password reset functionality
|
|
||||||
print("✓ Test scenario: Password reset form is accessible")
|
|
||||||
|
|
||||||
# Step 4: Test password mismatch validation
|
|
||||||
password_field = driver.find_element(By.NAME, "new_password")
|
|
||||||
confirm_password_field = driver.find_element(By.NAME, "confirm_password")
|
|
||||||
|
|
||||||
password_field.clear()
|
|
||||||
password_field.send_keys("Password123!")
|
|
||||||
|
|
||||||
confirm_password_field.clear()
|
|
||||||
confirm_password_field.send_keys("DifferentPassword123!") # Intentionally different
|
|
||||||
|
|
||||||
# Submit the form
|
|
||||||
submit_button = driver.find_element(By.NAME, "form_reset_password")
|
|
||||||
submit_button.click()
|
|
||||||
|
|
||||||
# Wait for and verify mismatch error message
|
|
||||||
WebDriverWait(driver, 10).until(
|
|
||||||
lambda d: "match" in d.page_source.lower() or
|
|
||||||
"error" in d.page_source.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
page_source = driver.page_source.lower()
|
|
||||||
assert ("match" in page_source or
|
|
||||||
"do not match" in page_source or
|
|
||||||
"error" in page_source)
|
|
||||||
print("✓ Test passed: Password mismatch validation works")
|
|
||||||
|
|
||||||
# Step 5: Test successful password reset with matching passwords
|
|
||||||
# Navigate back to the reset form
|
|
||||||
driver.get(reset_url_valid)
|
|
||||||
|
|
||||||
# Wait for form to load again
|
|
||||||
password_field = WebDriverWait(driver, 10).until(
|
|
||||||
EC.presence_of_element_located((By.NAME, "new_password"))
|
|
||||||
)
|
|
||||||
confirm_password_field = driver.find_element(By.NAME, "confirm_password")
|
|
||||||
|
|
||||||
# Enter matching passwords
|
|
||||||
new_password = "NewPassword123!"
|
|
||||||
password_field.clear()
|
|
||||||
password_field.send_keys(new_password)
|
|
||||||
|
|
||||||
confirm_password_field.clear()
|
|
||||||
confirm_password_field.send_keys(new_password) # Same password
|
|
||||||
|
|
||||||
# Submit the form
|
|
||||||
submit_button = driver.find_element(By.NAME, "form_reset_password")
|
|
||||||
submit_button.click()
|
|
||||||
|
|
||||||
# Wait for success message or redirect to login page
|
|
||||||
WebDriverWait(driver, 15).until(
|
|
||||||
lambda d: "success" in d.page_source.lower() or
|
|
||||||
"password has been reset" in d.page_source.lower() or
|
|
||||||
"sign in" in d.page_source.lower() or
|
|
||||||
"login" in d.page_source.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
page_source = driver.page_source.lower()
|
|
||||||
|
|
||||||
# Check if we're redirected to login page with success message
|
|
||||||
if "sign in" in page_source or "login" in page_source:
|
|
||||||
# We're on the login page - check for success message
|
|
||||||
assert ("success" in page_source or
|
|
||||||
"password has been reset" in page_source or
|
|
||||||
"reset successfully" in page_source)
|
|
||||||
print("✓ Test passed: Successfully redirected to login page with success message")
|
|
||||||
|
|
||||||
# Step 6: Test login with new password
|
|
||||||
# Fill in login form with new password
|
|
||||||
user_field = WebDriverWait(driver, 10).until(
|
|
||||||
EC.presence_of_element_located((By.ID, "user_name"))
|
|
||||||
)
|
|
||||||
password_login_field = driver.find_element(By.ID, "user_password")
|
|
||||||
|
|
||||||
user_field.clear()
|
|
||||||
user_field.send_keys("mohamed@qq.com")
|
|
||||||
|
|
||||||
password_login_field.clear()
|
|
||||||
password_login_field.send_keys(new_password)
|
|
||||||
|
|
||||||
# Submit login form
|
|
||||||
login_button = driver.find_element(By.ID, "login_btn")
|
|
||||||
login_button.click()
|
|
||||||
|
|
||||||
# Wait for successful login redirect or error
|
|
||||||
WebDriverWait(driver, 10).until(
|
|
||||||
lambda d: "courses" in d.page_source.lower() or
|
|
||||||
"dashboard" in d.page_source.lower() or
|
|
||||||
"welcome" in d.page_source.lower() or
|
|
||||||
"error" in d.page_source.lower() or
|
|
||||||
"wrong" in d.page_source.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
final_page_source = driver.page_source.lower()
|
|
||||||
if ("courses" in final_page_source or
|
|
||||||
"dashboard" in final_page_source or
|
|
||||||
"welcome" in final_page_source):
|
|
||||||
print("✓ Test passed: Login successful with new password")
|
|
||||||
else:
|
|
||||||
print("ℹ Note: Login test completed (password reset functionality verified)")
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Success message shown on reset form page
|
|
||||||
assert ("success" in page_source or
|
|
||||||
"password has been reset" in page_source or
|
|
||||||
"reset successfully" in page_source)
|
|
||||||
print("✓ Test passed: Password reset success message shown")
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Token validation failed - this is expected for mock token
|
|
||||||
page_source = driver.page_source.lower()
|
|
||||||
assert ("invalid" in page_source or
|
|
||||||
"error" in page_source or
|
|
||||||
"token" in page_source)
|
|
||||||
print("✓ Test passed: Mock token properly rejected (expected behavior)")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
# If password form is not accessible due to token validation, that's acceptable
|
|
||||||
page_source = driver.page_source.lower()
|
|
||||||
assert ("invalid" in page_source or
|
|
||||||
"error" in page_source or
|
|
||||||
"token" in page_source)
|
|
||||||
print("✓ Test passed: Token validation working (mock token rejected as expected)")
|
|
||||||
|
|
||||||
print("✓ Complete password reset test flow completed successfully")
|
|
||||||
driver.quit()
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
========================== Teacher Provided tests results ===========================
|
|
||||||
|
|
||||||
pytest SeleniumHui/test_lrr.py
|
|
||||||
========================== test session starts ===========================
|
|
||||||
platform darwin -- Python 3.9.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
|
|
||||||
rootdir: /Applications/XAMPP/xamppfiles/htdocs/LRR/test
|
|
||||||
plugins: metadata-2.0.1, variables-1.9.0, base-url-1.4.2, html-3.1.1, mock-3.14.0
|
|
||||||
collected 12 items
|
|
||||||
|
|
||||||
SeleniumHui/test_lrr.py ............ [100%]
|
|
||||||
|
|
||||||
===================== 12 passed in 141.63s (0:02:21) =====================
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
========================== Test results for our new email password recovery restuls ===========================
|
|
||||||
pytest SeleniumZayid/test_email_password_recovery.py
|
|
||||||
========================== test session starts ===========================
|
|
||||||
platform darwin -- Python 3.9.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
|
|
||||||
rootdir: /Applications/XAMPP/xamppfiles/htdocs/LRR/test
|
|
||||||
plugins: metadata-2.0.1, variables-1.9.0, base-url-1.4.2, html-3.1.1, mock-3.14.0
|
|
||||||
collected 2 items
|
|
||||||
|
|
||||||
SeleniumZayid/test_email_password_recovery.py .. [100%]
|
|
||||||
|
|
||||||
=========================== 2 passed in 22.55s ===========================
|
|
||||||
Loading…
Reference in New Issue