mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-03 14:09:42 +02:00
Opt-in multi-user foundation (off by default; no behavior change without CODEMAN_MULTIUSER/--multiuser): - src/config/multiuser.ts: isMultiUserMode(), getUserSpacesDir()/userCasesDir(), maxUsers(), maxSessionsPerUser() (per-user fairness cap = global/2). - src/types/user.ts: UserRecord/PasswordHash/AuthUser/PublicUser/UserRole. - src/user-store.ts: ~/.codeman/users.json (atomic tmp+rename, mode 0600, short TTL cache). scrypt hashing with per-record params + timingSafeEqual verify plus rehash detection; createUser/setPassword/updateUser/deleteUser with last-admin invariants; guarded deleteUserSpace (symlink + realpath confinement, section 8); pure section-6.3 resolvers (resolveClaudeModeForUser downgrades bypass to auto for non-granted users; canRunPrivilegedCommands); bootstrapInitialAdmin. - src/cli.ts: "codeman users add|passwd|list|rm" (hidden prompt or --password-stdin) operating directly on users.json; a --multiuser flag on the web command. Tests: test/user-store.test.ts (29 tests: hashing/verify/rehash, username validation, atomic 0600 write, last-admin invariants, 6.3 resolvers, delete-space guards, bootstrap). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
/**
|
|
* @fileoverview Multi-user mode types (opt-in `--multiuser`).
|
|
*
|
|
* Users live in `~/.codeman/users.json` (via `dataPath`, mode 0600). Each record
|
|
* carries a scrypt password hash with its own parameters so hashing cost can be
|
|
* raised later and old records rehashed on next login. `AuthUser` is the
|
|
* request-scoped identity decorated onto Fastify requests; in SINGLE-user mode a
|
|
* synthetic `{ username: 'admin', role: 'admin' }` is used so downstream code has
|
|
* one code path. See `src/user-store.ts` and `docs/multi-user-plan.md`.
|
|
*/
|
|
|
|
export type UserRole = 'admin' | 'user';
|
|
|
|
/** Per-record scrypt parameters + salt/hash (all hex). */
|
|
export interface PasswordHash {
|
|
algo: 'scrypt';
|
|
N: number;
|
|
r: number;
|
|
p: number;
|
|
salt: string;
|
|
hash: string;
|
|
}
|
|
|
|
export interface UserRecord {
|
|
/** Canonical lowercase slug; also the user's folder name under USER_SPACES_DIR. */
|
|
username: string;
|
|
role: UserRole;
|
|
password: PasswordHash;
|
|
/** Disabled accounts fail auth closed but keep their space on disk. */
|
|
disabled?: boolean;
|
|
/** Set by an admin reset; gates all API access until the user changes it. */
|
|
mustChangePassword?: boolean;
|
|
/**
|
|
* Permission-mode grant (section 6.3). When false (the default for new users),
|
|
* the user's Claude sessions are forced to `--permission-mode auto`, shell mode
|
|
* and cron `launchCommand` are refused, and other CLIs' bypass flags are dropped.
|
|
*/
|
|
canBypassPermissions?: boolean;
|
|
createdAt: number;
|
|
lastLoginAt?: number;
|
|
}
|
|
|
|
/** On-disk shape of `users.json`. */
|
|
export interface UsersFile {
|
|
version: 1;
|
|
users: UserRecord[];
|
|
}
|
|
|
|
/** Request-scoped identity (decorated as `req.authUser`). */
|
|
export interface AuthUser {
|
|
username: string;
|
|
role: UserRole;
|
|
}
|
|
|
|
/** Admin-facing projection of a user: never carries the password hash. */
|
|
export interface PublicUser {
|
|
username: string;
|
|
role: UserRole;
|
|
disabled: boolean;
|
|
mustChangePassword: boolean;
|
|
canBypassPermissions: boolean;
|
|
createdAt: number;
|
|
lastLoginAt?: number;
|
|
}
|