From d11837e449036e457372e16dac8ba67a7fdeef4a Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 9 Jul 2025 10:05:12 -0600 Subject: [PATCH] Added in a password field for login. need to add encryption sometime --- map/app/public/login.html | 15 ++- map/app/server.js | 277 ++++++++++++++++++++++++++++++++------ map/build-nocodb.sh | 20 ++- 3 files changed, 265 insertions(+), 47 deletions(-) diff --git a/map/app/public/login.html b/map/app/public/login.html index 88d9daa..69c5ba5 100644 --- a/map/app/public/login.html +++ b/map/app/public/login.html @@ -163,6 +163,18 @@ > +
+ + +
+ @@ -180,6 +192,7 @@ e.preventDefault(); const email = document.getElementById('email').value; + const password = document.getElementById('password').value; const button = document.getElementById('login-button'); const errorMessage = document.getElementById('error-message'); const successMessage = document.getElementById('success-message'); @@ -198,7 +211,7 @@ headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ email }), + body: JSON.stringify({ email, password }), credentials: 'include' }); diff --git a/map/app/server.js b/map/app/server.js index db1c6bd..0c4e7a1 100644 --- a/map/app/server.js +++ b/map/app/server.js @@ -311,12 +311,12 @@ app.post('/api/auth/login', authLimiter, async (req, res) => { userAgent: req.headers['user-agent'] }); - const { email } = req.body; + const { email, password } = req.body; - if (!email) { + if (!email || !password) { return res.status(400).json({ 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}`; logger.info(`Checking authentication for email: ${email}`); @@ -350,54 +350,79 @@ app.post('/api/auth/login', authLimiter, async (req, res) => { 'Content-Type': 'application/json' }, params: { - limit: 1000 // Adjust if you have more authorized users + where: `(Email,eq,${email})`, + limit: 1 } }); const users = response.data.list || []; - // Check if email exists in the authorized users list - const authorizedUser = users.find(user => - user.Email && user.Email.toLowerCase() === email.toLowerCase() - ); - - 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({ + if (users.length === 0) { + logger.warn(`No user found with email: ${email}`); + return res.status(401).json({ 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) { logger.error('Login error:', error.message); 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 app.use((err, req, res, next) => { logger.error('Unhandled error:', err); diff --git a/map/build-nocodb.sh b/map/build-nocodb.sh index 24180fe..e96d37b 100755 --- a/map/build-nocodb.sh +++ b/map/build-nocodb.sh @@ -411,6 +411,12 @@ create_login_table() { "uidt": "Email", "rqd": true }, + { + "column_name": "password", + "title": "Password", + "uidt": "SingleLineText", + "rqd": true + }, { "column_name": "name", "title": "Name", @@ -563,9 +569,10 @@ create_default_admin() { local login_table_id=$2 print_status "Creating default admin user..." - + local admin_data='{ "email": "admin@thebunkerops.ca", + "password": "admin123", "name": "Administrator", "admin": true, "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_LOGIN_SHEET (for login table)" print_status " - NOCODB_SETTINGS_SHEET (for settings table)" - print_status "4. The default admin user is: admin@thebunkerops.ca" - print_status "5. Start adding your location data!" + print_status "4. The default admin user is: admin@thebunkerops.ca with password: admin123" + 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 "IMPORTANT: This script created a NEW base. Your existing data was NOT modified."