update with pass hashing and login
This commit is contained in:
parent
6fcf603603
commit
1cd8d62469
2
Sean/.gitignore
vendored
Normal file
2
Sean/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
node_modules
|
103
Sean/server.js
103
Sean/server.js
@ -6,6 +6,8 @@ const bcrypt = require('bcrypt');
|
|||||||
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
app.use(bodyParser.json());
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
require('dotenv').config();
|
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.use(session({ secret: 'your_session_secret', resave: false, saveUninitialized: true }));
|
||||||
app.set('view engine', 'ejs');
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
app.get('/login', (req, res) => {
|
|
||||||
res.render('login');
|
|
||||||
});
|
|
||||||
|
|
||||||
function isAuthenticated(req, res, next) {
|
function isAuthenticated(req, res, next) {
|
||||||
if (req.session && req.session.authenticated) {
|
if (req.session && req.session.authenticated) {
|
||||||
return next();
|
return next();
|
||||||
@ -42,11 +40,17 @@ function isAuthenticated(req, res, next) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.post('/login', (req, res) => {
|
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;
|
let { username, password } = req.body;
|
||||||
username = username.trim();
|
username = username.trim();
|
||||||
|
|
||||||
const loginSql = 'SELECT * FROM users WHERE username = ? AND password = ?';
|
const loginSql = 'SELECT * FROM users WHERE username = ?';
|
||||||
const updateLastLoginSql = 'UPDATE users SET lastLogin = CURRENT_TIMESTAMP WHERE username = ?';
|
const updateLastLoginSql = 'UPDATE users SET lastLogin = CURRENT_TIMESTAMP WHERE username = ?';
|
||||||
|
|
||||||
// Check credentials and retrieve user information
|
// Check credentials and retrieve user information
|
||||||
@ -54,7 +58,12 @@ app.post('/login', (req, res) => {
|
|||||||
|
|
||||||
connection.connect();
|
connection.connect();
|
||||||
|
|
||||||
connection.query(loginSql, [username, password], (error, results) => {
|
console.log('Login Query:', loginSql);
|
||||||
|
console.log('Query Parameters:', [username]);
|
||||||
|
|
||||||
|
connection.query(loginSql, [username], async (error, results) => {
|
||||||
|
console.log('Login Results:', results);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error executing login query:', error);
|
console.error('Error executing login query:', error);
|
||||||
res.status(500).send('Internal Server Error');
|
res.status(500).send('Internal Server Error');
|
||||||
@ -63,9 +72,14 @@ app.post('/login', (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (results.length === 0) {
|
if (results.length === 0) {
|
||||||
res.status(401).send('Invalid username or password');
|
// Pass the error to the template
|
||||||
|
res.render('login', { error: 'Invalid username or password' });
|
||||||
connection.end(); // Close the connection when not needed anymore
|
connection.end(); // Close the connection when not needed anymore
|
||||||
} else {
|
} else {
|
||||||
|
const user = results[0];
|
||||||
|
const passwordMatch = await bcrypt.compare(password, user.password);
|
||||||
|
|
||||||
|
if (passwordMatch) {
|
||||||
// Update lastLogin field for the user
|
// Update lastLogin field for the user
|
||||||
connection.query(updateLastLoginSql, [username], (updateError, updateResults) => {
|
connection.query(updateLastLoginSql, [username], (updateError, updateResults) => {
|
||||||
if (updateError) {
|
if (updateError) {
|
||||||
@ -78,21 +92,37 @@ app.post('/login', (req, res) => {
|
|||||||
// Check if the update affected any rows
|
// Check if the update affected any rows
|
||||||
if (updateResults.affectedRows > 0) {
|
if (updateResults.affectedRows > 0) {
|
||||||
// Set session data for authentication
|
// 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.authenticated = true;
|
||||||
req.session.username = username;
|
req.session.username = username;
|
||||||
|
|
||||||
// Redirect to the home page or any other protected route
|
|
||||||
res.redirect('/home');
|
res.redirect('/home');
|
||||||
|
connection.end();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(500).send('Error updating lastLogin. No rows affected.');
|
// 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
|
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
|
// Update your /home route to retrieve the overall last 10 logins for all users
|
||||||
app.get('/home', isAuthenticated, (req, res) => {
|
app.get('/home', isAuthenticated, (req, res) => {
|
||||||
// Retrieve the overall last 10 logins for all users
|
// Retrieve the overall last 10 logins for all users
|
||||||
@ -127,13 +157,51 @@ app.get('/inusers', isAuthenticated, (req, res) => {
|
|||||||
res.render('inusers', { allUsers });
|
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 {
|
try {
|
||||||
const { name, username, email, password, jobTitle } = req.body;
|
const { name, username, email, password, jobTitle } = req.body;
|
||||||
|
|
||||||
// Hash the password using bcrypt
|
// Validate password complexity
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
if (!isStrongPassword(password)) {
|
||||||
|
return res.status(400).json({ error: 'Password does not meet complexity requirements' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Start a transaction
|
// Start a transaction
|
||||||
mysqlConnection.beginTransaction((transactionErr) => {
|
mysqlConnection.beginTransaction((transactionErr) => {
|
||||||
@ -181,6 +249,7 @@ app.post('/createUser', async (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating user:', error);
|
console.error('Error creating user:', error);
|
||||||
res.status(500).json({ error: 'Internal Server Error' });
|
res.status(500).json({ error: 'Internal Server Error' });
|
||||||
|
@ -75,23 +75,27 @@
|
|||||||
<div class="user-details">
|
<div class="user-details">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<span class="details">Full Name</span>
|
<span class="details">Full Name</span>
|
||||||
<input type="text" name="name" placeholder="Enter your name" required>
|
<input type="text" name="name" id="name" placeholder="Enter your name" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<span class="details">Username</span>
|
<span class="details">Username</span>
|
||||||
<input type="text" name="username" placeholder="Enter your username" required>
|
<input type="text" name="username" id="username" placeholder="Enter your username" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<span class="details">Email</span>
|
<span class="details">Email</span>
|
||||||
<input type="text" name="email" placeholder="Enter your email" required>
|
<input type="text" name="email" id="email" placeholder="Enter your email" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<span class="details">Password</span>
|
<span class="details">Password</span>
|
||||||
<input type="password" name="password" placeholder="Enter your password" required>
|
<input type="password" name="password" id="password" placeholder="Enter your password" required>
|
||||||
|
</div>
|
||||||
|
<div class="input-box">
|
||||||
|
<span class="details">Confirm Password</span>
|
||||||
|
<input type="password" name="confirmPassword" id="confirmPassword" placeholder="Confirm your password" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<span class="details">Job Title</span>
|
<span class="details">Job Title</span>
|
||||||
<select name="jobTitle">
|
<select name="jobTitle" id="jobTitle">
|
||||||
<option value="admin">Admin</option>
|
<option value="admin">Admin</option>
|
||||||
<option value="dataAnalyst">Data Analyst</option>
|
<option value="dataAnalyst">Data Analyst</option>
|
||||||
</select>
|
</select>
|
||||||
@ -106,6 +110,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Your existing script tags -->
|
<!-- Your existing script tags -->
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js"></script>
|
||||||
@ -134,20 +139,38 @@ document.getElementById('userDataLink').addEventListener('click', function () {
|
|||||||
|
|
||||||
document.getElementById('userForm').addEventListener('submit', function (event) {
|
document.getElementById('userForm').addEventListener('submit', function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const formData = new FormData(this);
|
|
||||||
const newUser = {};
|
|
||||||
formData.forEach((value, key) => {
|
|
||||||
newUser[key] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('Form Data Before Sending:', newUser);
|
// 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', {
|
fetch('/createUser', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(newUser),
|
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 => {
|
.then(response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -157,11 +180,48 @@ document.getElementById('userForm').addEventListener('submit', function (event)
|
|||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log('Success:', 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 => {
|
.catch(error => {
|
||||||
console.error('Fetch Error:', error);
|
console.error('Fetch Error:', error);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function downloadExcel(allUsers) {
|
function downloadExcel(allUsers) {
|
||||||
if (allUsers && allUsers.length > 0) {
|
if (allUsers && allUsers.length > 0) {
|
||||||
|
@ -55,19 +55,33 @@
|
|||||||
button:hover {
|
button:hover {
|
||||||
background-color: #45a049;
|
background-color: #45a049;
|
||||||
}
|
}
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="loginForm" class="user-creation-container">
|
||||||
|
<div class="title">Login</div>
|
||||||
|
<div class="content">
|
||||||
<form action="/login" method="post">
|
<form action="/login" method="post">
|
||||||
<h1>Admin Login</h1>
|
<% if (error) { %>
|
||||||
|
<div class="error-message"><%= error %></div>
|
||||||
<label for="username">Username:</label>
|
<% } %>
|
||||||
<input type="text" id="username" name="username" required>
|
<div class="input-box">
|
||||||
|
<span class="details">Username</span>
|
||||||
<label for="password">Password:</label>
|
<input type="text" name="username" placeholder="Enter your username" required>
|
||||||
<input type="password" id="password" name="password" required>
|
</div>
|
||||||
|
<div class="input-box">
|
||||||
<button type="submit">Login</button>
|
<span class="details">Password</span>
|
||||||
|
<input type="password" name="password" placeholder="Enter your password" required>
|
||||||
|
</div>
|
||||||
|
<div class="button">
|
||||||
|
<input type="submit" value="Login">
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user