myaidev-method 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/wordpress-admin.md +271 -0
- package/.env.example +0 -1
- package/PACKAGE_FIXES_SUMMARY.md +319 -0
- package/PAYLOADCMS_AUTH_UPDATE.md +248 -0
- package/USER_GUIDE.md +260 -0
- package/bin/cli.js +70 -0
- package/dist/server/.tsbuildinfo +1 -0
- package/dist/server/auth/controllers/AuthController.d.ts +34 -0
- package/dist/server/auth/controllers/AuthController.d.ts.map +1 -0
- package/dist/server/auth/controllers/AuthController.js +43 -0
- package/dist/server/auth/controllers/AuthController.js.map +1 -0
- package/dist/server/auth/example-usage.d.ts +53 -0
- package/dist/server/auth/example-usage.d.ts.map +1 -0
- package/dist/server/auth/example-usage.js +129 -0
- package/dist/server/auth/example-usage.js.map +1 -0
- package/dist/server/auth/index.d.ts +11 -0
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/auth/index.js +15 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/layers.d.ts +19 -0
- package/dist/server/auth/layers.d.ts.map +1 -0
- package/dist/server/auth/layers.js +33 -0
- package/dist/server/auth/layers.js.map +1 -0
- package/dist/server/auth/middleware/authMiddleware.d.ts +24 -0
- package/dist/server/auth/middleware/authMiddleware.d.ts.map +1 -0
- package/dist/server/auth/middleware/authMiddleware.js +65 -0
- package/dist/server/auth/middleware/authMiddleware.js.map +1 -0
- package/dist/server/auth/routes/authRoutes.d.ts +11 -0
- package/dist/server/auth/routes/authRoutes.d.ts.map +1 -0
- package/dist/server/auth/routes/authRoutes.js +213 -0
- package/dist/server/auth/routes/authRoutes.js.map +1 -0
- package/dist/server/auth/services/AuditLogService.d.ts +21 -0
- package/dist/server/auth/services/AuditLogService.d.ts.map +1 -0
- package/dist/server/auth/services/AuditLogService.js +28 -0
- package/dist/server/auth/services/AuditLogService.js.map +1 -0
- package/dist/server/auth/services/AuthService.d.ts +27 -0
- package/dist/server/auth/services/AuthService.d.ts.map +1 -0
- package/dist/server/auth/services/AuthService.js +246 -0
- package/dist/server/auth/services/AuthService.js.map +1 -0
- package/dist/server/auth/services/PasswordService.d.ts +12 -0
- package/dist/server/auth/services/PasswordService.d.ts.map +1 -0
- package/dist/server/auth/services/PasswordService.js +31 -0
- package/dist/server/auth/services/PasswordService.js.map +1 -0
- package/dist/server/auth/services/SessionRepository.d.ts +24 -0
- package/dist/server/auth/services/SessionRepository.d.ts.map +1 -0
- package/dist/server/auth/services/SessionRepository.js +101 -0
- package/dist/server/auth/services/SessionRepository.js.map +1 -0
- package/dist/server/auth/services/TokenService.d.ts +12 -0
- package/dist/server/auth/services/TokenService.d.ts.map +1 -0
- package/dist/server/auth/services/TokenService.js +86 -0
- package/dist/server/auth/services/TokenService.js.map +1 -0
- package/dist/server/auth/services/UserRepository.d.ts +23 -0
- package/dist/server/auth/services/UserRepository.d.ts.map +1 -0
- package/dist/server/auth/services/UserRepository.js +168 -0
- package/dist/server/auth/services/UserRepository.js.map +1 -0
- package/dist/server/auth/services/example.d.ts +26 -0
- package/dist/server/auth/services/example.d.ts.map +1 -0
- package/dist/server/auth/services/example.js +221 -0
- package/dist/server/auth/services/example.js.map +1 -0
- package/dist/server/auth/services/index.d.ts +6 -0
- package/dist/server/auth/services/index.d.ts.map +1 -0
- package/dist/server/auth/services/index.js +7 -0
- package/dist/server/auth/services/index.js.map +1 -0
- package/dist/server/database/db.d.ts +28 -0
- package/dist/server/database/db.d.ts.map +1 -0
- package/dist/server/database/db.js +91 -0
- package/dist/server/database/db.js.map +1 -0
- package/dist/server/database/schema.sql +95 -0
- package/dist/server/hono/app.d.ts +10 -0
- package/dist/server/hono/app.d.ts.map +1 -0
- package/dist/server/hono/app.js +26 -0
- package/dist/server/hono/app.js.map +1 -0
- package/dist/server/hono/routes.d.ts +12 -0
- package/dist/server/hono/routes.d.ts.map +1 -0
- package/dist/server/hono/routes.js +40 -0
- package/dist/server/hono/routes.js.map +1 -0
- package/dist/server/main.d.ts +2 -0
- package/dist/server/main.d.ts.map +1 -0
- package/dist/server/main.js +94 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/user-management/DirectoryService.d.ts +62 -0
- package/dist/server/user-management/DirectoryService.d.ts.map +1 -0
- package/dist/server/user-management/DirectoryService.js +201 -0
- package/dist/server/user-management/DirectoryService.js.map +1 -0
- package/dist/server/user-management/LinuxUserService.d.ts +71 -0
- package/dist/server/user-management/LinuxUserService.d.ts.map +1 -0
- package/dist/server/user-management/LinuxUserService.js +192 -0
- package/dist/server/user-management/LinuxUserService.js.map +1 -0
- package/dist/server/user-management/QuotaService.d.ts +59 -0
- package/dist/server/user-management/QuotaService.d.ts.map +1 -0
- package/dist/server/user-management/QuotaService.js +148 -0
- package/dist/server/user-management/QuotaService.js.map +1 -0
- package/dist/server/user-management/UserManagementService.d.ts +74 -0
- package/dist/server/user-management/UserManagementService.d.ts.map +1 -0
- package/dist/server/user-management/UserManagementService.js +122 -0
- package/dist/server/user-management/UserManagementService.js.map +1 -0
- package/dist/server/user-management/index.d.ts +26 -0
- package/dist/server/user-management/index.d.ts.map +1 -0
- package/dist/server/user-management/index.js +26 -0
- package/dist/server/user-management/index.js.map +1 -0
- package/dist/server/user-management/layers.d.ts +27 -0
- package/dist/server/user-management/layers.d.ts.map +1 -0
- package/dist/server/user-management/layers.js +37 -0
- package/dist/server/user-management/layers.js.map +1 -0
- package/dist/shared/types.d.ts +94 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +32 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +25 -5
- package/src/lib/payloadcms-utils.js +5 -12
- package/src/server/auth/ARCHITECTURE.md +575 -0
- package/src/server/auth/IMPLEMENTATION_SUMMARY.md +287 -0
- package/src/server/auth/QUICK_START.md +283 -0
- package/src/server/auth/README.md +290 -0
- package/src/server/auth/controllers/AuthController.ts +129 -0
- package/src/server/auth/example-usage.ts +159 -0
- package/src/server/auth/index.ts +19 -0
- package/src/server/auth/layers.ts +57 -0
- package/src/server/auth/middleware/authMiddleware.ts +118 -0
- package/src/server/auth/routes/authRoutes.ts +319 -0
- package/src/server/auth/services/AuditLogService.ts +81 -0
- package/src/server/auth/services/AuthService.ts +408 -0
- package/src/server/auth/services/IMPLEMENTATION_SUMMARY.md +404 -0
- package/src/server/auth/services/PasswordService.ts +85 -0
- package/src/server/auth/services/README.md +361 -0
- package/src/server/auth/services/SessionRepository.ts +227 -0
- package/src/server/auth/services/TokenService.ts +174 -0
- package/src/server/auth/services/UserRepository.ts +318 -0
- package/src/server/auth/services/example.ts +346 -0
- package/src/server/auth/services/index.ts +6 -0
- package/src/server/database/db.ts +161 -0
- package/src/server/database/schema.sql +95 -0
- package/src/server/hono/app.ts +41 -0
- package/src/server/main.ts +115 -0
- package/src/server/user-management/DirectoryService.ts +348 -0
- package/src/server/user-management/LinuxUserService.ts +338 -0
- package/src/server/user-management/QuotaService.ts +256 -0
- package/src/server/user-management/README.md +333 -0
- package/src/server/user-management/UserManagementService.ts +335 -0
- package/src/server/user-management/index.ts +26 -0
- package/src/server/user-management/layers.ts +51 -0
- package/src/shared/types.ts +111 -0
- package/src/templates/claude/agents/coolify-deploy.md +50 -50
- package/src/templates/claude/agents/payloadcms-publish.md +46 -18
- package/src/templates/codex/commands/myai-astro-publish.md +8 -2
- package/src/templates/codex/commands/myai-content-writer.md +8 -2
- package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
- package/src/templates/codex/commands/myai-dev-architect.md +8 -2
- package/src/templates/codex/commands/myai-dev-code.md +8 -2
- package/src/templates/codex/commands/myai-dev-docs.md +8 -2
- package/src/templates/codex/commands/myai-dev-review.md +8 -2
- package/src/templates/codex/commands/myai-dev-test.md +8 -2
- package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
- package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
- package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
- package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
- package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
- package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# User Management Module
|
|
2
|
+
|
|
3
|
+
Linux user management system for multi-user MyAIDev Method deployment with isolated user environments.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module provides comprehensive Linux user management capabilities including:
|
|
8
|
+
|
|
9
|
+
- **User Creation**: Create Linux system users with restricted shells
|
|
10
|
+
- **Directory Management**: Set up isolated home directories with Claude Code configurations
|
|
11
|
+
- **Resource Limits**: Apply CPU, memory, and process limits
|
|
12
|
+
- **Disk Quotas**: Manage storage limits per user
|
|
13
|
+
- **Cleanup**: Safe deletion of users and their resources
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
### Services
|
|
18
|
+
|
|
19
|
+
#### LinuxUserService
|
|
20
|
+
Low-level Linux user operations using system commands (`useradd`, `userdel`, `id`).
|
|
21
|
+
|
|
22
|
+
**Features:**
|
|
23
|
+
- Create/delete Linux users
|
|
24
|
+
- Username sanitization
|
|
25
|
+
- Resource limit enforcement via `/etc/security/limits.d/`
|
|
26
|
+
- User existence checks
|
|
27
|
+
|
|
28
|
+
#### DirectoryService
|
|
29
|
+
Home directory and workspace management.
|
|
30
|
+
|
|
31
|
+
**Features:**
|
|
32
|
+
- `.claude/` configuration directory setup
|
|
33
|
+
- Workspace directory creation
|
|
34
|
+
- Ownership and permission management
|
|
35
|
+
- Shell configuration (`.bashrc`)
|
|
36
|
+
|
|
37
|
+
#### QuotaService
|
|
38
|
+
Disk quota management (optional - requires quota tools).
|
|
39
|
+
|
|
40
|
+
**Features:**
|
|
41
|
+
- Set soft/hard disk limits
|
|
42
|
+
- Monitor usage
|
|
43
|
+
- Fallback to directory size calculation if quotas unavailable
|
|
44
|
+
|
|
45
|
+
#### UserManagementService
|
|
46
|
+
High-level orchestration service integrating all components.
|
|
47
|
+
|
|
48
|
+
**Features:**
|
|
49
|
+
- Complete user environment setup
|
|
50
|
+
- Integrated error handling
|
|
51
|
+
- Automatic username sanitization
|
|
52
|
+
- Graceful degradation (quotas optional)
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Basic User Creation
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { Effect, ManagedRuntime } from "effect";
|
|
60
|
+
import { UserManagementService } from "./user-management";
|
|
61
|
+
import { createUserManagementLayer } from "./user-management/layers";
|
|
62
|
+
|
|
63
|
+
const layer = createUserManagementLayer();
|
|
64
|
+
const runtime = ManagedRuntime.make(layer);
|
|
65
|
+
|
|
66
|
+
const result = await runtime.runPromise(
|
|
67
|
+
Effect.gen(function* () {
|
|
68
|
+
const userMgmt = yield* UserManagementService;
|
|
69
|
+
|
|
70
|
+
return yield* userMgmt.createUser({
|
|
71
|
+
username: "john",
|
|
72
|
+
email: "john@example.com",
|
|
73
|
+
claudeApiKey: "sk-...",
|
|
74
|
+
diskQuotaMB: 2048, // 2GB quota
|
|
75
|
+
shell: "/bin/rbash", // Restricted bash
|
|
76
|
+
});
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
console.log(result);
|
|
81
|
+
// {
|
|
82
|
+
// linuxUser: { username: "john", uid: 1001, gid: 1001, homeDir: "/home/john", ... },
|
|
83
|
+
// homeDir: "/home/john",
|
|
84
|
+
// claudeConfigPath: "/home/john/.claude",
|
|
85
|
+
// workspacePath: "/home/john/workspace",
|
|
86
|
+
// quotaApplied: true,
|
|
87
|
+
// limitsApplied: true
|
|
88
|
+
// }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Check Username Availability
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const available = await runtime.runPromise(
|
|
95
|
+
Effect.gen(function* () {
|
|
96
|
+
const userMgmt = yield* UserManagementService;
|
|
97
|
+
return yield* userMgmt.isUsernameAvailable("john");
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Delete User
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
await runtime.runPromise(
|
|
106
|
+
Effect.gen(function* () {
|
|
107
|
+
const userMgmt = yield* UserManagementService;
|
|
108
|
+
yield* userMgmt.deleteUser("john");
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Integration with Authentication
|
|
114
|
+
|
|
115
|
+
The user management system is automatically integrated with the authentication system in `AuthService`:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// In AuthService.register()
|
|
119
|
+
yield* userManagement.createUser({
|
|
120
|
+
username: linuxUsername,
|
|
121
|
+
email,
|
|
122
|
+
shell: "/bin/rbash",
|
|
123
|
+
diskQuotaMB: 2048,
|
|
124
|
+
}).pipe(
|
|
125
|
+
Effect.catchAll((error) => {
|
|
126
|
+
// Gracefully handle errors - don't fail registration
|
|
127
|
+
console.error(`Failed to create Linux user:`, error);
|
|
128
|
+
return Effect.succeed(void 0);
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
When a user registers:
|
|
134
|
+
1. Database user record created
|
|
135
|
+
2. Linux user created with sanitized username
|
|
136
|
+
3. Home directory structure set up
|
|
137
|
+
4. Claude Code configuration initialized
|
|
138
|
+
5. Resource limits applied
|
|
139
|
+
6. Disk quotas set (if available)
|
|
140
|
+
|
|
141
|
+
## Directory Structure
|
|
142
|
+
|
|
143
|
+
Created for each user:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
/home/{username}/
|
|
147
|
+
├── .claude/ # Claude Code configuration
|
|
148
|
+
│ ├── config.json # API key, model settings
|
|
149
|
+
│ ├── sessions/ # Session storage
|
|
150
|
+
│ └── cache/ # Cache directory
|
|
151
|
+
├── workspace/ # User workspace
|
|
152
|
+
│ └── README.md # Welcome message
|
|
153
|
+
└── .bashrc # Shell configuration
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Security Features
|
|
157
|
+
|
|
158
|
+
### Restricted Shell
|
|
159
|
+
Users are created with `/bin/rbash` (restricted bash) by default:
|
|
160
|
+
- No `cd` command
|
|
161
|
+
- No path modification
|
|
162
|
+
- No redirection
|
|
163
|
+
- No command substitution
|
|
164
|
+
|
|
165
|
+
### Resource Limits
|
|
166
|
+
Per-user limits enforced via `/etc/security/limits.d/{username}.conf`:
|
|
167
|
+
- **Max open files**: 1024 (soft) / 2048 (hard)
|
|
168
|
+
- **Max processes**: 256 (soft) / 512 (hard)
|
|
169
|
+
- **Max memory**: 2GB (soft) / 4GB (hard)
|
|
170
|
+
- **Max CPU time**: 60s (soft) / 120s (hard) per process
|
|
171
|
+
|
|
172
|
+
### Disk Quotas (Optional)
|
|
173
|
+
If quota tools installed:
|
|
174
|
+
- **Soft limit**: 2GB (warning threshold)
|
|
175
|
+
- **Hard limit**: 3GB (absolute maximum)
|
|
176
|
+
|
|
177
|
+
### No Sudo Access
|
|
178
|
+
Created users have no sudo privileges and cannot escalate permissions.
|
|
179
|
+
|
|
180
|
+
## System Requirements
|
|
181
|
+
|
|
182
|
+
### Required
|
|
183
|
+
- Linux operating system (Ubuntu/Debian recommended)
|
|
184
|
+
- Node.js 18+
|
|
185
|
+
- Sudo access for server process
|
|
186
|
+
|
|
187
|
+
### Optional
|
|
188
|
+
- `quota` package for disk quota management
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Install quota tools (optional)
|
|
192
|
+
sudo apt-get install quota
|
|
193
|
+
sudo quotacheck -cum /
|
|
194
|
+
sudo quotaon -v /
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Error Handling
|
|
198
|
+
|
|
199
|
+
All services use Effect-TS for structured error handling:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// LinuxUserError
|
|
203
|
+
{
|
|
204
|
+
_tag: "LinuxUserError",
|
|
205
|
+
message: "Failed to create user",
|
|
206
|
+
cause: Error
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// DirectoryError
|
|
210
|
+
{
|
|
211
|
+
_tag: "DirectoryError",
|
|
212
|
+
message: "Failed to setup home directory",
|
|
213
|
+
cause: Error
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// QuotaError
|
|
217
|
+
{
|
|
218
|
+
_tag: "QuotaError",
|
|
219
|
+
message: "Failed to set quota",
|
|
220
|
+
cause: Error
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// UserManagementError
|
|
224
|
+
{
|
|
225
|
+
_tag: "UserManagementError",
|
|
226
|
+
message: "Failed to create user environment",
|
|
227
|
+
cause: Error
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Username Sanitization
|
|
232
|
+
|
|
233
|
+
Usernames are automatically sanitized to meet Linux requirements:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// Example sanitization
|
|
237
|
+
"John Doe" → "john_doe"
|
|
238
|
+
"user@email.com" → "user_email_com"
|
|
239
|
+
"123user" → "u_123user" // Must start with letter
|
|
240
|
+
"very-long-username-that-exceeds-limit" → "very_long_username_that_exce" // Truncated to 32 chars
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Testing
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Run type checking
|
|
247
|
+
npm run typecheck:server
|
|
248
|
+
|
|
249
|
+
# Build
|
|
250
|
+
npm run build:server
|
|
251
|
+
|
|
252
|
+
# Test user creation (requires sudo)
|
|
253
|
+
node -e "
|
|
254
|
+
import('./dist/server/user-management/index.js').then(async ({ UserManagementService }) => {
|
|
255
|
+
const { Effect, ManagedRuntime } = await import('effect');
|
|
256
|
+
const { createUserManagementLayer } = await import('./dist/server/user-management/layers.js');
|
|
257
|
+
|
|
258
|
+
const layer = createUserManagementLayer();
|
|
259
|
+
const runtime = ManagedRuntime.make(layer);
|
|
260
|
+
|
|
261
|
+
const result = await runtime.runPromise(
|
|
262
|
+
Effect.gen(function* () {
|
|
263
|
+
const userMgmt = yield* UserManagementService;
|
|
264
|
+
return yield* userMgmt.createUser({
|
|
265
|
+
username: 'testuser',
|
|
266
|
+
email: 'test@example.com',
|
|
267
|
+
diskQuotaMB: 1024,
|
|
268
|
+
});
|
|
269
|
+
})
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
console.log('User created:', result);
|
|
273
|
+
});
|
|
274
|
+
"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Production Considerations
|
|
278
|
+
|
|
279
|
+
### Security
|
|
280
|
+
- ✅ Users run with restricted shells
|
|
281
|
+
- ✅ Resource limits prevent DoS
|
|
282
|
+
- ✅ No sudo access
|
|
283
|
+
- ✅ Directory isolation
|
|
284
|
+
- ⚠️ Consider additional AppArmor/SELinux profiles
|
|
285
|
+
|
|
286
|
+
### Performance
|
|
287
|
+
- User creation takes 1-3 seconds
|
|
288
|
+
- Quota checks are fast if using quota tools
|
|
289
|
+
- Directory size calculation (fallback) can be slow for large directories
|
|
290
|
+
|
|
291
|
+
### Monitoring
|
|
292
|
+
- Check `/etc/security/limits.d/` for limit configurations
|
|
293
|
+
- Monitor disk usage with `quota` command
|
|
294
|
+
- Audit log records user creation in database
|
|
295
|
+
|
|
296
|
+
### Cleanup
|
|
297
|
+
When deleting users:
|
|
298
|
+
1. Home directory removed (`rm -rf /home/{username}`)
|
|
299
|
+
2. User deleted from system (`userdel`)
|
|
300
|
+
3. Limits configuration removed
|
|
301
|
+
4. Database records remain for audit trail
|
|
302
|
+
|
|
303
|
+
## Troubleshooting
|
|
304
|
+
|
|
305
|
+
### "Failed to create Linux user"
|
|
306
|
+
- Ensure server has sudo access
|
|
307
|
+
- Check if username already exists: `id username`
|
|
308
|
+
- Verify useradd command available
|
|
309
|
+
|
|
310
|
+
### "Quota system not available"
|
|
311
|
+
- Install quota tools: `sudo apt-get install quota`
|
|
312
|
+
- Or system will fallback to directory size monitoring
|
|
313
|
+
|
|
314
|
+
### "Permission denied" errors
|
|
315
|
+
- Server process needs sudo for user management
|
|
316
|
+
- Consider running with sudo or configuring sudoers file
|
|
317
|
+
|
|
318
|
+
### "Directory already exists"
|
|
319
|
+
- Previous user not fully cleaned up
|
|
320
|
+
- Manually remove: `sudo rm -rf /home/username`
|
|
321
|
+
|
|
322
|
+
## Future Enhancements
|
|
323
|
+
|
|
324
|
+
- [ ] User suspension/reactivation
|
|
325
|
+
- [ ] Disk usage monitoring dashboard
|
|
326
|
+
- [ ] Automated cleanup of inactive users
|
|
327
|
+
- [ ] User groups for shared resources
|
|
328
|
+
- [ ] Network namespace isolation
|
|
329
|
+
- [ ] Container-based isolation option
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
Same as MyAIDev Method project.
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { Effect, Context, Layer } from "effect";
|
|
2
|
+
import { LinuxUserService, LinuxUser } from "./LinuxUserService";
|
|
3
|
+
import { DirectoryService, ClaudeConfig } from "./DirectoryService";
|
|
4
|
+
import { QuotaService, DiskQuota } from "./QuotaService";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* User Management Service
|
|
8
|
+
*
|
|
9
|
+
* High-level orchestration service that coordinates Linux user creation,
|
|
10
|
+
* directory setup, and resource management for multi-user environments.
|
|
11
|
+
*
|
|
12
|
+
* This service integrates:
|
|
13
|
+
* - LinuxUserService: System user creation and management
|
|
14
|
+
* - DirectoryService: Home directory and Claude config setup
|
|
15
|
+
* - QuotaService: Disk quotas and resource limits
|
|
16
|
+
*
|
|
17
|
+
* Workflow:
|
|
18
|
+
* 1. Sanitize and validate username
|
|
19
|
+
* 2. Create Linux system user
|
|
20
|
+
* 3. Set up home directory structure
|
|
21
|
+
* 4. Configure Claude Code environment
|
|
22
|
+
* 5. Apply resource limits and quotas
|
|
23
|
+
* 6. Return complete user setup information
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export interface CreateUserRequest {
|
|
27
|
+
username: string;
|
|
28
|
+
email: string;
|
|
29
|
+
claudeApiKey?: string;
|
|
30
|
+
diskQuotaMB?: number;
|
|
31
|
+
shell?: "/bin/rbash" | "/usr/sbin/nologin" | "/bin/bash";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface UserSetupResult {
|
|
35
|
+
linuxUser: LinuxUser;
|
|
36
|
+
homeDir: string;
|
|
37
|
+
claudeConfigPath: string;
|
|
38
|
+
workspacePath: string;
|
|
39
|
+
quotaApplied: boolean;
|
|
40
|
+
limitsApplied: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface UserManagementError {
|
|
44
|
+
readonly _tag: "UserManagementError";
|
|
45
|
+
readonly message: string;
|
|
46
|
+
readonly cause?: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const UserManagementError = (
|
|
50
|
+
message: string,
|
|
51
|
+
cause?: unknown
|
|
52
|
+
): UserManagementError => ({
|
|
53
|
+
_tag: "UserManagementError",
|
|
54
|
+
message,
|
|
55
|
+
cause,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export class UserManagementService extends Context.Tag("UserManagementService")<
|
|
59
|
+
UserManagementService,
|
|
60
|
+
{
|
|
61
|
+
/**
|
|
62
|
+
* Create a complete user environment
|
|
63
|
+
* - Creates Linux user
|
|
64
|
+
* - Sets up home directory
|
|
65
|
+
* - Configures Claude Code
|
|
66
|
+
* - Applies quotas and limits
|
|
67
|
+
*/
|
|
68
|
+
readonly createUser: (
|
|
69
|
+
request: CreateUserRequest
|
|
70
|
+
) => Effect.Effect<UserSetupResult, UserManagementError>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete user and clean up all resources
|
|
74
|
+
*/
|
|
75
|
+
readonly deleteUser: (
|
|
76
|
+
username: string
|
|
77
|
+
) => Effect.Effect<void, UserManagementError>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get user information
|
|
81
|
+
*/
|
|
82
|
+
readonly getUserInfo: (
|
|
83
|
+
username: string
|
|
84
|
+
) => Effect.Effect<LinuxUser, UserManagementError>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Update user quotas
|
|
88
|
+
*/
|
|
89
|
+
readonly updateQuota: (
|
|
90
|
+
username: string,
|
|
91
|
+
quota: DiskQuota
|
|
92
|
+
) => Effect.Effect<void, UserManagementError>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if username is available
|
|
96
|
+
*/
|
|
97
|
+
readonly isUsernameAvailable: (
|
|
98
|
+
username: string
|
|
99
|
+
) => Effect.Effect<boolean, UserManagementError>;
|
|
100
|
+
}
|
|
101
|
+
>() {
|
|
102
|
+
static Live = Layer.effect(
|
|
103
|
+
this,
|
|
104
|
+
Effect.gen(function* () {
|
|
105
|
+
const linuxUserService = yield* LinuxUserService;
|
|
106
|
+
const directoryService = yield* DirectoryService;
|
|
107
|
+
const quotaService = yield* QuotaService;
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
createUser: (request: CreateUserRequest) =>
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
const {
|
|
113
|
+
username,
|
|
114
|
+
claudeApiKey,
|
|
115
|
+
diskQuotaMB = 2048,
|
|
116
|
+
shell = "/bin/rbash",
|
|
117
|
+
} = request;
|
|
118
|
+
|
|
119
|
+
// Step 1: Sanitize username for Linux compatibility
|
|
120
|
+
const linuxUsername = yield* linuxUserService
|
|
121
|
+
.sanitizeUsername(username)
|
|
122
|
+
.pipe(
|
|
123
|
+
Effect.mapError((err) =>
|
|
124
|
+
UserManagementError(
|
|
125
|
+
`Failed to sanitize username: ${err.message}`,
|
|
126
|
+
err
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Step 2: Check if user already exists
|
|
132
|
+
const exists = yield* linuxUserService.userExists(linuxUsername).pipe(
|
|
133
|
+
Effect.mapError((err) =>
|
|
134
|
+
UserManagementError(
|
|
135
|
+
`Failed to check user existence: ${err.message}`,
|
|
136
|
+
err
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (exists) {
|
|
142
|
+
return yield* Effect.fail(
|
|
143
|
+
UserManagementError(
|
|
144
|
+
`Linux user ${linuxUsername} already exists`
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Step 3: Create Linux user
|
|
150
|
+
const linuxUser = yield* linuxUserService
|
|
151
|
+
.createUser({
|
|
152
|
+
username: linuxUsername,
|
|
153
|
+
shell,
|
|
154
|
+
createHome: true,
|
|
155
|
+
groups: ["myaidev"], // Optional: create this group for MyAIDev users
|
|
156
|
+
})
|
|
157
|
+
.pipe(
|
|
158
|
+
Effect.mapError((err) =>
|
|
159
|
+
UserManagementError(
|
|
160
|
+
`Failed to create Linux user: ${err.message}`,
|
|
161
|
+
err
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Step 4: Set up home directory structure
|
|
167
|
+
const claudeConfig: ClaudeConfig = {
|
|
168
|
+
apiKey: claudeApiKey || "",
|
|
169
|
+
model: "claude-sonnet-4-20250514",
|
|
170
|
+
maxTokens: 8192,
|
|
171
|
+
temperature: 1.0,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
yield* directoryService
|
|
175
|
+
.setupHomeDirectory({
|
|
176
|
+
username: linuxUsername,
|
|
177
|
+
homeDir: linuxUser.homeDir,
|
|
178
|
+
uid: linuxUser.uid,
|
|
179
|
+
gid: linuxUser.gid,
|
|
180
|
+
claudeConfig,
|
|
181
|
+
})
|
|
182
|
+
.pipe(
|
|
183
|
+
Effect.mapError((err) =>
|
|
184
|
+
UserManagementError(
|
|
185
|
+
`Failed to setup home directory: ${err.message}`,
|
|
186
|
+
err
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// Step 5: Apply resource limits
|
|
192
|
+
yield* linuxUserService
|
|
193
|
+
.setResourceLimits(linuxUsername, {
|
|
194
|
+
maxOpenFiles: 1024,
|
|
195
|
+
maxProcesses: 256,
|
|
196
|
+
maxMemoryKB: 2097152, // 2GB
|
|
197
|
+
maxCPUTime: 120, // 2 minutes per process
|
|
198
|
+
})
|
|
199
|
+
.pipe(
|
|
200
|
+
Effect.mapError((err) =>
|
|
201
|
+
UserManagementError(
|
|
202
|
+
`Failed to set resource limits: ${err.message}`,
|
|
203
|
+
err
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Step 6: Try to apply disk quotas (optional - may not be available)
|
|
209
|
+
const quotaAvailable = yield* quotaService
|
|
210
|
+
.isQuotaAvailable()
|
|
211
|
+
.pipe(Effect.orElseSucceed(() => false));
|
|
212
|
+
|
|
213
|
+
let quotaApplied = false;
|
|
214
|
+
if (quotaAvailable) {
|
|
215
|
+
yield* quotaService
|
|
216
|
+
.setDiskQuota(linuxUsername, {
|
|
217
|
+
softLimitMB: diskQuotaMB,
|
|
218
|
+
hardLimitMB: diskQuotaMB * 1.5, // 50% buffer
|
|
219
|
+
})
|
|
220
|
+
.pipe(
|
|
221
|
+
Effect.catchAll(() => Effect.succeed(void 0)) // Don't fail if quota setup fails
|
|
222
|
+
);
|
|
223
|
+
quotaApplied = true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Return complete setup information
|
|
227
|
+
return {
|
|
228
|
+
linuxUser,
|
|
229
|
+
homeDir: linuxUser.homeDir,
|
|
230
|
+
claudeConfigPath: `${linuxUser.homeDir}/.claude`,
|
|
231
|
+
workspacePath: `${linuxUser.homeDir}/workspace`,
|
|
232
|
+
quotaApplied,
|
|
233
|
+
limitsApplied: true,
|
|
234
|
+
};
|
|
235
|
+
}),
|
|
236
|
+
|
|
237
|
+
deleteUser: (username: string) =>
|
|
238
|
+
Effect.gen(function* () {
|
|
239
|
+
// Get user info first
|
|
240
|
+
const userInfo = yield* linuxUserService.getUserInfo(username).pipe(
|
|
241
|
+
Effect.mapError((err) =>
|
|
242
|
+
UserManagementError(
|
|
243
|
+
`Failed to get user info: ${err.message}`,
|
|
244
|
+
err
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// Clean up directories
|
|
250
|
+
yield* directoryService
|
|
251
|
+
.cleanupDirectories(userInfo.homeDir)
|
|
252
|
+
.pipe(
|
|
253
|
+
Effect.mapError((err) =>
|
|
254
|
+
UserManagementError(
|
|
255
|
+
`Failed to cleanup directories: ${err.message}`,
|
|
256
|
+
err
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Delete Linux user
|
|
262
|
+
yield* linuxUserService.deleteUser(username, true).pipe(
|
|
263
|
+
Effect.mapError((err) =>
|
|
264
|
+
UserManagementError(
|
|
265
|
+
`Failed to delete user: ${err.message}`,
|
|
266
|
+
err
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Remove resource limits file
|
|
272
|
+
yield* Effect.tryPromise({
|
|
273
|
+
try: async () => {
|
|
274
|
+
const { execSync } = await import("child_process");
|
|
275
|
+
execSync(`sudo rm -f /etc/security/limits.d/${username}.conf`, {
|
|
276
|
+
stdio: "pipe",
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
catch: () =>
|
|
280
|
+
UserManagementError("Failed to remove limits configuration"),
|
|
281
|
+
});
|
|
282
|
+
}),
|
|
283
|
+
|
|
284
|
+
getUserInfo: (username: string) =>
|
|
285
|
+
linuxUserService.getUserInfo(username).pipe(
|
|
286
|
+
Effect.mapError((err) =>
|
|
287
|
+
UserManagementError(
|
|
288
|
+
`Failed to get user info: ${err.message}`,
|
|
289
|
+
err
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
),
|
|
293
|
+
|
|
294
|
+
updateQuota: (username: string, quota: DiskQuota) =>
|
|
295
|
+
quotaService.setDiskQuota(username, quota).pipe(
|
|
296
|
+
Effect.mapError((err) =>
|
|
297
|
+
UserManagementError(
|
|
298
|
+
`Failed to update quota: ${err.message}`,
|
|
299
|
+
err
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
),
|
|
303
|
+
|
|
304
|
+
isUsernameAvailable: (username: string) =>
|
|
305
|
+
Effect.gen(function* () {
|
|
306
|
+
// Sanitize username first
|
|
307
|
+
const linuxUsername = yield* linuxUserService
|
|
308
|
+
.sanitizeUsername(username)
|
|
309
|
+
.pipe(
|
|
310
|
+
Effect.mapError((err) =>
|
|
311
|
+
UserManagementError(
|
|
312
|
+
`Failed to sanitize username: ${err.message}`,
|
|
313
|
+
err
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// Check if exists
|
|
319
|
+
const exists = yield* linuxUserService
|
|
320
|
+
.userExists(linuxUsername)
|
|
321
|
+
.pipe(
|
|
322
|
+
Effect.mapError((err) =>
|
|
323
|
+
UserManagementError(
|
|
324
|
+
`Failed to check availability: ${err.message}`,
|
|
325
|
+
err
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return !exists;
|
|
331
|
+
}),
|
|
332
|
+
};
|
|
333
|
+
})
|
|
334
|
+
);
|
|
335
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Management Module
|
|
3
|
+
*
|
|
4
|
+
* Provides Linux user management capabilities for multi-user MyAIDev Method deployment.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Create and delete Linux system users
|
|
8
|
+
* - Set up isolated home directories
|
|
9
|
+
* - Configure Claude Code environments
|
|
10
|
+
* - Apply disk quotas and resource limits
|
|
11
|
+
* - Clean up user resources
|
|
12
|
+
*
|
|
13
|
+
* Services:
|
|
14
|
+
* - LinuxUserService: Low-level Linux user operations
|
|
15
|
+
* - DirectoryService: Home directory and config management
|
|
16
|
+
* - QuotaService: Disk quota and storage limits
|
|
17
|
+
* - UserManagementService: High-level orchestration
|
|
18
|
+
*
|
|
19
|
+
* @module user-management
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export * from "./LinuxUserService";
|
|
23
|
+
export * from "./DirectoryService";
|
|
24
|
+
export * from "./QuotaService";
|
|
25
|
+
export * from "./UserManagementService";
|
|
26
|
+
export * from "./layers";
|