Files
appServerPortalUI/auth/auth.js
2026-06-12 19:22:50 +02:00

157 lines
5.1 KiB
JavaScript
Executable File

import express from "express";
import cookieParser from "cookie-parser";
import bcrypt from "bcrypt";
import fs from "fs";
import crypto from "crypto";
import https from "https";
const USERS = JSON.parse(fs.readFileSync("./users.json"));
const SESSIONS = {}; // in-memory session store
// Robot-Driver hinter dem Tunnel (TLS, selbst-signiert -> Verify aus)
const DRIVER_HOST = "appServer_TunnelHead";
const DRIVER_PORT = 9798;
// Kleiner HTTPS-Proxy-Helfer zum Driver (Core-Modul, keine Extra-Dependency)
function driverRequest(method, path) {
return new Promise((resolve, reject) => {
const req = https.request(
{
host: DRIVER_HOST,
port: DRIVER_PORT,
path,
method,
rejectUnauthorized: false, // entspricht nginx proxy_ssl_verify off
timeout: 5000
},
(res) => {
let body = "";
res.on("data", (c) => { body += c; });
res.on("end", () => resolve({ status: res.statusCode, body }));
}
);
req.on("timeout", () => req.destroy(new Error("driver timeout")));
req.on("error", reject);
req.end();
});
}
// Session-Guard: true wenn eingeloggt, sonst sendet selbst 401
function requireSession(req, res) {
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) return true;
res.status(401).send({ ok: false, error: "not authenticated" });
return false;
}
const app = express();
app.use(express.json());
app.use(cookieParser());
app.post("/api/login", async (req,res)=>{
const { user, pass } = req.body;
console.log(`Auth-Service login attempt for ${user}`);
const hash = USERS[user];
if(!hash) return res.status(401).send({ ok:false });
const valid = await bcrypt.compare(pass, hash);
if(!valid) return res.status(401).send({ ok:false });
// create secure random session
const sessionID = crypto.randomBytes(32).toString("hex");
SESSIONS[sessionID] = {
user,
created: Date.now()
};
res.cookie("SESSIONID", sessionID, {
httpOnly: true,
secure: true,
domain: ".server.schooltech.ch",
sameSite: "None",
path: "/"
});
res.status(200).send({ ok:true });
});
// Logout endpoint
app.post("/api/logout", (req, res) => {
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) {
delete SESSIONS[sid];
}
// Cookie löschen
res.clearCookie("SESSIONID", {
httpOnly: true,
secure: true,
domain: ".server.schooltech.ch",
sameSite: "None",
path: "/"
});
return res.status(200).send({ ok: true });
});
// Event logging endpoint for frontend button presses
app.post('/api/event', (req,res)=>{
const svc = req.body.service || req.body.action || 'unknown';
const user = req.cookies.SESSIONID || 'anonymous';
console.log(`Event: user=${user} service=${svc} payload=${JSON.stringify(req.body)}`);
res.status(200).send({ ok:true });
});
// Optional für Nginx auth_request
app.get("/internal/auth", (req,res)=>{
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) {
return res.sendStatus(200);
}
return res.sendStatus(401);
});
// Status endpoint (unter /api so dass Nginx /api/ auf appserverauth proxyt)
app.get("/api/status", (req, res) => {
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) {
return res.status(200).send({ ok: true, user: SESSIONS[sid].user });
}
return res.status(401).send({ ok: false });
});
// ===========================
// Robot-Driver Proxy (same-origin, auth-geschützt)
// Nginx leitet /api/ auf diesen Service -> kein CORS, Cookie wird mitgeschickt.
// ===========================
// Armed-Status abfragen: GET /api/power-status
app.get("/api/power-status", async (req, res) => {
if (!requireSession(req, res)) return;
try {
const r = await driverRequest("GET", "/api/power-status");
res.status(r.status).type("application/json").send(r.body);
} catch (e) {
console.error("power-status proxy error:", e.message);
// Failsafe: Driver nicht erreichbar -> armed:false, Button bleibt versteckt
res.status(502).send({ ok: false, armed: false, error: "driver unreachable" });
}
});
// Not-Aus auslösen: POST /api/emergency-stop
app.post("/api/emergency-stop", async (req, res) => {
if (!requireSession(req, res)) return;
const user = SESSIONS[req.cookies.SESSIONID].user;
console.log(`EMERGENCY-STOP ausgelöst von user=${user}`);
try {
const r = await driverRequest("POST", "/api/emergency-stop");
res.status(r.status).type("application/json").send(r.body);
} catch (e) {
console.error("emergency-stop proxy error:", e.message);
res.status(502).send({ ok: false, error: "driver unreachable" });
}
});
app.listen(3000, ()=>console.log("Auth-Service läuft auf 3000"));