very-express
Version:
based on json schema and openapi, generate Express RestApi server with mongoDb
66 lines (53 loc) • 2.32 kB
text/typescript
// {{headerComment}}
import { Request, Response, NextFunction } from "express";
import { VexResErr } from "../_types/vex";
import joinWhitelistRegistry from "./JoinWhitelistRegistry.gen";
/**
* Validates join paths against the centralized whitelist registry.
*
* The registry maps documentName → { relationName → relatedDocumentName }
* and is generated at build time by VeryExpress from schema FK metadata.
*
* Nested paths like "user.userContact" on Package are validated hop-by-hop:
* 1. check "user" in Package's map → next doc = "User"
* 2. check "userContact" in User's map → ok
*/
class JoinWhitelistMiddleware {
middleware(documentName: string) {
return (req: Request, res: Response, next: NextFunction): void => {
const join = this.extractJoin(req);
if (!join || join.length === 0) {
return next();
}
for (const joinPath of join) {
this.validateJoinPath(joinPath, documentName);
}
next();
};
}
private validateJoinPath(joinPath: string, rootDocumentName: string): void {
const segments = joinPath.split(".");
let currentDoc = rootDocumentName;
const visited = new Set<string>();
for (const segment of segments) {
if (visited.has(currentDoc)) {
throw new VexResErr(403, undefined, `Circular join detected: "${joinPath}" revisits "${currentDoc}"`);
}
visited.add(currentDoc);
const relations = joinWhitelistRegistry[currentDoc];
if (!relations || !(segment in relations)) {
throw new VexResErr(403, undefined, `Join "${segment}" is not allowed on "${currentDoc}"`);
}
currentDoc = relations[segment];
}
}
private extractJoin(req: Request): string[] | undefined {
// getList sends join inside POST body; get sends it as query param
const bodyJoin = (req.body as { join?: string[] })?.join;
const queryJoin = req.query?.join;
if (bodyJoin) return bodyJoin;
if (!queryJoin) return undefined;
return Array.isArray(queryJoin) ? (queryJoin as string[]) : [queryJoin as string];
}
}
export default new JoinWhitelistMiddleware();