-
Notifications
You must be signed in to change notification settings - Fork 120
Expand file tree
/
Copy pathensure-user.ts
More file actions
104 lines (90 loc) · 3.4 KB
/
Copy pathensure-user.ts
File metadata and controls
104 lines (90 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
* Admin Command: Ensure Unix User Exists
*
* PRIVILEGED OPERATION - Must be called via sudo
*
* Creates a Unix user if it doesn't exist, with home directory and ~/agor/worktrees setup.
* This command is designed to be called by the daemon via `sudo agor admin ensure-user`.
*
* @see context/guides/rbac-and-unix-isolation.md
*/
import {
AGOR_HOME_BASE,
createAdminExecutor,
isValidUnixUsername,
UnixUserCommands,
} from '@agor/core/unix';
import { Command, Flags } from '@oclif/core';
export default class EnsureUser extends Command {
static override description = 'Ensure a Unix user exists with proper Agor setup (admin only)';
static override examples = [
'<%= config.bin %> <%= command.id %> --username agor_03b62447',
'<%= config.bin %> <%= command.id %> --username alice --home-base /home',
'<%= config.bin %> <%= command.id %> --username alice --dry-run',
];
static override flags = {
username: Flags.string({
char: 'u',
description: 'Unix username to create/ensure',
required: true,
}),
'home-base': Flags.string({
description: 'Base directory for home directories',
default: AGOR_HOME_BASE,
}),
'dry-run': Flags.boolean({
char: 'n',
description: 'Show what would be done without making changes',
default: false,
}),
verbose: Flags.boolean({
char: 'v',
description: 'Show detailed output including command stdout/stderr',
default: false,
}),
};
public async run(): Promise<void> {
const { flags } = await this.parse(EnsureUser);
const { username, verbose } = flags;
const homeBase = flags['home-base'];
const dryRun = flags['dry-run'];
// Create executor with dry-run and verbose support
const executor = createAdminExecutor({ 'dry-run': dryRun, verbose });
if (dryRun) {
this.log('🔍 Dry run mode - no changes will be made\n');
}
// Validate username format
if (!isValidUnixUsername(username)) {
this.error(`Invalid Unix username format: ${username}`);
}
// Check if user already exists
const userExists = await executor.check(UnixUserCommands.userExists(username));
if (userExists) {
this.log(`✅ Unix user ${username} already exists`);
// Ensure ~/agor/worktrees directory exists
try {
await executor.execAll(UnixUserCommands.setupWorktreesDir(username, homeBase));
this.log(`✅ Ensured ~/agor/worktrees directory for ${username}`);
} catch (error) {
this.warn(`Failed to setup worktrees directory: ${error}`);
}
return;
}
// Create the user
try {
this.log(`Creating Unix user: ${username}`);
// Only pass --home when the user explicitly overrides the default
// home base; otherwise let useradd use the system default. The
// wrapper validates the supplied path (see docker/sudoers/agor-user-admin).
const homeDir =
homeBase && homeBase !== AGOR_HOME_BASE ? `${homeBase}/${username}` : undefined;
await executor.exec(UnixUserCommands.createUser(username, homeDir));
this.log(`✅ Created Unix user: ${username}`);
// Setup ~/agor/worktrees directory
await executor.execAll(UnixUserCommands.setupWorktreesDir(username, homeBase));
this.log(`✅ Created ~/agor/worktrees directory for ${username}`);
} catch (error) {
this.error(`Failed to create user ${username}: ${error}`);
}
}
}