Files
ProxmoxAAS-API/src/routes/access.js
2025-02-17 21:19:54 +00:00

140 lines
3.9 KiB
JavaScript

import { Router } from "express";
export const router = Router({ mergeParams: true }); ;
const checkAuth = global.utils.checkAuth;
global.utils.recursiveImportRoutes(router, "", "access", import.meta.url);
/**
* GET - check authentication
* responses:
* - 200: {auth: true}
* - 401: {auth: false}
*/
router.get("/", async (req, res) => {
const auth = await checkAuth(req.cookies, res);
if (!auth) {
return;
}
res.status(200).send({ auth: true });
});
/**
* Fetches and consumes cookies from backends and avoids duplicate cookies from repeat backends. Also helps handle errors.
*/
class CookieFetcher {
#fetchedBackends = [];
#cookies = [];
async fetchBackends (backends, user, password) {
for (const backend of backends) {
if (this.#fetchedBackends.indexOf(backend) === -1) {
const response = await global.backends[backend].openSession(user, password);
if (!response.ok) {
return response.message;
}
this.#cookies = this.#cookies.concat(response.cookies);
this.#fetchedBackends.push(backend);
}
else { // assume that repeat backends should not be requested
continue;
}
}
return null;
}
exportCookies () {
return this.#cookies;
}
}
/**
* POST - safer ticket generation using proxmox authentication but adding HttpOnly
* request:
* - username: string
* - password: string
* responses:
* - 200: {auth: true}
* - 401: {auth: false}
*/
router.post("/ticket", async (req, res) => {
const params = {
username: req.body.username,
password: req.body.password
};
const domain = global.config.application.domain;
const userObj = global.utils.getUserObjFromUsername(params.username);
let backends = global.userManager.getBackendsByUser(userObj);
if (backends == null) {
res.status(401).send({ auth: false, error: `${params.username} not found in any ProxmoxAAS backends` });
return;
}
backends = backends.concat(["pve"]);
const cm = new CookieFetcher();
const error = await cm.fetchBackends(backends, userObj, params.password);
if (error) {
res.status(401).send({ auth: false, error });
return;
}
const cookies = cm.exportCookies();
let minimumExpires = Infinity;
for (const cookie of cookies) {
const expiresDate = new Date(Date.now() + cookie.expiresMSFromNow);
res.cookie(cookie.name, cookie.value, { domain, path: "/", httpOnly: true, secure: true, expires: expiresDate, sameSite: "none" });
if (cookie.expiresMSFromNow < minimumExpires) {
minimumExpires = cookie.expiresMSFromNow;
}
}
const expiresDate = new Date(Date.now() + minimumExpires);
res.cookie("username", params.username, { domain, path: "/", secure: true, expires: expiresDate, sameSite: "none" });
res.cookie("auth", 1, { domain, path: "/", secure: true, expires: expiresDate, sameSite: "none" });
res.status(200).send({ auth: true });
});
/**
* DELETE - request to destroy ticket
* responses:
* - 200: {auth: false}
*/
router.delete("/ticket", async (req, res) => {
if (Object.keys(req.cookies).length === 0) {
res.status(200).send({ auth: false });
return;
}
const domain = global.config.application.domain;
const expire = new Date(0);
for (const cookie in req.cookies) {
res.cookie(cookie, "", { domain, path: "/", expires: expire, secure: true, sameSite: "none" });
}
await global.pve.closeSession(req.cookies);
await global.userManager.closeSession(req.cookies);
res.status(200).send({ auth: false });
});
/**
* POST - change user password
* request:
* - password: string
* responses:
* - PAAS-LDAP API response
*/
router.post("/password", async (req, res) => {
const params = {
username: req.cookies.username,
password: req.body.password
};
// check auth
const auth = await checkAuth(req.cookies, res);
if (!auth) {
return;
}
const userObj = global.utils.getUserObjFromUsername(params.username);
const newAttributes = {
userpassword: params.password
};
const response = await global.userManager.setUser(userObj, newAttributes, req.cookies);
res.status(response.status).send(response);
});