mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-17 22:17:50 +00:00
9922d8e765
* feat(rbac): make stack-scoped grants node-specific Qualify stack role assignments as (nodeId, stackName), migrate legacy rows to the default node, and forward bound multi-action evidence on Proxy/Pilot hops so scoped users keep least-privilege remote access without shipping the full grant table. * fix: mirror scoped-stack-auth-evidence capability to frontend, sanitize node id in role assignment log Backend added the scoped-stack-auth-evidence capability without the matching frontend entry, failing the capability parity test. The role assignment log also interpolated the node id without sanitizeForLog, unlike the rest of the line. * fix(rbac): honor node-wide scopes and fix proxied DELETE cleanup Node-scoped grants now authorize that role's stack actions on the same node in the backend resolver, frontend can(), and remote evidence. Proxied DELETE cleanup uses the gate-stashed route because pathRewrite mutates req.path before proxyRes. Add proxy integration coverage and drop the stale scoped-permissions screenshot. * fix(rbac): preserve node-qualified grants during repair
70 lines
2.6 KiB
TypeScript
70 lines
2.6 KiB
TypeScript
/**
|
|
* HTTP header names used for Distributed License Enforcement between
|
|
* Sencho instances. A primary instance proxies tier-gated requests to
|
|
* its remote fleet nodes and asserts the license state via these
|
|
* headers; the remote node trusts the headers when the request is
|
|
* authenticated as a node_proxy bearer.
|
|
*/
|
|
export const PROXY_TIER_HEADER = 'x-sencho-tier';
|
|
|
|
/**
|
|
* Carries the signed-in user's role from the forwarding primary to the remote
|
|
* node, so the remote enforces that user's RBAC instead of treating every
|
|
* proxied request as admin. Trusted under the same rule as PROXY_TIER_HEADER:
|
|
* only a request authenticated as a node_proxy/pilot_tunnel bearer may set it,
|
|
* and the gateway overwrites it on every proxied request so a browser or API
|
|
* client cannot smuggle a role through.
|
|
*/
|
|
export const PROXY_ROLE_HEADER = 'x-sencho-actor-role';
|
|
|
|
/**
|
|
* Trusted deploy provenance for machine-to-machine / proxied deploys.
|
|
* The gateway always strips client-supplied values and, for interactive
|
|
* proxied requests, overwrites with source=manual and the signed-in username.
|
|
* Background callers (scheduler, fleet, blueprint, mesh) set these only on
|
|
* direct machine-originated HTTP after the strip boundary.
|
|
*/
|
|
export const PROXY_DEPLOY_SOURCE_HEADER = 'x-sencho-deploy-source';
|
|
export const PROXY_DEPLOY_ACTOR_HEADER = 'x-sencho-deploy-actor';
|
|
|
|
/**
|
|
* Bound stack-scoped RBAC evidence for Proxy/Pilot hops. The hub strips any
|
|
* client-supplied values and, when scoped elevation is required, sets the
|
|
* exact stack name plus a comma-separated PermissionAction set conferred by
|
|
* that tuple's hub assignments. Remotes trust these only under node_proxy /
|
|
* pilot_tunnel machine auth.
|
|
*/
|
|
export const PROXY_SCOPED_STACK_NAME_HEADER = 'x-sencho-scoped-stack-name';
|
|
export const PROXY_SCOPED_STACK_ACTIONS_HEADER = 'x-sencho-scoped-stack-actions';
|
|
|
|
export const DEPLOY_SOURCES = [
|
|
'manual',
|
|
'rollback',
|
|
'template',
|
|
'from_git',
|
|
'git_apply',
|
|
'fleet_snapshot',
|
|
'labels',
|
|
'scheduler',
|
|
'webhook',
|
|
'blueprint',
|
|
'mesh_redeploy',
|
|
] as const;
|
|
|
|
export type DeploySourceHeader = (typeof DEPLOY_SOURCES)[number];
|
|
|
|
export function isDeploySourceHeader(value: unknown): value is DeploySourceHeader {
|
|
return typeof value === 'string' && (DEPLOY_SOURCES as readonly string[]).includes(value);
|
|
}
|
|
|
|
/** Headers for direct machine-originated deploy HTTP (never for browser clients). */
|
|
export function deployProvenanceHeaders(
|
|
source: DeploySourceHeader,
|
|
actor: string,
|
|
): Record<string, string> {
|
|
return {
|
|
[PROXY_DEPLOY_SOURCE_HEADER]: source,
|
|
[PROXY_DEPLOY_ACTOR_HEADER]: actor,
|
|
};
|
|
}
|