Restrict server detail access for guests
All checks were successful
Deploy GSM / deploy (push) Successful in 26s
All checks were successful
Deploy GSM / deploy (push) Successful in 26s
- Add isGuest flag to UserContext - Block guests from navigating to /server/:id route - Make ServerCards non-clickable for guests - Add rejectGuest middleware to backend - Protect server detail endpoints (/:id, /metrics/history, /whitelist) Guests can now only view the dashboard overview without accessing individual server details. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,10 +48,20 @@ export function requireRole(minRole) {
|
||||
const userRole = req.user?.role || 'user';
|
||||
const userLevel = ROLE_HIERARCHY[userRole] || 0;
|
||||
const requiredLevel = ROLE_HIERARCHY[minRole] || 0;
|
||||
|
||||
|
||||
if (userLevel < requiredLevel) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
export function rejectGuest(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Authentication required' });
|
||||
}
|
||||
if (req.user.isGuest || req.user.role === 'guest') {
|
||||
return res.status(403).json({ error: 'Guests cannot access server details' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Router } from 'express';
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { authenticateToken, optionalAuth, requireRole } from '../middleware/auth.js';
|
||||
import { authenticateToken, optionalAuth, requireRole, rejectGuest } from '../middleware/auth.js';
|
||||
import { getServerStatus, startServer, stopServer, restartServer, getConsoleLog, getProcessUptime, listFactorioSaves, createFactorioWorld, deleteFactorioSave, getFactorioCurrentSave, isHostFailed, listZomboidConfigs, readZomboidConfig, writeZomboidConfig, listPalworldConfigs, readPalworldConfig, writePalworldConfig, readTerrariaConfig, writeTerrariaConfig, readOpenTTDConfig, writeOpenTTDConfig } from '../services/ssh.js';
|
||||
import { sendRconCommand, getPlayers, getPlayerList } from '../services/rcon.js';
|
||||
import { getServerMetricsHistory, getCurrentMetrics } from '../services/prometheus.js';
|
||||
@@ -501,8 +501,8 @@ router.put("/openttd/config", authenticateToken, requireRole("moderator"), async
|
||||
});
|
||||
|
||||
|
||||
// Get single server
|
||||
router.get('/:id', optionalAuth, async (req, res) => {
|
||||
// Get single server (guests not allowed)
|
||||
router.get('/:id', authenticateToken, rejectGuest, async (req, res) => {
|
||||
const config = loadConfig();
|
||||
const server = config.servers.find(s => s.id === req.params.id);
|
||||
if (!server) {
|
||||
@@ -578,8 +578,8 @@ router.get('/:id', optionalAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Get metrics history from Prometheus
|
||||
router.get('/:id/metrics/history', optionalAuth, async (req, res) => {
|
||||
// Get metrics history from Prometheus (guests not allowed)
|
||||
router.get('/:id/metrics/history', authenticateToken, rejectGuest, async (req, res) => {
|
||||
const config = loadConfig();
|
||||
const server = config.servers.find(s => s.id === req.params.id);
|
||||
if (!server) return res.status(404).json({ error: 'Server not found' });
|
||||
@@ -706,8 +706,8 @@ router.post('/:id/restart', authenticateToken, requireRole('moderator'), async (
|
||||
}
|
||||
});
|
||||
|
||||
// Get whitelist (with server-side caching)
|
||||
router.get('/:id/whitelist', optionalAuth, async (req, res) => {
|
||||
// Get whitelist (with server-side caching, guests not allowed)
|
||||
router.get('/:id/whitelist', authenticateToken, rejectGuest, async (req, res) => {
|
||||
const config = loadConfig();
|
||||
const server = config.servers.find(s => s.id === req.params.id);
|
||||
if (!server) return res.status(404).json({ error: 'Server not found' });
|
||||
|
||||
Reference in New Issue
Block a user