diff --git a/Sean/.gitignore b/Sean/.gitignore new file mode 100644 index 0000000..97aca2e --- /dev/null +++ b/Sean/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules \ No newline at end of file diff --git a/Sean/server.js b/Sean/server.js index ee110ca..0a36335 100644 --- a/Sean/server.js +++ b/Sean/server.js @@ -6,6 +6,8 @@ const bcrypt = require('bcrypt'); const app = express(); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); const PORT = process.env.PORT || 3000; require('dotenv').config(); @@ -30,10 +32,6 @@ app.use(bodyParser.urlencoded({ extended: true })); app.use(session({ secret: 'your_session_secret', resave: false, saveUninitialized: true })); app.set('view engine', 'ejs'); -app.get('/login', (req, res) => { - res.render('login'); -}); - function isAuthenticated(req, res, next) { if (req.session && req.session.authenticated) { return next(); @@ -42,57 +40,89 @@ function isAuthenticated(req, res, next) { } } -app.post('/login', (req, res) => { - let { username, password } = req.body; - username = username.trim(); - - const loginSql = 'SELECT * FROM users WHERE username = ? AND password = ?'; - const updateLastLoginSql = 'UPDATE users SET lastLogin = CURRENT_TIMESTAMP WHERE username = ?'; - - // Check credentials and retrieve user information - const connection = mysql.createConnection(mysqlConfig); - - connection.connect(); - - connection.query(loginSql, [username, password], (error, results) => { - if (error) { - console.error('Error executing login query:', error); - res.status(500).send('Internal Server Error'); - connection.end(); // Close the connection in case of an error - return; - } - - if (results.length === 0) { - res.status(401).send('Invalid username or password'); - connection.end(); // Close the connection when not needed anymore - } else { - // Update lastLogin field for the user - connection.query(updateLastLoginSql, [username], (updateError, updateResults) => { - if (updateError) { - console.error('Error updating lastLogin:', updateError); - res.status(500).send('Internal Server Error'); - connection.end(); // Close the connection in case of an error - return; - } - - // Check if the update affected any rows - if (updateResults.affectedRows > 0) { - // Set session data for authentication - req.session.authenticated = true; - req.session.username = username; - - // Redirect to the home page or any other protected route - res.redirect('/home'); - } else { - res.status(500).send('Error updating lastLogin. No rows affected.'); - } - - connection.end(); // Close the connection when not needed anymore - }); - } - }); +app.get('/login', (req, res) => { + // Pass an initial value for the error variable + res.render('login', { error: null }); }); +app.post('/login', async (req, res) => { + try { + let { username, password } = req.body; + username = username.trim(); + + const loginSql = 'SELECT * FROM users WHERE username = ?'; + const updateLastLoginSql = 'UPDATE users SET lastLogin = CURRENT_TIMESTAMP WHERE username = ?'; + + // Check credentials and retrieve user information + const connection = mysql.createConnection(mysqlConfig); + + connection.connect(); + + console.log('Login Query:', loginSql); + console.log('Query Parameters:', [username]); + + connection.query(loginSql, [username], async (error, results) => { + console.log('Login Results:', results); + + if (error) { + console.error('Error executing login query:', error); + res.status(500).send('Internal Server Error'); + connection.end(); // Close the connection in case of an error + return; + } + + if (results.length === 0) { + // Pass the error to the template + res.render('login', { error: 'Invalid username or password' }); + connection.end(); // Close the connection when not needed anymore + } else { + const user = results[0]; + const passwordMatch = await bcrypt.compare(password, user.password); + + if (passwordMatch) { + // Update lastLogin field for the user + connection.query(updateLastLoginSql, [username], (updateError, updateResults) => { + if (updateError) { + console.error('Error updating lastLogin:', updateError); + res.status(500).send('Internal Server Error'); + connection.end(); // Close the connection in case of an error + return; + } + + // Check if the update affected any rows + if (updateResults.affectedRows > 0) { + // Set session data for authentication + req.session.regenerate(err => { + if (err) { + console.error('Error regenerating session:', err); + } + console.log('Session regenerated successfully'); + req.session.authenticated = true; + req.session.username = username; + res.redirect('/home'); + connection.end(); + }); + } else { + // Pass the error to the template + res.render('login', { error: 'Error updating lastLogin. No rows affected.' }); + connection.end(); // Close the connection when not needed anymore + } + }); + } else { + // Pass the error to the template + res.render('login', { error: 'Invalid username or password' }); + connection.end(); // Close the connection when not needed anymore + } + } + }); + } catch (error) { + console.error('Error in login route:', error); + res.status(500).send('Internal Server Error'); + } +}); + + + // Update your /home route to retrieve the overall last 10 logins for all users app.get('/home', isAuthenticated, (req, res) => { // Retrieve the overall last 10 logins for all users @@ -127,57 +157,96 @@ app.get('/inusers', isAuthenticated, (req, res) => { res.render('inusers', { allUsers }); }); }); +function isStrongPassword(password) { + // Password must be at least 10 characters long + if (password.length < 10) { + return false; + } -app.post('/createUser', async (req, res) => { + // Password must contain at least one uppercase letter + if (!/[A-Z]/.test(password)) { + return false; + } + + // Password must contain at least one lowercase letter + if (!/[a-z]/.test(password)) { + return false; + } + + // Password must contain at least one digit + if (!/\d/.test(password)) { + return false; + } + + // Password must contain at least one symbol + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { + return false; + } + + return true; +} + +app.post('/createUser', (req, res) => { try { const { name, username, email, password, jobTitle } = req.body; - // Hash the password using bcrypt - const hashedPassword = await bcrypt.hash(password, 10); + // Validate password complexity + if (!isStrongPassword(password)) { + return res.status(400).json({ error: 'Password does not meet complexity requirements' }); + } - // Start a transaction - mysqlConnection.beginTransaction((transactionErr) => { - if (transactionErr) { - console.error('Error starting transaction:', transactionErr); + // Hash the password before storing it in the database + bcrypt.hash(password, 10, (hashError, hashedPassword) => { + if (hashError) { + console.error('Error hashing password:', hashError); res.status(500).json({ error: 'Internal Server Error' }); return; } - // Define the insert query - const insertUserQuery = 'INSERT INTO users (name, username, email, password, lastLogin, jobTitle) VALUES (?, ?, ?, ?, NULL, ?)'; - - // Log the query and its parameters - console.log('Insert Query:', insertUserQuery); - console.log('Query Parameters:', [name, username, email, hashedPassword, jobTitle]); - - // Execute the query with user data - mysqlConnection.query(insertUserQuery, [name, username, email, hashedPassword, jobTitle], (queryErr, results) => { - if (queryErr) { - console.error('Error executing query:', queryErr); - - // Rollback the transaction in case of an error - mysqlConnection.rollback((rollbackErr) => { - if (rollbackErr) { - console.error('Error rolling back transaction:', rollbackErr); - } - res.status(500).json({ error: 'Internal Server Error' }); - }); + // Start a transaction + mysqlConnection.beginTransaction((transactionErr) => { + if (transactionErr) { + console.error('Error starting transaction:', transactionErr); + res.status(500).json({ error: 'Internal Server Error' }); return; } - // Commit the transaction - mysqlConnection.commit((commitErr) => { - if (commitErr) { - console.error('Error committing transaction:', commitErr); - res.status(500).json({ error: 'Internal Server Error' }); + // Define the insert query + const insertUserQuery = 'INSERT INTO users (name, username, email, password, lastLogin, jobTitle) VALUES (?, ?, ?, ?, NULL, ?)'; + + // Log the query and its parameters + console.log('Insert Query:', insertUserQuery); + console.log('Query Parameters:', [name, username, email, hashedPassword, jobTitle]); + + // Execute the query with user data + mysqlConnection.query(insertUserQuery, [name, username, email, hashedPassword, jobTitle], (queryErr, results) => { + if (queryErr) { + console.error('Error executing query:', queryErr); + + // Rollback the transaction in case of an error + mysqlConnection.rollback((rollbackErr) => { + if (rollbackErr) { + console.error('Error rolling back transaction:', rollbackErr); + } + res.status(500).json({ error: 'Internal Server Error' }); + }); return; } - // Log the results of the query - console.log('Query Results:', results); + // Commit the transaction + mysqlConnection.commit((commitErr) => { + if (commitErr) { + console.error('Error committing transaction:', commitErr); + res.status(500).json({ error: 'Internal Server Error' }); + return; + } - // Respond with a success message - res.status(201).json({ message: 'User created successfully' }); + // Log the results of the query + console.log('Query Results:', results); + + // Respond with a success message + res.status(201).json({ message: 'User created successfully' }); + }); }); }); }); diff --git a/Sean/views/inusers.ejs b/Sean/views/inusers.ejs index 2c016e4..cc0dd79 100644 --- a/Sean/views/inusers.ejs +++ b/Sean/views/inusers.ejs @@ -75,23 +75,27 @@
Full Name - +
Username - +
Email - +
Password - + +
+
+ Confirm Password +
Job Title - @@ -106,6 +110,7 @@
+ @@ -133,35 +138,90 @@ document.getElementById('userDataLink').addEventListener('click', function () { }); document.getElementById('userForm').addEventListener('submit', function (event) { - event.preventDefault(); - const formData = new FormData(this); - const newUser = {}; - formData.forEach((value, key) => { - newUser[key] = value; + event.preventDefault(); + + // Use FormData directly + const formData = new FormData(document.getElementById('userForm')); + + // Check password complexity + const password = formData.get('password'); + const confirmPassword = formData.get('confirmPassword'); + + if (!isStrongPassword(password)) { + alert('Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.'); + return; + } + + // Check if passwords match + if (password !== confirmPassword) { + alert('Passwords do not match. Please enter the same password in both fields.'); + return; + } + + // Make a fetch request + fetch('/createUser', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: formData.get('name'), + username: formData.get('username'), + email: formData.get('email'), + password: password, // Use the validated password + jobTitle: formData.get('jobTitle'), + }), + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + console.log('Success:', data); + + // Show an alert with the received data + alert(`User Registered!`); + + // Optionally, you can clear the form or take other actions after registration + document.getElementById('userForm').reset(); + }) + .catch(error => { + console.error('Fetch Error:', error); + }); }); - console.log('Form Data Before Sending:', newUser); + // Function to validate password complexity + function isStrongPassword(password) { + // Password must be at least 10 characters long + if (password.length < 10) { + return false; + } + + // Password must contain at least one uppercase letter + if (!/[A-Z]/.test(password)) { + return false; + } + + // Password must contain at least one lowercase letter + if (!/[a-z]/.test(password)) { + return false; + } + + // Password must contain at least one digit + if (!/\d/.test(password)) { + return false; + } + + // Password must contain at least one symbol + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { + return false; + } + + return true; + } - fetch('/createUser', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(newUser), - }) - .then(response => { - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - return response.json(); - }) - .then(data => { - console.log('Success:', data); - }) - .catch(error => { - console.error('Fetch Error:', error); - }); -}) function downloadExcel(allUsers) { if (allUsers && allUsers.length > 0) { diff --git a/Sean/views/login.ejs b/Sean/views/login.ejs index 457fa25..5e3df14 100644 --- a/Sean/views/login.ejs +++ b/Sean/views/login.ejs @@ -55,19 +55,33 @@ button:hover { background-color: #45a049; } + .error-message { + color: red; + margin-top: 10px; + } -
-

Admin Login

- - - - - - - - -
+
+
Login
+
+
+ <% if (error) { %> +
<%= error %>
+ <% } %> +
+ Username + +
+
+ Password + +
+
+ +
+
+
+