Added in a password field for login. need to add encryption sometime
This commit is contained in:
parent
ab2e91ec12
commit
d11837e449
@ -163,6 +163,18 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
required
|
||||||
|
autocomplete="current-password"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="login-button" id="login-button">
|
<button type="submit" class="login-button" id="login-button">
|
||||||
Sign In
|
Sign In
|
||||||
</button>
|
</button>
|
||||||
@ -180,6 +192,7 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const email = document.getElementById('email').value;
|
const email = document.getElementById('email').value;
|
||||||
|
const password = document.getElementById('password').value;
|
||||||
const button = document.getElementById('login-button');
|
const button = document.getElementById('login-button');
|
||||||
const errorMessage = document.getElementById('error-message');
|
const errorMessage = document.getElementById('error-message');
|
||||||
const successMessage = document.getElementById('success-message');
|
const successMessage = document.getElementById('success-message');
|
||||||
@ -198,7 +211,7 @@
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ email }),
|
body: JSON.stringify({ email, password }),
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -311,12 +311,12 @@ app.post('/api/auth/login', authLimiter, async (req, res) => {
|
|||||||
userAgent: req.headers['user-agent']
|
userAgent: req.headers['user-agent']
|
||||||
});
|
});
|
||||||
|
|
||||||
const { email } = req.body;
|
const { email, password } = req.body;
|
||||||
|
|
||||||
if (!email) {
|
if (!email || !password) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Email is required'
|
error: 'Email and password are required'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +338,7 @@ app.post('/api/auth/login', authLimiter, async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch authorized emails from NocoDB
|
// Fetch user from NocoDB
|
||||||
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
|
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
|
||||||
|
|
||||||
logger.info(`Checking authentication for email: ${email}`);
|
logger.info(`Checking authentication for email: ${email}`);
|
||||||
@ -350,54 +350,79 @@ app.post('/api/auth/login', authLimiter, async (req, res) => {
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
limit: 1000 // Adjust if you have more authorized users
|
where: `(Email,eq,${email})`,
|
||||||
|
limit: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const users = response.data.list || [];
|
const users = response.data.list || [];
|
||||||
|
|
||||||
// Check if email exists in the authorized users list
|
if (users.length === 0) {
|
||||||
const authorizedUser = users.find(user =>
|
logger.warn(`No user found with email: ${email}`);
|
||||||
user.Email && user.Email.toLowerCase() === email.toLowerCase()
|
return res.status(401).json({
|
||||||
);
|
|
||||||
|
|
||||||
if (authorizedUser) {
|
|
||||||
// Set session including admin status
|
|
||||||
req.session.authenticated = true;
|
|
||||||
req.session.userEmail = email;
|
|
||||||
req.session.userName = authorizedUser.Name || email;
|
|
||||||
req.session.isAdmin = authorizedUser.Admin === true || authorizedUser.Admin === 1;
|
|
||||||
|
|
||||||
// Force session save before sending response
|
|
||||||
req.session.save((err) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Session save error:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Session error. Please try again.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`User authenticated: ${email}, Admin: ${req.session.isAdmin}`);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: 'Login successful',
|
|
||||||
user: {
|
|
||||||
email: email,
|
|
||||||
name: req.session.userName,
|
|
||||||
isAdmin: req.session.isAdmin
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
logger.warn(`Authentication failed for email: ${email}`);
|
|
||||||
res.status(401).json({
|
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Email not authorized. Please contact an administrator.'
|
error: 'Invalid email or password'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const user = users[0];
|
||||||
|
|
||||||
|
// Check password (plain text comparison for now)
|
||||||
|
if (user.Password !== password && user.password !== password) {
|
||||||
|
logger.warn(`Invalid password for email: ${email}`);
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Invalid email or password'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last login time
|
||||||
|
try {
|
||||||
|
const updateUrl = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}/${user.Id || user.id || user.ID}`;
|
||||||
|
await axios.patch(updateUrl, {
|
||||||
|
'Last Login': new Date().toISOString(),
|
||||||
|
last_login: new Date().toISOString()
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
'xc-token': process.env.NOCODB_API_TOKEN,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (updateError) {
|
||||||
|
logger.warn('Failed to update last login time:', updateError.message);
|
||||||
|
// Don't fail the login if we can't update last login time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set session including admin status
|
||||||
|
req.session.authenticated = true;
|
||||||
|
req.session.userEmail = email;
|
||||||
|
req.session.userName = user.Name || user.name || email;
|
||||||
|
req.session.isAdmin = user.Admin === true || user.Admin === 1 || user.admin === true || user.admin === 1;
|
||||||
|
req.session.userId = user.Id || user.id || user.ID;
|
||||||
|
|
||||||
|
// Force session save before sending response
|
||||||
|
req.session.save((err) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error('Session save error:', err);
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Session error. Please try again.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`User authenticated: ${email}, Admin: ${req.session.isAdmin}`);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Login successful',
|
||||||
|
user: {
|
||||||
|
email: email,
|
||||||
|
name: req.session.userName,
|
||||||
|
isAdmin: req.session.isAdmin
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Login error:', error.message);
|
logger.error('Login error:', error.message);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
@ -1823,6 +1848,172 @@ app.get('/api/debug/walk-sheet-raw', requireAdmin, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Admin user management endpoints
|
||||||
|
app.get('/api/admin/users', requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!LOGIN_SHEET_ID) {
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Login sheet not configured'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
|
||||||
|
|
||||||
|
const response = await axios.get(url, {
|
||||||
|
headers: {
|
||||||
|
'xc-token': process.env.NOCODB_API_TOKEN,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
limit: 100,
|
||||||
|
sort: '-created_at'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const users = response.data.list || [];
|
||||||
|
|
||||||
|
// Remove password field from response for security
|
||||||
|
const safeUsers = users.map(user => {
|
||||||
|
const { Password, password, ...safeUser } = user;
|
||||||
|
return safeUser;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
users: safeUsers
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error fetching users:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to fetch users'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/admin/users', requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { email, password, name, admin } = req.body;
|
||||||
|
|
||||||
|
if (!email || !password) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Email and password are required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LOGIN_SHEET_ID) {
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Login sheet not configured'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user already exists
|
||||||
|
const checkUrl = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}`;
|
||||||
|
|
||||||
|
const checkResponse = await axios.get(checkUrl, {
|
||||||
|
headers: {
|
||||||
|
'xc-token': process.env.NOCODB_API_TOKEN,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
where: `(Email,eq,${email})`,
|
||||||
|
limit: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkResponse.data.list && checkResponse.data.list.length > 0) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'User with this email already exists'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new user
|
||||||
|
const userData = {
|
||||||
|
Email: email,
|
||||||
|
email: email,
|
||||||
|
Password: password,
|
||||||
|
password: password,
|
||||||
|
Name: name || '',
|
||||||
|
name: name || '',
|
||||||
|
Admin: admin === true,
|
||||||
|
admin: admin === true,
|
||||||
|
'Created At': new Date().toISOString(),
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axios.post(checkUrl, userData, {
|
||||||
|
headers: {
|
||||||
|
'xc-token': process.env.NOCODB_API_TOKEN,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
message: 'User created successfully',
|
||||||
|
user: {
|
||||||
|
id: response.data.Id || response.data.id || response.data.ID,
|
||||||
|
email: email,
|
||||||
|
name: name,
|
||||||
|
admin: admin
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error creating user:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to create user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/admin/users/:id', requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const userId = req.params.id;
|
||||||
|
|
||||||
|
if (!LOGIN_SHEET_ID) {
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Login sheet not configured'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow admins to delete themselves
|
||||||
|
if (userId === req.session.userId) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Cannot delete your own account'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${LOGIN_SHEET_ID}/${userId}`;
|
||||||
|
|
||||||
|
await axios.delete(url, {
|
||||||
|
headers: {
|
||||||
|
'xc-token': process.env.NOCODB_API_TOKEN
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'User deleted successfully'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error deleting user:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to delete user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Error handling middleware
|
// Error handling middleware
|
||||||
app.use((err, req, res, next) => {
|
app.use((err, req, res, next) => {
|
||||||
logger.error('Unhandled error:', err);
|
logger.error('Unhandled error:', err);
|
||||||
|
|||||||
@ -411,6 +411,12 @@ create_login_table() {
|
|||||||
"uidt": "Email",
|
"uidt": "Email",
|
||||||
"rqd": true
|
"rqd": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"column_name": "password",
|
||||||
|
"title": "Password",
|
||||||
|
"uidt": "SingleLineText",
|
||||||
|
"rqd": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"column_name": "name",
|
"column_name": "name",
|
||||||
"title": "Name",
|
"title": "Name",
|
||||||
@ -563,9 +569,10 @@ create_default_admin() {
|
|||||||
local login_table_id=$2
|
local login_table_id=$2
|
||||||
|
|
||||||
print_status "Creating default admin user..."
|
print_status "Creating default admin user..."
|
||||||
|
|
||||||
local admin_data='{
|
local admin_data='{
|
||||||
"email": "admin@thebunkerops.ca",
|
"email": "admin@thebunkerops.ca",
|
||||||
|
"password": "admin123",
|
||||||
"name": "Administrator",
|
"name": "Administrator",
|
||||||
"admin": true,
|
"admin": true,
|
||||||
"created_at": "'"$(date -u +"%Y-%m-%d %H:%M:%S")"'"
|
"created_at": "'"$(date -u +"%Y-%m-%d %H:%M:%S")"'"
|
||||||
@ -663,8 +670,15 @@ main() {
|
|||||||
print_status " - NOCODB_VIEW_URL (for locations table)"
|
print_status " - NOCODB_VIEW_URL (for locations table)"
|
||||||
print_status " - NOCODB_LOGIN_SHEET (for login table)"
|
print_status " - NOCODB_LOGIN_SHEET (for login table)"
|
||||||
print_status " - NOCODB_SETTINGS_SHEET (for settings table)"
|
print_status " - NOCODB_SETTINGS_SHEET (for settings table)"
|
||||||
print_status "4. The default admin user is: admin@thebunkerops.ca"
|
print_status "4. The default admin user is: admin@thebunkerops.ca with password: admin123"
|
||||||
print_status "5. Start adding your location data!"
|
print_status "5. IMPORTANT: Change the default password after first login!"
|
||||||
|
print_status "6. Start adding your location data!"
|
||||||
|
|
||||||
|
print_warning ""
|
||||||
|
print_warning "IMPORTANT: This script created a NEW base. Your existing data was NOT modified."
|
||||||
|
print_warning "Please update your .env file with the new table URLs from the newly created base."
|
||||||
|
print_warning "SECURITY: Change the default admin password immediately after first login!"
|
||||||
|
|
||||||
|
|
||||||
print_warning ""
|
print_warning ""
|
||||||
print_warning "IMPORTANT: This script created a NEW base. Your existing data was NOT modified."
|
print_warning "IMPORTANT: This script created a NEW base. Your existing data was NOT modified."
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user