Protect launcher admin tools
This commit is contained in:
parent
fad065c429
commit
ab1dfbf029
4 changed files with 384 additions and 151 deletions
81
server.js
81
server.js
|
|
@ -10,6 +10,38 @@ const __dirname = path.dirname(__filename);
|
|||
const app = express();
|
||||
const port = Number(process.env.PORT) || 5180;
|
||||
const host = process.env.HOST || "0.0.0.0";
|
||||
const launcherAdminPassword = String(process.env.LAUNCHER_ADMIN_PASSWORD || "").trim();
|
||||
|
||||
function isLauncherAdminProtectionEnabled() {
|
||||
return Boolean(launcherAdminPassword);
|
||||
}
|
||||
|
||||
function readLauncherAdminPasswordCandidate(req) {
|
||||
const headerValue = req.get("x-worldshaper-admin-password");
|
||||
if (String(headerValue || "").trim()) {
|
||||
return String(headerValue || "").trim();
|
||||
}
|
||||
return String(req.body?.password || "").trim();
|
||||
}
|
||||
|
||||
function requireLauncherAdminAccess(req, res) {
|
||||
if (!isLauncherAdminProtectionEnabled()) {
|
||||
res.status(503).json({
|
||||
error: "Launcher admin access is not configured on the server.",
|
||||
adminConfigured: false,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const submittedPassword = readLauncherAdminPasswordCandidate(req);
|
||||
if (!submittedPassword || submittedPassword !== launcherAdminPassword) {
|
||||
res.status(401).json({
|
||||
error: "Admin access denied.",
|
||||
adminConfigured: true,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveContentRoot() {
|
||||
const envPath = String(process.env.CONTENT_ROOT || "").trim();
|
||||
|
|
@ -2610,6 +2642,9 @@ app.get("/api/types", (_req, res) => {
|
|||
});
|
||||
|
||||
app.get("/api/debug/paths", (_req, res) => {
|
||||
if (!requireLauncherAdminAccess(_req, res)) {
|
||||
return;
|
||||
}
|
||||
const contentFiles = Object.fromEntries(
|
||||
Object.entries(contentMap).map(([type, entry]) => {
|
||||
const fullPath = path.join(contentRoot, entry.file);
|
||||
|
|
@ -2637,7 +2672,37 @@ app.get("/api/debug/paths", (_req, res) => {
|
|||
});
|
||||
});
|
||||
|
||||
app.get("/api/debug/recent-saves", (_req, res) => {
|
||||
app.post("/api/admin/auth-check", (req, res) => {
|
||||
if (!isLauncherAdminProtectionEnabled()) {
|
||||
res.status(503).json({
|
||||
ok: false,
|
||||
accessGranted: false,
|
||||
adminConfigured: false,
|
||||
error: "Launcher admin access is not configured on the server.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const accessGranted = readLauncherAdminPasswordCandidate(req) === launcherAdminPassword;
|
||||
if (!accessGranted) {
|
||||
res.status(401).json({
|
||||
ok: false,
|
||||
accessGranted: false,
|
||||
adminConfigured: true,
|
||||
error: "Admin access denied.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.json({
|
||||
ok: true,
|
||||
accessGranted: true,
|
||||
adminConfigured: true,
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/api/debug/recent-saves", (req, res) => {
|
||||
if (!requireLauncherAdminAccess(req, res)) {
|
||||
return;
|
||||
}
|
||||
res.json({
|
||||
ok: true,
|
||||
contentRoot,
|
||||
|
|
@ -2701,6 +2766,9 @@ app.post("/api/launcher-requests", (req, res) => {
|
|||
|
||||
app.patch("/api/launcher-requests/:requestId", (req, res) => {
|
||||
const requestId = String(req.params.requestId || "").trim();
|
||||
if (!requireLauncherAdminAccess(req, res)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = readLauncherRequestsPayload();
|
||||
const index = payload.requests.findIndex((entry) => entry.id === requestId);
|
||||
|
|
@ -2745,6 +2813,9 @@ app.patch("/api/launcher-requests/:requestId", (req, res) => {
|
|||
|
||||
app.delete("/api/launcher-requests/:requestId", (req, res) => {
|
||||
const requestId = String(req.params.requestId || "").trim();
|
||||
if (!requireLauncherAdminAccess(req, res)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = readLauncherRequestsPayload();
|
||||
const existing = payload.requests.find((entry) => entry.id === requestId);
|
||||
|
|
@ -2775,6 +2846,9 @@ app.delete("/api/launcher-requests/:requestId", (req, res) => {
|
|||
app.post("/api/launcher-requests/:requestId/process-analysis", (req, res) => {
|
||||
const requestId = String(req.params.requestId || "").trim();
|
||||
const action = String(req.body?.action || "").trim().toLowerCase();
|
||||
if (!requireLauncherAdminAccess(req, res)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = readLauncherRequestsPayload();
|
||||
const index = payload.requests.findIndex((entry) => entry.id === requestId);
|
||||
|
|
@ -2866,7 +2940,10 @@ app.post("/api/launcher-requests/:requestId/process-analysis", (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
app.post("/api/launcher-requests/process-pending", (_req, res) => {
|
||||
app.post("/api/launcher-requests/process-pending", (req, res) => {
|
||||
if (!requireLauncherAdminAccess(req, res)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = launchQueuedRequestAnalysis("manual-api-trigger");
|
||||
res.json({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue