mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-13 23:29:19 +00:00
49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
/**
|
|
* Who a plugin is acting as right now.
|
|
*
|
|
* Two ways a plugin gets an actor, and no third:
|
|
*
|
|
* - A request. A4's HTTP and WebSocket middleware runs the handler inside
|
|
* runAsActor with the user core already authenticated, so a route handler
|
|
* acts as the person who called it.
|
|
* - ctx.asUser(userId, fn), for background work with no request behind it.
|
|
* That call is always audited.
|
|
*
|
|
* Nothing else. A plugin cannot pass a user id to a guarded ctx method and
|
|
* have it believed, which is what stops the forged-caller class of bug the
|
|
* worker broker had.
|
|
*
|
|
* AsyncLocalStorage rather than a module variable because plugin work
|
|
* interleaves: two requests in flight would otherwise overwrite each other's
|
|
* identity between awaits.
|
|
*/
|
|
|
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
|
interface ActorStore {
|
|
userId: string;
|
|
/** Where the identity came from, for the audit line. */
|
|
source: "request" | "asUser" | "service";
|
|
/** The session a request came in on, so core can spare it when revoking. */
|
|
sessionId?: string;
|
|
}
|
|
|
|
const storage = new AsyncLocalStorage<ActorStore>();
|
|
|
|
export function runAsActor<T>(
|
|
userId: string,
|
|
source: ActorStore["source"],
|
|
fn: () => T,
|
|
sessionId?: string,
|
|
): T {
|
|
return storage.run({ userId, source, sessionId }, fn);
|
|
}
|
|
|
|
export function getActorSessionId(): string | undefined {
|
|
return storage.getStore()?.sessionId;
|
|
}
|
|
|
|
export function getActor(): string | undefined {
|
|
return storage.getStore()?.userId;
|
|
}
|