mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 23:19:43 +02:00
feat(multiuser): phase 3, ownership threading + scoping
Threads per-user ownership through sessions, cases, cron, and the permission policy. All scoping is a no-op in single-user mode (isMultiUserMode() guards). Sessions - Session.owner stamped at every create path from req.authUser / job.owner: POST /api/sessions, /api/run, /api/quick-start, ralph start, cron launch, plan generation. Round-trips through recovery (MuxSession.owner mirror, read muxSession.owner ?? savedState?.owner) and the mux layer. - findSessionOrFail(ctx, id, req) now does a NOT_FOUND owner check (never 403, so other users' session existence is not leaked); wired at ~50 call sites. - List endpoints filtered by owner: GET /api/sessions, /api/sessions/unified (live+persisted+lifecycle scoped, host-wide transcripts admin-only), cron jobs. Permission policy (section 6.3) - resolveClaudeModeForUsername wraps getClaudeModeConfig at every spawn site so a non-granted user is forced to --permission-mode auto (bypass -> auto), including recovery (or a reboot would un-downgrade). buildPromptArgs now respects the session's claudeMode, closing the one-shot (runPrompt) bypass hole. - Shell mode and cron launchCommand require canBypassPermissions: 403 at POST /api/sessions, /api/quick-start create, cron job create, AND cron fire time (re-checked against the owner's current grant). Cases - resolveCasesDir(user): per-user ~/codeman-users/<name>/cases in multi-user, the shared ~/codeman-cases otherwise. All case CRUD + ralph + plan + quick-start resolve through it. resolveCasePath is owner-aware. - GET /api/cases scoped per user (own folders; legacy linked cases admin-only; remote/docker cases owner-filtered). RemoteCase/DockerCase gain owner, stamped at link/quickcreate/import. - Remote + Docker host CRUD is admin-only. - Non-admin workingDir confinement (the linchpin): realpath must resolve inside the user's space, enforced at POST /api/sessions and /api/run BEFORE any disk write. Limits - sessionCapacityState / sessionCapacityMessage centralize the global + per-user cap (CODEMAN_MAX_SESSIONS_PER_USER, default global/2), replacing the 6 copy-pasted MAX_CONCURRENT_SESSIONS checks. Tests: test/ownership-scoping.test.ts (case isolation, host-CRUD gate, workingDir + shell gates, and the scoping helpers). Deferred to phase 4: WS owner gate, SSE fan-out filtering, file-route preview/thumbnail helper scoping, push routing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -1413,6 +1413,7 @@ export class Session extends EventEmitter {
|
||||
historyLimit: this._tmuxHistoryLimit,
|
||||
remote: this._remote,
|
||||
docker: this._docker,
|
||||
owner: this._owner,
|
||||
},
|
||||
createSessionOptions: {
|
||||
sessionId: this.id,
|
||||
@@ -1432,6 +1433,7 @@ export class Session extends EventEmitter {
|
||||
historyLimit: this._tmuxHistoryLimit,
|
||||
remote: this._remote,
|
||||
docker: this._docker,
|
||||
owner: this._owner,
|
||||
},
|
||||
spawnErrLabel: 'mux attachment',
|
||||
});
|
||||
@@ -1803,6 +1805,7 @@ export class Session extends EventEmitter {
|
||||
historyLimit: this._tmuxHistoryLimit,
|
||||
remote: this._remote,
|
||||
docker: this._docker,
|
||||
owner: this._owner,
|
||||
},
|
||||
createSessionOptions: {
|
||||
sessionId: this.id,
|
||||
@@ -1814,6 +1817,7 @@ export class Session extends EventEmitter {
|
||||
historyLimit: this._tmuxHistoryLimit,
|
||||
remote: this._remote,
|
||||
docker: this._docker,
|
||||
owner: this._owner,
|
||||
},
|
||||
spawnErrLabel: 'shell mux attachment',
|
||||
});
|
||||
@@ -1941,7 +1945,7 @@ export class Session extends EventEmitter {
|
||||
model ? `(model: ${model})` : ''
|
||||
);
|
||||
|
||||
const args = buildPromptArgs(prompt, model);
|
||||
const args = buildPromptArgs(prompt, model, this._claudeMode, this._allowedTools);
|
||||
|
||||
try {
|
||||
this.ptyProcess = pty.spawn('claude', args, {
|
||||
|
||||
Reference in New Issue
Block a user