sync: update from private repo (a360d15)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-06-09 09:19:09 +00:00
parent 44df3a7da1
commit 483464597a
11 changed files with 328 additions and 16 deletions
+28 -1
View File
@@ -25,7 +25,7 @@
* the pending host-key token (token only returned on test / observation).
*/
import { Router, type Request, type Response, type RequestHandler } from 'express';
import { Router, type Request, type Response, type NextFunction, type RequestHandler } from 'express';
import type Database from 'better-sqlite3';
import type { SshConnection, SshConnectionRepo, HostKeyVerifyResult } from '../ssh/connection-repo.js';
@@ -78,6 +78,14 @@ export interface SshApiDeps {
getUserId(req: Request): string | null;
isAdmin(req: Request): boolean;
getOrgIds(req: Request): string[];
/**
* Whether the bridge wired the auth subsystem. When `false` (no-auth
* single-user deployment) requests carry no `req.user`, so the router
* synthesizes a stable `local` owner — otherwise every endpoint 401s on
* the null userId and SSH is unusable. Mirrors notes-api / memory-api /
* user-folder-api. Defaults to `true` for existing (auth-only) callers.
*/
authActive?: boolean;
connectionRepo: SshConnectionRepo;
grantsRepo: SshGrantsRepo;
@@ -279,9 +287,27 @@ function jsonError(res: Response, status: number, error: string, detail?: unknow
// User router — /api/ssh/connections + /api/ssh/grants
// ──────────────────────────────────────────────────────────────────────
/**
* No-auth single-user mode: synthesize a `local` user so the per-user
* connection store has a stable owner and `getUserId`/`isAdmin` resolve.
* Role `admin` keeps the connection-management surface fully usable for the
* lone local operator (in single-user mode the admin/grant checks only ever
* compare against `local`-owned rows, so this never widens cross-user access).
* No-op when auth is active (Passport has already populated `req.user`).
*/
function makeSshLocalUserMiddleware(authActive: boolean): RequestHandler {
return (req: Request, _res: Response, next: NextFunction) => {
if (!authActive && !(req as { user?: unknown }).user) {
(req as { user?: unknown }).user = { id: 'local', role: 'admin', orgIds: [] };
}
next();
};
}
export function createSshUserRouter(deps: SshApiDeps): Router {
const router = Router();
const testTimeoutMs = deps.connectionTestTimeoutMs ?? 5000;
router.use(makeSshLocalUserMiddleware(deps.authActive ?? true));
// GET /api/ssh/connections — list owned (and globals visible via grant).
// For Phase 5 we return:
@@ -801,6 +827,7 @@ export function createSshUserRouter(deps: SshApiDeps): Router {
export function createSshAdminRouter(deps: SshApiDeps): Router {
const router = Router();
router.use(makeSshLocalUserMiddleware(deps.authActive ?? true));
// GET /api/ssh/admin/connections
router.get('/connections', deps.requireAdmin, (_req, res) => {