2025-01-29 11:13:55 +01:00

267 lines
8.1 KiB
TypeScript

/* Requires */
import express = require("express");
import path = require("path");
import logger = require("morgan");
import fs = require("fs");
import bodyParser = require("body-parser");
import cors = require("cors");
import { AudioBackend } from "./audio/audiobackend";
import expressBasicAuth from "express-basic-auth";
/* Load the proxy middleware plugin */
import httpProxyMiddleware = require("http-proxy-middleware");
function checkCustomHeaders(config, usersConfig, groupsConfig, req) {
let user = req.auth?.user ?? null;
let group = null;
/* Check if custom authorization headers are enabled */
if (
"customAuthHeaders" in config["frontend"] &&
config["frontend"]["customAuthHeaders"]["enabled"]
) {
/* If so, check that the custom headers are indeed present */
if (
config["frontend"]["customAuthHeaders"]["username"].toLowerCase() in
req.headers &&
config["frontend"]["customAuthHeaders"]["group"].toLowerCase() in
req.headers
) {
/* If they are, assign the group */
group =
req.headers[
config["frontend"]["customAuthHeaders"]["group"].toLowerCase()
];
/* Check that the user is in an existing group */
if (group in groupsConfig) {
user =
req.headers[
config["frontend"]["customAuthHeaders"]["username"].toLowerCase()
];
usersConfig[user] = { password: null, roles: groupsConfig[group] };
}
}
}
return user;
}
module.exports = function (configLocation, viteProxy) {
/* Config specific routers */
const elevationRouter = require("./routes/api/elevation")(configLocation);
const resourcesRouter = require("./routes/resources")(configLocation);
/* Default routers */
const airbasesRouter = require("./routes/api/airbases");
const databasesRouter = require("./routes/api/databases")(
path.join(
path.dirname(configLocation),
"..",
"Mods",
"Services",
"Olympus",
"databases"
)
);
const speechRouter = require("./routes/api/speech")();
/* Read the users configuration file */
let usersConfig = {};
if (
fs.existsSync(path.join(path.dirname(configLocation), "olympusUsers.json"))
) {
let rawdata = fs.readFileSync(
path.join(path.dirname(configLocation), "olympusUsers.json"),
{ encoding: "utf-8" }
);
usersConfig = JSON.parse(rawdata);
}
/* Read the groups configuration file */
let groupsConfig = {};
if (
fs.existsSync(path.join(path.dirname(configLocation), "olympusGroups.json"))
) {
let rawdata = fs.readFileSync(
path.join(path.dirname(configLocation), "olympusGroups.json"),
{ encoding: "utf-8" }
);
groupsConfig = JSON.parse(rawdata);
}
/* Load the config and create the express app */
let config = {};
console.log(`Loading configuration file from ${configLocation}`);
if (fs.existsSync(configLocation)) {
let rawdata = fs.readFileSync(configLocation, { encoding: "utf-8" });
config = JSON.parse(rawdata);
} else {
console.error("Error loading configuration file.");
return undefined;
}
/* Load the backend address where DCS is listening */
const backendAddress = config["backend"]["address"];
/* Start the express app */
const app = express();
/* Define the authentication */
const defaultUsers = {
"Game master": config["authentication"]["gameMasterPassword"],
"Blue commander": config["authentication"]["blueCommanderPassword"],
"Red commander": config["authentication"]["redCommanderPassword"],
};
let users = {};
Object.keys(usersConfig).forEach(
(user) => (users[user] = usersConfig[user].password)
);
const auth = expressBasicAuth({
users: { ...defaultUsers, ...users },
});
/* Define middleware */
app.use(logger("dev"));
/* Authorization middleware */
if (
"customAuthHeaders" in config["frontend"] &&
config["frontend"]["customAuthHeaders"]["enabled"]
) {
/* Custom authorization will be used */
app.use("/", async (req, res, next) => {
const user = checkCustomHeaders(config, usersConfig, groupsConfig, req);
if (user) {
/* If the user is preauthorized, set the authorization headers to the response */
res.set(
config["frontend"]["customAuthHeaders"]["username"],
req.headers[
config["frontend"]["customAuthHeaders"]["username"].toLowerCase()
]
);
res.set(
config["frontend"]["customAuthHeaders"]["group"],
req.headers[
config["frontend"]["customAuthHeaders"]["group"].toLowerCase()
]
);
}
next();
});
} else {
/* Simple internal authorization will be used */
app.use("/olympus", auth);
}
/* Define the middleware to replace the authorization header for the olympus backend */
app.use("/olympus", async (req, res, next) => {
/* Check if custom authorization headers are being used */
const user =
//@ts-ignore
req.auth?.user ??
checkCustomHeaders(config, usersConfig, groupsConfig, req);
/* If either simple authentication or custom authentication has succeded */
if (user) {
const userConfig = usersConfig[user];
/* Check that the user is authorized to at least one role */
if (userConfig && userConfig.roles.length > 0) {
/* If a specific command role is requested, proceed with that role */
if (req.headers["x-command-mode"]) {
/* Check that the user is authorized to that role */
if (userConfig.roles.includes(req.headers["x-command-mode"])) {
/* Check that the role is valid */
//@ts-ignore
if (req.headers["x-command-mode"] in defaultUsers) {
/* Apply the authorization headers */
req.headers.authorization = `Basic ${btoa(
//@ts-ignore
user + ":" + defaultUsers[req.headers["x-command-mode"]]
)}`;
} else {
res.sendStatus(401); // Unauthorized
}
} else {
res.sendStatus(401); // Unauthorized
}
} else {
/* No role has been specified, continue with the highest role */
/* Check that the role is valid */
if (userConfig.roles[0] in defaultUsers) {
/* Apply the authorization headers */
req.headers.authorization = `Basic ${btoa(
userConfig.roles[0] + ":" + defaultUsers[userConfig.roles[0]]
)}`;
} else {
res.sendStatus(401); // Unauthorized
}
}
} else {
if (!(user in defaultUsers)) res.sendStatus(401); // Unauthorized
}
/* Send back the roles that the user is enabled to */
if (userConfig) res.set("X-Enabled-Command-Modes", `${userConfig.roles}`);
else if (user in defaultUsers)
res.set("X-Enabled-Command-Modes", `${user}`);
next();
} else {
res.sendStatus(401); // Unauthorized
}
});
/* Proxy middleware */
app.use(
"/olympus",
httpProxyMiddleware.createProxyMiddleware({
target: `http://${
backendAddress === "*" ? "localhost" : backendAddress
}:${config["backend"]["port"]}/olympus`,
changeOrigin: true,
})
);
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
app.use(express.static(path.join(__dirname, "..", "public")));
app.use(cors());
/* Apply routers */
app.use("/api/airbases", airbasesRouter);
app.use("/api/elevation", elevationRouter);
app.use("/api/databases", databasesRouter);
app.use("/api/speech", speechRouter);
app.use("/resources", resourcesRouter);
/* Set default index */
if (viteProxy) {
app.use(
"/",
httpProxyMiddleware.createProxyMiddleware({
target: `http://localhost:8080/`,
ws: true,
})
);
} else {
app.get("/", function (req, res) {
res.sendFile(path.join(__dirname, "..", "public", "index.html"));
});
}
/* Start the audio backend */
if (config["audio"]) {
let audioBackend = new AudioBackend(
config["audio"]["SRSPort"],
config["audio"]["WSPort"]
);
audioBackend.start();
}
return app;
};