updated temp user control access and limited data send for temps

This commit is contained in:
admin 2025-08-04 12:54:04 -06:00
parent 2ebbb2dc44
commit 2591cfe8a8
5 changed files with 207 additions and 22 deletions

View File

@ -44,6 +44,44 @@ class LocationsController {
logger.info(`Retrieved ${validLocations.length} valid locations out of ${locations.length} total`); logger.info(`Retrieved ${validLocations.length} valid locations out of ${locations.length} total`);
// Check if user is temp user and limit data accordingly
if (req.session?.userType === 'temp') {
// For temp users, return limited data but include necessary fields for functionality
const limitedLocations = validLocations.map(loc => {
const locationId = loc.id || loc.Id || loc.ID || loc._id;
return {
// Include ID with all possible variants for compatibility
id: locationId,
Id: locationId,
ID: locationId,
_id: locationId,
'Geo-Location': loc['Geo-Location'],
latitude: loc.latitude,
longitude: loc.longitude,
// Include display fields needed for map functionality
'First Name': loc['First Name'] || '',
'Last Name': loc['Last Name'] || '', // Include last name for display
'Support Level': loc['Support Level'],
Address: loc.Address || '',
'Unit Number': loc['Unit Number'] || '',
Notes: loc.Notes || '',
Sign: loc.Sign,
'Sign Size': loc['Sign Size'] || '',
// Exclude sensitive fields like Email, Phone
};
});
logger.info(`Returning limited data for temp user: ${limitedLocations.length} locations`);
return res.json({
success: true,
count: limitedLocations.length,
total: response.pageInfo?.totalRows || limitedLocations.length,
locations: limitedLocations,
isLimited: true // Flag to indicate limited data
});
}
res.json({ res.json({
success: true, success: true,
count: validLocations.length, count: validLocations.length,

View File

@ -40,8 +40,29 @@ const authLimiter = rateLimit({
skipSuccessfulRequests: true skipSuccessfulRequests: true
}); });
// Temp user rate limiter - much stricter limits
const tempUserLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // Much lower limit for temp users
keyGenerator,
standardHeaders: true,
legacyHeaders: false,
trustProxy: true,
message: 'Too many requests for temporary account. Please contact an administrator for full access.'
});
// Conditional rate limiter that applies stricter limits to temp users
const conditionalTempLimiter = (req, res, next) => {
if (req.session?.userType === 'temp') {
return tempUserLimiter(req, res, next);
}
return apiLimiter(req, res, next);
};
module.exports = { module.exports = {
apiLimiter, apiLimiter,
strictLimiter, strictLimiter,
authLimiter authLimiter,
tempUserLimiter,
conditionalTempLimiter
}; };

View File

@ -29,15 +29,25 @@ let originalIcon = null;
export async function loadLocations() { export async function loadLocations() {
try { try {
showStatus('Loading locations...', 'info');
const response = await fetch('/api/locations'); const response = await fetch('/api/locations');
const data = await response.json(); const data = await response.json();
if (data.success) { if (!data.success) {
displayLocations(data.locations);
updateLocationCount(data.locations.length);
} else {
throw new Error(data.error || 'Failed to load locations'); throw new Error(data.error || 'Failed to load locations');
} }
// Check if data is limited (temp user)
if (data.isLimited && currentUser?.userType === 'temp') {
console.log('Map data loaded'); // Generic message for temp users
} else {
console.log(`Loaded ${data.count} locations from NocoDB`);
}
displayLocations(data.locations);
updateLocationCount(data.locations.length);
} catch (error) { } catch (error) {
console.error('Error loading locations:', error); console.error('Error loading locations:', error);
showStatus('Failed to load locations', 'error'); showStatus('Failed to load locations', 'error');
@ -91,7 +101,12 @@ export function displayLocations(locations) {
} }
}); });
console.log(`Displayed ${markers.length} location markers (${locations.length} total locations)`); // Limit console output for temp users
if (currentUser?.userType === 'temp') {
console.log('Map built successfully');
} else {
console.log(`Displayed ${markers.length} location markers (${locations.length} total locations)`);
}
} }
function createLocationMarker(location) { function createLocationMarker(location) {
@ -211,7 +226,10 @@ function createLocationMarker(location) {
marker.bindPopup(popupContent); marker.bindPopup(popupContent);
marker._locationData = location; marker._locationData = location;
console.log(`Created marker at ${lat}, ${lng} with color ${markerColor}`); // Only log marker creation for non-temp users
if (currentUser?.userType !== 'temp') {
console.log(`Created marker at ${lat}, ${lng} with color ${markerColor}`);
}
return marker; return marker;
} }
@ -219,14 +237,45 @@ function createLocationMarker(location) {
function createPopupContent(location) { function createPopupContent(location) {
const locationId = location.Id || location.id || location.ID || location._id; const locationId = location.Id || location.id || location.ID || location._id;
// If current user is temp, show limited information but allow editing
if (currentUser?.userType === 'temp') {
const name = [location['First Name'], location['Last Name']]
.filter(Boolean).join(' ') || 'Unknown';
const address = location.Address || 'No address';
const supportLevel = location['Support Level'] ?
`Level ${location['Support Level']}` : 'Not specified';
return `
<div class="popup-content">
<h3>${escapeHtml(name)}</h3>
<p><strong>Address:</strong> ${escapeHtml(address)}</p>
<p><strong>Support:</strong> ${escapeHtml(supportLevel)}</p>
${location.Sign ? '<p>🏁 Has campaign sign</p>' : ''}
${location.Notes ? `<p><strong>Notes:</strong> ${escapeHtml(location.Notes)}</p>` : ''}
<div class="popup-meta">
<p>ID: ${locationId || 'Unknown'}</p>
</div>
<div class="popup-actions">
<button class="btn btn-primary btn-sm edit-location-popup-btn"
data-location='${escapeHtml(JSON.stringify(location))}'>
Edit
</button>
</div>
</div>
`;
}
// Full information for regular users and admins
const name = [location['First Name'], location['Last Name']] const name = [location['First Name'], location['Last Name']]
.filter(Boolean).join(' ') || 'Unknown'; .filter(Boolean).join(' ') || 'Unknown';
const address = location.Address || 'No address'; const address = location.Address || 'No address';
const supportLevel = location['Support Level'] ? const supportLevel = location['Support Level'] ?
`Level ${location['Support Level']}` : 'Not specified'; `Level ${location['Support Level']}` : 'Not specified';
// Add debugging // Add debugging only for non-temp users
console.log('Creating popup for location:', locationId, location); if (currentUser?.userType !== 'temp') {
console.log('Creating popup for location:', locationId, location);
}
return ` return `
<div class="popup-content"> <div class="popup-content">
@ -319,6 +368,11 @@ export function openEditForm(location) {
// Extract ID - check multiple possible field names // Extract ID - check multiple possible field names
const locationId = location.Id || location.id || location.ID || location._id; const locationId = location.Id || location.id || location.ID || location._id;
// Add debugging for temp users
console.log('Opening edit form for location:', location);
console.log('Extracted ID:', locationId);
console.log('Available keys:', Object.keys(location));
if (!locationId) { if (!locationId) {
console.error('No ID found in location object. Available fields:', Object.keys(location)); console.error('No ID found in location object. Available fields:', Object.keys(location));
showStatus('Error: Location ID not found. Check console for details.', 'error'); showStatus('Error: Location ID not found. Check console for details.', 'error');
@ -803,7 +857,10 @@ function createMultiUnitMarker(group) {
setupAppApartmentPopupListeners(group); setupAppApartmentPopupListeners(group);
}); });
console.log(`Created multi-unit marker at ${lat}, ${lng} with ${locations.length} units`); // Only log multi-unit marker creation for non-temp users
if (currentUser?.userType !== 'temp') {
console.log(`Created multi-unit marker at ${lat}, ${lng} with ${locations.length} units`);
}
return marker; return marker;
} }
@ -883,8 +940,11 @@ function createUnitDetailsHTML(location, index) {
const name = [location['First Name'], location['Last Name']].filter(Boolean).join(' ') || 'Unknown'; const name = [location['First Name'], location['Last Name']].filter(Boolean).join(' ') || 'Unknown';
const unit = location['Unit Number'] || `Unit ${index + 1}`; const unit = location['Unit Number'] || `Unit ${index + 1}`;
const supportLevel = location['Support Level'] ? `Level ${location['Support Level']}` : 'Not specified'; const supportLevel = location['Support Level'] ? `Level ${location['Support Level']}` : 'Not specified';
const email = location.Email || '';
const phone = location.Phone || ''; // For temp users, hide sensitive contact information
const isTemp = currentUser?.userType === 'temp';
const email = !isTemp ? (location.Email || '') : '';
const phone = !isTemp ? (location.Phone || '') : '';
// Truncate long values for mobile // Truncate long values for mobile
const truncatedEmail = email.length > 25 ? email.substring(0, 25) + '...' : email; const truncatedEmail = email.length > 25 ? email.substring(0, 25) + '...' : email;
@ -920,11 +980,13 @@ function createUnitDetailsHTML(location, index) {
style="background: #a02c8d; border: none; padding: 3px 6px; border-radius: 3px; font-size: 9px; cursor: pointer; flex: 1; min-width: 45%;"> style="background: #a02c8d; border: none; padding: 3px 6px; border-radius: 3px; font-size: 9px; cursor: pointer; flex: 1; min-width: 45%;">
Edit Edit
</button> </button>
<button class="btn btn-primary btn-sm move-unit-btn" ${currentUser.userType !== 'temp' ? `
data-location='${escapeHtml(JSON.stringify(location))}' <button class="btn btn-primary btn-sm move-unit-btn"
style="background: #6c757d; border: none; padding: 3px 6px; border-radius: 3px; font-size: 9px; cursor: pointer; flex: 1; min-width: 45%;"> data-location='${escapeHtml(JSON.stringify(location))}'
📍 Move style="background: #6c757d; border: none; padding: 3px 6px; border-radius: 3px; font-size: 9px; cursor: pointer; flex: 1; min-width: 45%;">
</button> 📍 Move
</button>
` : ''}
</div> </div>
</div> </div>
` : ''} ` : ''}

View File

@ -30,6 +30,9 @@ document.addEventListener('DOMContentLoaded', async () => {
// First check authentication // First check authentication
await checkAuth(); await checkAuth();
// Setup temp user security measures after authentication
setupTempUserSecurity();
// Then initialize the map // Then initialize the map
await initializeMap(); await initializeMap();
@ -99,3 +102,64 @@ async function initializeUnifiedSearch() {
console.error('Error setting up unified search:', error); console.error('Error setting up unified search:', error);
} }
} }
// Setup security measures for temp users
function setupTempUserSecurity() {
// Import currentUser from auth module
import('./auth.js').then(authModule => {
const { currentUser } = authModule;
if (currentUser?.userType === 'temp') {
console.log('Applying temp user security measures...');
// Disable right-click context menu
document.addEventListener('contextmenu', (e) => {
e.preventDefault();
return false;
});
// Basic developer tools detection
const devtools = { open: false };
let devtoolsTimer = setInterval(() => {
// Detect if developer tools are open by checking window dimensions
if (window.outerHeight - window.innerHeight > 200 ||
window.outerWidth - window.innerWidth > 200) {
if (!devtools.open) {
console.clear();
console.log('Access to developer tools is restricted for temporary accounts.');
console.log('Please contact an administrator for full access.');
}
devtools.open = true;
} else {
devtools.open = false;
}
}, 1000);
// Clear interval on page unload
window.addEventListener('beforeunload', () => {
if (devtoolsTimer) {
clearInterval(devtoolsTimer);
}
});
// Disable common keyboard shortcuts for developer tools
document.addEventListener('keydown', (e) => {
// F12
if (e.keyCode === 123) {
e.preventDefault();
return false;
}
// Ctrl+Shift+I, Ctrl+Shift+J, Ctrl+U
if (e.ctrlKey && e.shiftKey && (e.keyCode === 73 || e.keyCode === 74)) {
e.preventDefault();
return false;
}
// Ctrl+U (view source)
if (e.ctrlKey && e.keyCode === 85) {
e.preventDefault();
return false;
}
});
}
});
}

View File

@ -1,14 +1,14 @@
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
const locationsController = require('../controllers/locationsController'); const locationsController = require('../controllers/locationsController');
const { strictLimiter } = require('../middleware/rateLimiter'); const { strictLimiter, conditionalTempLimiter } = require('../middleware/rateLimiter');
const { requireAuth, requireDeletePermission } = require('../middleware/auth'); const { requireAuth, requireDeletePermission } = require('../middleware/auth');
// Get all locations (public) // Get all locations (apply stricter rate limiting for temp users)
router.get('/', locationsController.getAll); router.get('/', conditionalTempLimiter, locationsController.getAll);
// Get single location (public) // Get single location (apply stricter rate limiting for temp users)
router.get('/:id', locationsController.getById); router.get('/:id', conditionalTempLimiter, locationsController.getById);
// Create location (requires authentication) // Create location (requires authentication)
router.post('/', requireAuth, strictLimiter, locationsController.create); router.post('/', requireAuth, strictLimiter, locationsController.create);