diff --git a/Sean/models/User.js b/Sean/models/User.js index ab12ee9..8cda275 100644 --- a/Sean/models/User.js +++ b/Sean/models/User.js @@ -1,35 +1,50 @@ -// models/User.js const { Sequelize, DataTypes } = require('sequelize'); -const sequelize = new Sequelize(process.env.database, process.env.user, process.env.password, { - host: process.env.host, - dialect: 'mysql', - timezone: 'Z', // Set the timezone to UTC -}); +const bcrypt = require('bcrypt'); -const User = sequelize.define('User', { - name: { - type: DataTypes.STRING, - allowNull: false, - }, - username: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - }, - password: { - type: DataTypes.STRING, - allowNull: false, - }, - jobTitle: { - type: DataTypes.STRING, - allowNull: false, - }, -}); +module.exports = (sequelize) => { + const User = sequelize.define('users', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + }, + jobTitle: { + type: DataTypes.STRING, + allowNull: false, + }, + reset_token: { + type: DataTypes.STRING, + }, + reset_token_expiry: { + type: DataTypes.DATE, + }, + }, { + hooks: { + beforeCreate: async (user) => { + user.password = await bcrypt.hash(user.password, 10); + }, + }, + timestamps: false, // Disabling timestamps here + }); -module.exports = User; + return User; +}; diff --git a/Sean/models/userLogs.js b/Sean/models/userLogs.js new file mode 100644 index 0000000..672bfe6 --- /dev/null +++ b/Sean/models/userLogs.js @@ -0,0 +1,30 @@ +// userLogs.js + +const { Sequelize, DataTypes } = require('sequelize'); + +module.exports = (sequelize) => { + const userLogs = sequelize.define('user_logs', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + }, + activity: { + type: DataTypes.STRING, + allowNull: false, + }, + timestamp: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }, { + timestamps: false, // Disabling timestamps + }); + + return userLogs; +}; diff --git a/Sean/modules/mysql.js b/Sean/modules/mysql.js index 2501d2d..cc53850 100644 --- a/Sean/modules/mysql.js +++ b/Sean/modules/mysql.js @@ -2,47 +2,47 @@ const mysql = require("mysql2"); const path = require("path"); require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) const fs = require('fs'); -/* -const mysqlConfig = { - host: process.env.host, - user: process.env.user, - password: process.env.password, - database: process.env.database, - timezone: "Z", // Set the timezone to UTC -}; +const UserModel = require('../models/User');// Adjust the path based on your project structure +const { Sequelize } = require('sequelize'); -const connection = mysql.createConnection(mysqlConfig); -connection.connect((err) => { - if (err) { - console.error("Error connecting to MySQL:", err); - return; - } - console.log("Connected to MySQL"); -}); -*/ -const connection = mysql.createConnection({ - host: process.env.host, - user: process.env.DB_USER, - password: process.env.DB_PASS, - database: "adminusers", - timezone: "Z", // Set the timezone to UTC - ssl: { - ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')), + + const sequelize = new Sequelize( + "adminusers", + process.env.DB_USER, + process.env.DB_PASS, + { + host: "mpsqldatabasean.mysql.database.azure.com", + dialect: 'mysql', + // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; + attributeBehavior: 'escape', + dialectOptions: { + ssl: { + ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')), - } - }); -/* -const connection = mysql.createConnection({ - host: process.env.host, - user: process.env.DB_USER, - password: process.env.DB_PASS, - database: "adminusers", - timezone: "Z", // Set the timezone to UTC - }); -*/ - -module.exports = { connection }; + }, + + }, + }, + + + ); + + sequelize.authenticate().then(() => { + console.log('Connection has been established successfully.'); + }).catch((error) => { + console.error('Unable to connect to the database: ', error); + }); + + const User = UserModel(sequelize); + + // Synchronize the models with the database + sequelize.sync(); + + module.exports = { + sequelize, + User, + }; diff --git a/Sean/server.js b/Sean/server.js index 3b36cf4..00b1949 100644 --- a/Sean/server.js +++ b/Sean/server.js @@ -9,10 +9,13 @@ const nodemailer = require("nodemailer"); const otpGenerator = require('otp-generator'); const { body, validationResult } = require('express-validator'); const validator = require('validator'); +const { format } = require('date-fns'); +const { Sequelize } = require('sequelize'); const { transporter } = require("./modules/nodeMailer"); const { connection } = require("./modules/mysql"); - +const { sequelize, User } = require("./modules/mysql"); +const userLogs= require('./models/userLogs')(sequelize); // Adjust the path based on your project structure const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); @@ -77,57 +80,6 @@ app.get("/login", (req, res) => { res.render("login", { error: null }); }); -const logActivity = async (username, success, message) => { - try { - if (!username) { - console.error("Error logging activity: Username is null or undefined"); - return; - } - - const activity = success ? `successful login: ${message}` : `unsuccessful login: ${message}`; - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - const logParams = [username, activity]; - - connection.query(logSql, logParams, (error, results) => { - if (error) { - console.error("Error executing logSql:", error); - // Handle error (you may want to log it or take other appropriate actions) - } else { - console.log("Activity logged successfully"); - } - }); - } catch (error) { - console.error("Error in logActivity function:", error); - // Handle error (you may want to log it or take other appropriate actions) - } - }; - - const logLogoutActivity = async (username, success, message) => { - try { - if (!username) { - console.error("Error logging activity: Username is null or undefined"); - return; - } - - const activity = success ? `successful logout: ${message}` : `unsuccessful logout: ${message}`; - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - const logParams = [username, activity]; - - connection.query(logSql, logParams, (error, results) => { - if (error) { - console.error("Error executing logSql:", error); - // Handle error (you may want to log it or take other appropriate actions) - } else { - console.log("Activity logged successfully"); - } - }); - } catch (error) { - console.error("Error in logActivity function:", error); - // Handle error (you may want to log it or take other appropriate actions) - } - }; const limiter = rateLimit({ @@ -135,167 +87,162 @@ const logActivity = async (username, success, message) => { max: 5, // limit each IP to 3 requests per windowMs message: 'Too many login attempts from this IP, please try again later.', }); + app.use('/login', limiter); - app.post('/login', [ - body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'), - body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'), + +app.post('/login', [ + body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'), + body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'), ], async (req, res) => { - try { - const errors = validationResult(req); + try { + const errors = validationResult(req); - if (!errors.isEmpty()) { - // Handle validation errors, e.g., return an error message to the client - return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken }); + if (!errors.isEmpty()) { + return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken }); + } + + let { username, password } = req.body; + username = username.trim(); + + const user = await User.findOne({ where: { username } }); + + if (user) { + const isLoginSuccessful = await bcrypt.compare(password, user.password); + + if (isLoginSuccessful) { + await userLogs.create({ username, success: true, activity: "Credentials entered correctly" }); + + + const { otp, expirationTime } = generateOTP(); + + req.session.otp = otp; + req.session.otpExpiration = expirationTime; + req.session.save(); + + try { + await sendOTPByEmail(user.email, otp); + await userLogs.create({ + username: username, + activity: "OTP successfully sent to user", + }); + + } catch (sendOTPError) { + await userLogs.create({ + username: username, + activity: "OTP failed to send to user", + }); + + console.error("Error sending OTP:", sendOTPError); + return res.status(500).send("Internal Server Error"); } - let { username, password } = req.body; - username = username.trim(); + res.render("otp", { error: null, username: user.username, csrfToken: req.session.csrfToken }); + } else { + await userLogs.create({ username, success: false, activity: "Incorrect password" }); - const loginSql = "SELECT * FROM users WHERE username = ?"; + res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); + } + } else { + await userLogs.create({ + username: username, + activity: "User not found", + }); - connection.query(loginSql, [username], async (error, results) => { - if (error) { - console.error("Error executing login query:", error); - res.status(500).send("Internal Server Error"); - return; - } - - if (results.length > 0) { - const isLoginSuccessful = await bcrypt.compare(password, results[0].password); - - if (isLoginSuccessful) { - // Log successful login attempt - await logActivity(username, true, "Credentials entered correctly"); - - const user = results[0]; - const { otp, expirationTime } = generateOTP(); - - // Store the OTP and expiration time in the session for verification - req.session.otp = otp; - req.session.otpExpiration = expirationTime; - req.session.save(); - - // Send OTP via email - try { - await sendOTPByEmail(user.email, otp); - // Log successful OTP sending - await logActivity(username, true, "OTP successfully sent to user"); - } catch (sendOTPError) { - // Log unsuccessful OTP sending - await logActivity(username, false, "OTP failed to send to user"); - console.error("Error sending OTP:", sendOTPError); - res.status(500).send("Internal Server Error"); - return; - } - - // Render OTP input page - res.render("otp", { error: null, username: user.username, csrfToken: req.session.csrfToken }); - } else { - // Log unsuccessful login attempt - await logActivity(username, false, "Incorrect password"); - res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); - } - } else { - // Log unsuccessful login attempt - await logActivity(username, false, "User not found"); - res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); - } - }); - } catch (error) { - console.error("Error in login route:", error); - res.status(500).send("Internal Server Error"); + res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); } + } catch (error) { + console.error("Error in login route:", error); + res.status(500).send("Internal Server Error"); + } }); + // OTP verification route app.post("/verify-otp", [ - body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'), -], -async (req, res) => { - try { - const errors = validationResult(req); - - if (!errors.isEmpty()) { - // Handle validation errors, e.g., return an error message to the client - return res.render('otp', { error: 'Invalid OTP. Please try again.', username: req.body.username, csrfToken: req.session.csrfToken }); - } - - const enteredOTP = req.body.otp; - - if (!req.session) { - // If session is not defined, handle accordingly - console.error("Session is not defined."); - return res.status(500).send("Internal Server Error"); - } - - if (enteredOTP === req.session.otp) { - // Log successful OTP entry - if (req.body.username) { - await logActivity(req.body.username, true, "OTP entered correctly"); - } - - // Correct OTP, generate a session token - const sessionToken = crypto.randomBytes(32).toString('hex'); - - // Store the session token in the session - req.session.authenticated = true; - req.session.username = req.body.username; - req.session.sessionToken = sessionToken; - - // Generate and store anti-CSRF token in the session - csrfTokensession = crypto.randomBytes(32).toString('hex'); - - // Set anti-CSRF token in res.locals - - - // Log anti-CSRF token - console.log(`Generated Anti-CSRF Token: ${csrfTokensession}`); - // Set CSRF token as a cookie - - // Implement secure session handling: - // 1. Set secure, HttpOnly, and SameSite flags - // 2. Set an expiration time for the session token - // 3. Regenerate the session after authentication - res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day - - console.log(`Generated Session Token: ${sessionToken}`); - - // Redirect to home page with session token - res.redirect("/home"); - } else { - // Log unsuccessful OTP entry - if (req.body.username) { - await logActivity(req.body.username, false, "Incorrect OTP entered"); - } - - // Incorrect OTP, render login page with error - res.render("login", { error: "Incorrect OTP. Please try again.", csrfToken: req.session.csrfToken }); - } - } catch (error) { - console.error("Error in OTP verification route:", error); - res.status(500).send("Internal Server Error"); - } -}); - - - - app.get("/logout", (req, res) => { + body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'), + ], + async (req, res) => { try { - const username = req.session.username || "Unknown User"; + const errors = validationResult(req); + + if (!errors.isEmpty()) { + return res.render('otp', { error: 'Invalid OTP. Please try again.', username: req.body.username, csrfToken: req.session.csrfToken }); + } + + const enteredOTP = req.body.otp; + + if (!req.session) { + console.error("Session is not defined."); + return res.status(500).send("Internal Server Error"); + } + + const user = await User.findOne({ where: { username: req.body.username } }); + + if (!user) { + console.error("User not found."); + return res.status(500).send("Internal Server Error"); + } + + if (enteredOTP === req.session.otp) { + if (req.body.username) { + await userLogs.create({ username: req.body.username, activity: "OTP entered correctly" }); + } + + const sessionToken = crypto.randomBytes(32).toString('hex'); + + req.session.authenticated = true; + req.session.username = req.body.username; + req.session.sessionToken = sessionToken; + + + csrfTokenSession = crypto.randomBytes(32).toString('hex'); + + // Log anti-CSRF token + console.log(`Generated Anti-CSRF Token: ${csrfTokenSession}`); + + // Set CSRF token as a cookie + res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day + + console.log(`Generated Session Token: ${sessionToken}`); + + res.redirect("/home"); + } else { + if (req.body.username) { + await userLogs.create({ username: req.body.username, activity: "Incorrect OTP entered" }); + } + + res.render("login", { error: "Incorrect OTP. Please try again."}); + } + } catch (error) { + console.error("Error in OTP verification route:", error); + res.status(500).send("Internal Server Error"); + } + }); + + app.get("/logout", async (req, res) => { + try { + const username = req.session.username || "Unknown User"; + + // Log the logout activity using Sequelize + await userLogs.create({ username, activity: "User logged out. Session destroyed." }); + + // Log the user out by clearing the session req.session.destroy(async (err) => { if (err) { console.error("Error destroying session:", err); - await logLogoutActivity(username, false, "User logged out unsucessfully. Session not destroyed."); + + // Log the logout activity using Sequelize + await userLogs.create({ username, activity: "User logged out unsuccessfully. Session not destroyed." }); } else { - - console.log(`Session destroyed.`); + console.log("Session destroyed."); + + // Clear the session token cookie res.clearCookie('sessionToken'); - // Log the logout activity using a separate async function - await logLogoutActivity(username, true, "User logged out. Session destroyed."); } + // Redirect to the login page after logout res.redirect("/login"); }); @@ -305,6 +252,7 @@ async (req, res) => { } }); + app.get("/home", isAuthenticated, (req, res) => { // Render the home page with sensor data @@ -315,21 +263,23 @@ async (req, res) => { -app.get("/inusers", isAuthenticated, (req, res) => { - // Fetch all user data from the database - const allUsersQuery = "SELECT * FROM users"; - - connection.query(allUsersQuery, (error, allUsers) => { - if (error) { + app.get("/inusers", isAuthenticated, async (req, res) => { + try { + // Fetch all user data from the database using Sequelize + const allUsers = await User.findAll({ + attributes: ['name', 'username', 'email', 'jobTitle'], + }); + + const currentUsername = req.session.username; + + // Render the inusers page with JSON data + res.render("inusers", { allUsers, csrfToken: csrfTokenSession, currentUsername }); + } catch (error) { console.error("Error fetching all users:", error); res.status(500).send("Internal Server Error"); - return; } - const currentUsername = req.session.username; - // Render the inusers page with JSON data - res.render("inusers", { allUsers ,csrfToken: csrfTokensession, currentUsername:currentUsername }); }); -}); + function isStrongPassword(password) { // Password must be at least 10 characters long if (password.length < 10) { @@ -359,29 +309,7 @@ function isStrongPassword(password) { return true; } -const logUserCreationActivity = async (creatorUsername, success, message) => { - try { - const activity = success - ? "successful user creation" - : `unsuccessful user creation: ${message}`; - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - const logParams = [creatorUsername, activity]; - connection.query(logSql, logParams, (error, results) => { - if (error) { - console.error("Error logging user creation activity:", error); - // Handle error (you may want to log it or take other appropriate actions) - } else { - console.log("User creation activity logged successfully"); - } - - }); - } catch (error) { - console.error("Error in logUserCreationActivity function:", error); - // Handle error (you may want to log it or take other appropriate actions) - } -}; app.post( '/createUser', @@ -405,13 +333,14 @@ app.post( // Validate the anti-CSRF token const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokensession || submittedCSRFToken !== csrfTokensession) { + if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { return res.status(403).json({ error: 'CSRF token mismatch' }); } // Extract user input const { name, username, email, password, jobTitle } = req.body; console.log(submittedCSRFToken); + // Extract the username of the user creating a new user const creatorUsername = req.session.username; // Adjust this based on how you store the creator's username in your session @@ -421,216 +350,171 @@ app.post( } // Check if the username is already taken - const checkUsernameQuery = "SELECT * FROM users WHERE username = ?"; - connection.query(checkUsernameQuery, [username], (usernameQueryErr, usernameResults) => { - if (usernameQueryErr) { - console.error("Error checking username:", usernameQueryErr); - return res.status(500).json({ error: "Internal Server Error" }); - } + const existingUser = await User.findOne({ where: { username } }); - if (usernameResults.length > 0) { - // Log unsuccessful user creation due to username taken - logUserCreationActivity(creatorUsername, false, "username taken"); - return res.status(400).json({ - error: "Username is already taken", - message: "Username is already taken. Please choose a different username." - }); - } + if (existingUser) { + // Log unsuccessful user creation due to username taken + await userLogs.create({ username: creatorUsername, activity: "username taken" }); - // Check if the email is already taken - const checkEmailQuery = "SELECT * FROM users WHERE email = ?"; - connection.query(checkEmailQuery, [email], (emailQueryErr, emailResults) => { - if (emailQueryErr) { - console.error("Error checking email:", emailQueryErr); - return res.status(500).json({ error: "Internal Server Error" }); - } + return res.status(400).json({ + error: "Username is already taken", + message: "Username is already taken. Please choose a different username." + }); + } - if (emailResults.length > 0) { - // Log unsuccessful user creation due to email taken - logUserCreationActivity(creatorUsername, false, "email taken"); - return res.status(400).json({ - error: "Email is already in use", - message: "Email is already in use. Please choose another email." - }); - } - bcrypt.genSalt(10, (saltError, salt) => { - if (saltError) { - console.error("Error generating salt:", saltError); - return res.status(500).json({ error: "Internal Server Error" }); - } - - bcrypt.hash(req.body.password, salt, (hashError, hashedPassword) => { - if (hashError) { - console.error("Error hashing password:", hashError); - return res.status(500).json({ error: "Internal Server Error" }); - } - - connection.beginTransaction((transactionErr) => { - if (transactionErr) { - console.error("Error starting transaction:", transactionErr); - return res.status(500).json({ error: "Internal Server Error" }); - } + // Check if the email is already taken + const existingEmailUser = await User.findOne({ where: { email } }); - // Define the insert query - const insertUserQuery = - "INSERT INTO users (name, username, email, password, lastLogin, jobTitle) VALUES (?, ?, ?, ?, NULL, ?)"; + if (existingEmailUser) { + // Log unsuccessful user creation due to email taken + await userLogs.create({ username: creatorUsername, activity: "email taken" }); - // Log the query and its parameters - console.log("Insert Query:", insertUserQuery); - console.log("Query Parameters:", [name, username, email, hashedPassword, jobTitle]); + return res.status(400).json({ + error: "Email is already in use", + message: "Email is already in use. Please choose another email." + }); + } - // Execute the query with user data - connection.query(insertUserQuery, [name, username, email, hashedPassword, jobTitle], (queryErr, results) => { - if (queryErr) { - console.error("Error executing query:", queryErr); + // Hash the password + const hashedPassword = await bcrypt.hash(password, 10); - // Rollback the transaction in case of an error - connection.rollback((rollbackErr) => { - if (rollbackErr) { - console.error("Error rolling back transaction:", rollbackErr); - } - // Log unsuccessful user creation due to an error - logUserCreationActivity(creatorUsername, false, "internal error"); - return res.status(500).json({ error: "Internal Server Error" }); - }); - return; - } + // Start a transaction + const t = await sequelize.transaction(); - // Commit the transaction - connection.commit((commitErr) => { - if (commitErr) { - console.error("Error committing transaction:", commitErr); - return res.status(500).json({ error: "Internal Server Error" }); - } - - res.status(200).json({ message: "User created successfully" }); - logUserCreationActivity(creatorUsername, true, "user created successfully"); + try { + // Create the user + const newUser = await User.create({ + name, + username, + email, + password: hashedPassword, + lastLogin: null, + jobTitle, + }, { transaction: t }); - - }); - }); - }); - }); - }); - }); - }); - } catch (error) { - console.error("Error creating user:", error); - return res.status(500).json({ error: "Internal Server Error" }); - } - } - ); + // Commit the transaction + await t.commit(); + + // Log successful user creation + await userLogs.create({ username: creatorUsername, activity: "user created successfully" }); + + return res.status(200).json({ message: "User created successfully" }); + } catch (createUserError) { + // Rollback the transaction in case of an error + await t.rollback(); + + console.error("Error creating user:", createUserError); + + // Log unsuccessful user creation due to an error + await userLogs.create({ username: creatorUsername, activity: "internal error" }); + + return res.status(500).json({ error: "Internal Server Error" }); + } + } catch (error) { + console.error("Error creating user:", error); + return res.status(500).json({ error: "Internal Server Error" }); + } + } +); app.get("/forgot-password", (req, res) => { res.render("forgot-password", { error: null, success: null }); }); -// Handle the submission of the forgot password form -app.post("/forgot-password", (req, res) => { - const { usernameOrEmail } = req.body; +app.post("/forgot-password", async (req, res) => { + let user; // Declare the 'user' variable outside the try-catch block - // Sanitize the input - const sanitizedUsernameOrEmail = validator.escape(usernameOrEmail); + try { + const { usernameOrEmail } = req.body; - // Perform the logic for sending the reset password email + // Sanitize the input + const sanitizedUsernameOrEmail = validator.escape(usernameOrEmail); - // Check if the username or email exists in the database - const checkUserQuery = "SELECT * FROM users WHERE username = ? OR email = ?"; - connection.query( - checkUserQuery, - [sanitizedUsernameOrEmail, sanitizedUsernameOrEmail], - (checkError, checkResults) => { - if (checkError) { - console.error("Error checking user:", checkError); - const error = "An error occurred during the password reset process."; - res.render("forgot-password", { error, success: null }); - } else if (checkResults.length === 0) { - const error = "Username or email not found."; - res.render("forgot-password", { error, success: null }); - } else { - // Assuming the user exists, generate a reset token and send an email - const user = checkResults[0]; - const resetToken = crypto.randomBytes(20).toString("hex"); - const resetTokenExpiry = new Date(Date.now() + 3600000); // Token expires in 1 hour + // Find the user by username or email + user = await User.findOne({ + where: { + [Sequelize.Op.or]: [ + { username: sanitizedUsernameOrEmail }, + { email: sanitizedUsernameOrEmail }, + ], + }, + }); - // Update user with reset token and expiry - const updateQuery = - "UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?"; - connection.query( - updateQuery, - [resetToken, resetTokenExpiry, user.id], - (updateError) => { - if (updateError) { - console.error("Error updating reset token:", updateError); - const error = - "An error occurred during the password reset process."; - res.render("forgot-password", { error, success: null }); - } else { - // Send email with reset link - const resetLink = `http://localhost:3000/reset-password/${resetToken}`; - const mailOptions = { - to: user.email, - subject: "Password Reset", - text: `Click on the following link to reset your password: ${resetLink}`, - }; - transporter.sendMail(mailOptions, (emailError, info) => { - if (emailError) { - console.error("Error sending email:", emailError); - const error = - "An error occurred during the password reset process."; - res.render("forgot-password", { error, success: null }); - } else { - console.log("Email sent: " + info.response); - const success = - "Password reset email sent successfully. Check your inbox."; - res.render("forgot-password", { error: null, success }); - - // Log the successful sending of the reset link in the database - logPasswordResetActivity(user.username,"link sent successfully"); - } - }); - } - } - ); - } + if (!user) { + const error = "Username or email not found."; + return res.render("forgot-password", { error, success: null }); } - ); + + // Generate reset token and update the user + const reset_token = crypto.randomBytes(20).toString("hex"); + const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour + + // Update the user with the reset token and expiry + await User.update( + { + reset_token, + reset_token_expiry, + }, + { + where: { + id: user.id, // Replace 'id' with the actual primary key field of your User model + }, + } + ); + + // Send email with reset link + const resetLink = `http://localhost:3000/reset-password/${reset_token}`; + const mailOptions = { + to: user.email, + subject: "Password Reset", + text: `Click on the following link to reset your password: ${resetLink}`, + }; + + await transporter.sendMail(mailOptions); + + const success = "Password reset email sent successfully. Check your inbox."; + res.render("forgot-password", { error: null, success }); + + // Log the successful sending of the reset link in the database + await userLogs.create({ + username: user.username, + activity: "Password reset link sent successfully", + }); + } catch (error) { + if (user) { + await userLogs.create({ + username: user.username, + activity: "Password reset link unsuccessfully sent. Please check with the administrator.", + }); + } + console.error("Error during password reset:", error); + const errorMessage = "An error occurred during the password reset process."; + res.render("forgot-password", { error: errorMessage, success: null }); + } }); + + -// Function to log the password reset activity in the database -function logPasswordResetActivity(username, activity) { - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - connection.query(logSql, [username, activity], (error) => { - if (error) { - console.error("Error logging password reset activity:", error); - } else { - console.log("Password reset activity logged successfully"); - } - }); -} - -// Handle Reset Password request -app.post("/reset-password/:token", async (req, res) => { - const { token } = req.params; - const { password, confirmPassword } = req.body; + app.post("/reset-password/:token", async (req, res) => { + try { + const { token } = req.params; + const { password, confirmPassword } = req.body; - // Sanitize the inputs - const sanitizedToken = validator.escape(token); - const sanitizedPassword = validator.escape(password); - const sanitizedConfirmPassword = validator.escape(confirmPassword); + // Sanitize the inputs + const sanitizedToken = validator.escape(token); + const sanitizedPassword = validator.escape(password); + const sanitizedConfirmPassword = validator.escape(confirmPassword); - // Find user with matching reset token and not expired - const selectQuery = - "SELECT * FROM users WHERE reset_token = ? AND reset_token_expiry > NOW()"; - connection.query(selectQuery, [sanitizedToken], async (selectErr, selectResults) => { - if (selectErr) { - console.error("Error querying reset token:", selectErr); - return res.status(500).json({ error: "Error querying reset token" }); - } + // Find user with matching reset token and not expired + const user = await User.findOne({ + where: { + reset_token: sanitizedToken, + reset_token_expiry: { [Sequelize.Op.gt]: new Date() }, + }, + }); - if (selectResults.length === 0) { + if (!user) { // Pass the error to the template when rendering the reset-password page return res.render("reset-password", { token, @@ -658,51 +542,37 @@ app.post("/reset-password/:token", async (req, res) => { } // Hash the new password - const hashedPassword = await new Promise((resolve, reject) => { - bcrypt.genSalt(10, (saltError, salt) => { - if (saltError) { - console.error("Error generating salt:", saltError); - reject("Internal Server Error"); - } - - // Use the generated salt to hash the password - bcrypt.hash(sanitizedPassword, salt, (hashError, hashed) => { - if (hashError) { - console.error("Error hashing password:", hashError); - reject("Internal Server Error"); - } - - resolve(hashed); - }); - }); - }); + const hashedPassword = await bcrypt.hash(sanitizedPassword, 10); // Update user's password and clear reset token - const updateQuery = - "UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE reset_token = ?"; -connection.query(updateQuery, [hashedPassword, sanitizedToken], async (updateErr, updateResults) => { - if (updateErr) { - console.error("Error updating password:", updateErr); - // Pass the error to the template when rendering the reset-password page - res.render("reset-password", { - token, - resetError: "Error updating password", - }); - } else { - // Log password reset activity - const username = selectResults[0].username; // Assuming 'username' is the column name - const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, 'Password Reseted successfully', NOW())"; - connection.query(logQuery, [username], (logErr) => { - if (logErr) { - console.error("Error logging password reset:", logErr); - // You might want to handle the logging error here - } - }); - // Redirect to the success page upon successful password reset - res.redirect("/success"); - } + const updateQuery = { + password: hashedPassword, + reset_token: null, + reset_token_expiry: null, + }; + const whereCondition = { + reset_token: sanitizedToken, + }; + + await User.update(updateQuery, { + where: whereCondition, }); - }); + // Log password reset activity + await userLogs.create({ + username: user.username, + activity: "Password reset successfully", + }); + + // Redirect to the success page upon successful password reset + res.redirect("/success"); + } catch (error) { + console.error("Error during password reset:", error); + // Pass the error to the template when rendering the reset-password page + res.render("reset-password", { + token: req.params.token, + resetError: "Error during password reset", + }); + } }); app.get("/success", (req, res) => { @@ -720,12 +590,13 @@ app.get("/reset-password/:token", (req, res) => { success: null, }); }); + app.post("/reset-password", async (req, res) => { const { username, password, confirmPassword, csrf_token } = req.body; const creatorUsername = req.session.username; const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokensession|| submittedCSRFToken !== csrfTokensession) { + if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { return res.status(403).json({ error: 'CSRF token mismatch' }); } @@ -747,183 +618,154 @@ app.post("/reset-password", async (req, res) => { }); } - // Generate a random salt - const saltRounds = 10; // You can adjust the number of rounds based on your security requirements - const salt = await bcrypt.genSalt(saltRounds); + try { + // Find the user in the database + const user = await User.findOne({ where: { username: sanitizedUsername } }); - // Hash the new password with the generated salt - const hashedPassword = await bcrypt.hash(sanitizedPassword, salt); + if (!user) { + return res.status(404).json({ error: "User does not exist" }); + } - // Check if the user exists in the database before updating the password - const userExists = await checkIfUserExists(sanitizedUsername); + // Generate a random salt and hash the new password + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds); - if (!userExists) { - return res.status(404).json({ error: "User does not exist" }); + // Update user's password + await User.update( + { password: hashedPassword }, + { where: { username: sanitizedUsername } } + ); + + // Log password reset activity + await userLogs.create({ + username: creatorUsername, + activity: `Password has been reset for ${sanitizedUsername}`, + }); + + // Password update successful + return res.status(200).json({ success: "Password updated successfully" }); + } catch (error) { + console.error("Error updating password:", error); + return res.status(500).json({ error: "Error updating password" }); } - - // Update user's password based on the username - const updateQuery = "UPDATE users SET password = ? WHERE username = ?"; - connection.query(updateQuery, [hashedPassword, sanitizedUsername], async (updateErr, updateResults) => { - if (updateErr) { - console.error("Error updating password:", updateErr); - return res.status(500).json({ error: "Error updating password" }); - } - - // Check if the update affected any rows - if (updateResults.affectedRows > 0) { - // Log password reset activity - const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, NOW())"; - const logActivity = `Password has been reset for ${sanitizedUsername}`; - connection.query(logQuery, [creatorUsername, logActivity], (logErr) => { - if (logErr) { - console.error("Error logging password reset:", logErr); - // You might want to handle the logging error here - } - - // Password update successful - return res.status(200).json({ success: "Password updated successfully" }); - }); - } else { - return res.status(404).json({ - error: "User not found or password not updated.", - }); - } - }); }); -async function checkIfUserExists(username) { - return new Promise((resolve, reject) => { - const query = "SELECT * FROM users WHERE username = ?"; - connection.query(query, [username], (err, results) => { - if (err) { - reject(err); - } else { - resolve(results.length > 0); - } - }); - }); -} -app.get('/searchUser', (req, res) => { - const { username } = req.query; - - // Sanitize the input - const sanitizedUsername = validator.escape(username); - - const query = 'SELECT * FROM users WHERE username = ?'; - - connection.query(query, [sanitizedUsername], (err, results) => { - if (err) { - console.error('MySQL query error:', err); - res.status(500).json({ success: false, error: 'Internal Server Error' }); - } else if (results.length === 0) { - // No user found with the given username - res.status(404).json({ success: false, error: 'User not found' }); - } else { - res.json(results); - } - }); - }); +app.get('/searchUser', async (req, res) => { + const { username } = req.query; -app.get('/api/users', (req, res) => { - const query = 'SELECT * FROM users'; + // Sanitize the input + const sanitizedUsername = validator.escape(username); + + try { + // Find the user in the database + const user = await User.findOne({ where: { username: sanitizedUsername } }); + console.log(user); + if (!user) { + // No user found with the given username + res.status(404).json({ success: false, error: 'User not found' }); + } else { + // User found, return user data + res.json(user); + + } + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error' }); + } +}); + +app.get('/api/users', async (req, res) => { + try { + // Find all users in the database + const users = await User.findAll(); + + // Return the users in the response + res.json(users); + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error' }); + } +}); - connection.query(query, (err, results) => { - if (err) { - console.error('MySQL query error:', err); - res.status(500).json({ success: false, error: 'Internal Server Error' }); - } else { - res.json(results); - } - }); - }); +app.get('/api/searchUser', async (req, res) => { + const { username } = req.query; + console.log(username); + try { + // Find the user in the database by username + const user = await User.findOne({ where: { username } }); + + if (!user) { + // No user found with the given username + res.status(404).json({ success: false, error: 'User not found' }); + } else { + // User found, return user data + res.json(user); + + } + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error' }); + } +}); - // Route to search for a user by username - app.get('/api/searchUser', (req, res) => { - const { username } = req.query; - const query = 'SELECT * FROM users WHERE username = ?'; +app.delete('/api/deleteUser/:username', async (req, res) => { + const { username } = req.params; + const creatorUsername = req.session.username; + + try { + // Extract CSRF token from the request body + const { csrfToken } = req.body; + + // Compare CSRF token with the one stored in the session + if (csrfToken !== csrfTokenSession) { + return res.status(403).json({ success: false, error: 'CSRF token mismatch' }); + } + + // Log deletion activity to UserLogs model + const deletionActivity = `User ${username} has been successfully deleted`; + await userLogs.create({ username: creatorUsername, activity: deletionActivity }); + + // Perform user deletion using the User model + const deletedUser = await User.destroy({ where: { username } }); + + if (!deletedUser) { + res.status(404).json({ success: false, error: 'User not found' }); + } else { + res.json({ success: true, message: 'User deleted successfully' }); + } + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error', details: error.message }); + } +}); - connection.query(query, [username], (err, results) => { - if (err) { - console.error('MySQL query error:', err); - res.status(500).json({ success: false, error: 'Internal Server Error' }); - } else { - res.json(results); - } - }); - }); - - app.delete('/api/deleteUser/:username', async (req, res) => { - const { username } = req.params; - const query = 'DELETE FROM users WHERE username = ?'; - const creatorUsername = req.session.username; - - try { - // Extract CSRF token from the request body - const { csrfToken } = req.body; - console.log(csrfToken); - console.log(csrfTokensession); - // Compare CSRF token with the one stored in the session - if (csrfToken !== csrfTokensession) { - return res.status(403).json({ success: false, error: 'CSRF token mismatch' }); - } - - // Log deletion activity to USER_LOGS - const deletionActivity = `User ${username} has been successfully deleted`; - const logQuery = 'INSERT INTO USER_LOGS (USERNAME, ACTIVITY, TIMESTAMP) VALUES (?, ?, CURRENT_TIMESTAMP)'; - await executeQuery(logQuery, [creatorUsername, deletionActivity]); - - // Perform user deletion - const results = await executeQuery(query, [username]); - - if (results.affectedRows === 0) { - res.status(404).json({ success: false, error: 'User not found' }); - } else { - res.json({ success: true, message: 'User deleted successfully' }); - } - } catch (error) { - console.error('MySQL query error:', error); - res.status(500).json({ success: false, error: 'Internal Server Error', details: error.message }); - } - }); - - - async function executeQuery(sql, values) { - return new Promise((resolve, reject) => { - connection.query(sql, values, (err, results) => { - if (err) { - reject(err); - } else { - resolve(results); - } - }); - }); - } - - app.get('/api/getLogs', (req, res) => { - // Query the database to fetch logs - const query = 'SELECT id, username, activity, timestamp FROM user_logs'; - connection.query(query, (err, results) => { - if (err) { - console.error('Error fetching logs from MySQL:', err); - res.status(500).json({ error: 'Error fetching logs from MySQL' }); - return; - } - - // Format timestamps to a more readable format - const formattedLogs = results.map((log) => ({ - id: log.id, - username: log.username, - activity: log.activity, - timestamp: new Date(log.timestamp).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }), - })); - - // Send the formatted logs as a JSON response - res.json(formattedLogs); - }); - }); + +app.get('/api/getLogs', async (req, res) => { + try { + // Query the database to fetch logs using Sequelize model + const logs = await userLogs.findAll({ + attributes: ['id', 'username', 'activity', 'timestamp'], + }); + + // Format timestamps to a more readable format with timezone conversion + const formattedLogs = logs.map((log) => ({ + id: log.id, + username: log.username, + activity: log.activity, + timestamp: format(new Date(log.timestamp), 'yyyy-MM-dd HH:mm:ssXXX', { timeZone: 'Asia/Singapore' }), + })); + + // Send the formatted logs as a JSON response + res.json(formattedLogs); + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ error: 'Error fetching logs from Sequelize' }); + } +}); + app.use(express.static("views")); app.listen(PORT, () => { diff --git a/Sean/views/inusers.ejs b/Sean/views/inusers.ejs index eeec05f..4962a64 100644 --- a/Sean/views/inusers.ejs +++ b/Sean/views/inusers.ejs @@ -41,7 +41,6 @@