@zincapp/znvault-cli 2.18.1 → 2.19.1

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.
Files changed (132) hide show
  1. package/dist/commands/dynamic-secrets/connection.d.ts +17 -0
  2. package/dist/commands/dynamic-secrets/connection.d.ts.map +1 -0
  3. package/dist/commands/dynamic-secrets/connection.js +217 -0
  4. package/dist/commands/dynamic-secrets/connection.js.map +1 -0
  5. package/dist/commands/dynamic-secrets/creds.d.ts +5 -0
  6. package/dist/commands/dynamic-secrets/creds.d.ts.map +1 -0
  7. package/dist/commands/dynamic-secrets/creds.js +39 -0
  8. package/dist/commands/dynamic-secrets/creds.js.map +1 -0
  9. package/dist/commands/dynamic-secrets/helpers.d.ts +5 -0
  10. package/dist/commands/dynamic-secrets/helpers.d.ts.map +1 -0
  11. package/dist/commands/dynamic-secrets/helpers.js +36 -0
  12. package/dist/commands/dynamic-secrets/helpers.js.map +1 -0
  13. package/dist/commands/dynamic-secrets/index.d.ts +7 -0
  14. package/dist/commands/dynamic-secrets/index.d.ts.map +1 -0
  15. package/dist/commands/dynamic-secrets/index.js +173 -0
  16. package/dist/commands/dynamic-secrets/index.js.map +1 -0
  17. package/dist/commands/dynamic-secrets/lease.d.ts +11 -0
  18. package/dist/commands/dynamic-secrets/lease.d.ts.map +1 -0
  19. package/dist/commands/dynamic-secrets/lease.js +137 -0
  20. package/dist/commands/dynamic-secrets/lease.js.map +1 -0
  21. package/dist/commands/dynamic-secrets/role.d.ts +15 -0
  22. package/dist/commands/dynamic-secrets/role.d.ts.map +1 -0
  23. package/dist/commands/dynamic-secrets/role.js +184 -0
  24. package/dist/commands/dynamic-secrets/role.js.map +1 -0
  25. package/dist/commands/dynamic-secrets/types.d.ts +125 -0
  26. package/dist/commands/dynamic-secrets/types.d.ts.map +1 -0
  27. package/dist/commands/dynamic-secrets/types.js +3 -0
  28. package/dist/commands/dynamic-secrets/types.js.map +1 -0
  29. package/dist/commands/dynamic-secrets.d.ts +6 -2
  30. package/dist/commands/dynamic-secrets.d.ts.map +1 -1
  31. package/dist/commands/dynamic-secrets.js +6 -754
  32. package/dist/commands/dynamic-secrets.js.map +1 -1
  33. package/dist/commands/policy/attachments.d.ts +9 -0
  34. package/dist/commands/policy/attachments.d.ts.map +1 -0
  35. package/dist/commands/policy/attachments.js +161 -0
  36. package/dist/commands/policy/attachments.js.map +1 -0
  37. package/dist/commands/policy/crud.d.ts +8 -0
  38. package/dist/commands/policy/crud.d.ts.map +1 -0
  39. package/dist/commands/policy/crud.js +232 -0
  40. package/dist/commands/policy/crud.js.map +1 -0
  41. package/dist/commands/policy/helpers.d.ts +13 -0
  42. package/dist/commands/policy/helpers.d.ts.map +1 -0
  43. package/dist/commands/policy/helpers.js +61 -0
  44. package/dist/commands/policy/helpers.js.map +1 -0
  45. package/dist/commands/policy/index.d.ts +7 -0
  46. package/dist/commands/policy/index.d.ts.map +1 -0
  47. package/dist/commands/policy/index.js +160 -0
  48. package/dist/commands/policy/index.js.map +1 -0
  49. package/dist/commands/policy/io.d.ts +4 -0
  50. package/dist/commands/policy/io.d.ts.map +1 -0
  51. package/dist/commands/policy/io.js +65 -0
  52. package/dist/commands/policy/io.js.map +1 -0
  53. package/dist/commands/policy/list.d.ts +4 -0
  54. package/dist/commands/policy/list.d.ts.map +1 -0
  55. package/dist/commands/policy/list.js +99 -0
  56. package/dist/commands/policy/list.js.map +1 -0
  57. package/dist/commands/policy/test.d.ts +3 -0
  58. package/dist/commands/policy/test.d.ts.map +1 -0
  59. package/dist/commands/policy/test.js +58 -0
  60. package/dist/commands/policy/test.js.map +1 -0
  61. package/dist/commands/policy/types.d.ts +84 -0
  62. package/dist/commands/policy/types.d.ts.map +1 -0
  63. package/dist/commands/policy/types.js +3 -0
  64. package/dist/commands/policy/types.js.map +1 -0
  65. package/dist/commands/policy.d.ts +6 -2
  66. package/dist/commands/policy.d.ts.map +1 -1
  67. package/dist/commands/policy.js +4 -770
  68. package/dist/commands/policy.js.map +1 -1
  69. package/dist/commands/secret/index.d.ts.map +1 -1
  70. package/dist/commands/secret/index.js +2 -0
  71. package/dist/commands/secret/index.js.map +1 -1
  72. package/dist/commands/secret/patch/diff.d.ts +14 -0
  73. package/dist/commands/secret/patch/diff.d.ts.map +1 -0
  74. package/dist/commands/secret/patch/diff.js +181 -0
  75. package/dist/commands/secret/patch/diff.js.map +1 -0
  76. package/dist/commands/secret/patch/operations.d.ts +77 -0
  77. package/dist/commands/secret/patch/operations.d.ts.map +1 -0
  78. package/dist/commands/secret/patch/operations.js +351 -0
  79. package/dist/commands/secret/patch/operations.js.map +1 -0
  80. package/dist/commands/secret/patch/parsers.d.ts +22 -0
  81. package/dist/commands/secret/patch/parsers.d.ts.map +1 -0
  82. package/dist/commands/secret/patch/parsers.js +349 -0
  83. package/dist/commands/secret/patch/parsers.js.map +1 -0
  84. package/dist/commands/secret/patch/types.d.ts +109 -0
  85. package/dist/commands/secret/patch/types.d.ts.map +1 -0
  86. package/dist/commands/secret/patch/types.js +22 -0
  87. package/dist/commands/secret/patch/types.js.map +1 -0
  88. package/dist/commands/secret/patch.d.ts +9 -0
  89. package/dist/commands/secret/patch.d.ts.map +1 -0
  90. package/dist/commands/secret/patch.js +247 -0
  91. package/dist/commands/secret/patch.js.map +1 -0
  92. package/dist/lib/db/audit.d.ts +16 -0
  93. package/dist/lib/db/audit.d.ts.map +1 -0
  94. package/dist/lib/db/audit.js +60 -0
  95. package/dist/lib/db/audit.js.map +1 -0
  96. package/dist/lib/db/client.d.ts +27 -0
  97. package/dist/lib/db/client.d.ts.map +1 -0
  98. package/dist/lib/db/client.js +70 -0
  99. package/dist/lib/db/client.js.map +1 -0
  100. package/dist/lib/db/emergency.d.ts +50 -0
  101. package/dist/lib/db/emergency.d.ts.map +1 -0
  102. package/dist/lib/db/emergency.js +180 -0
  103. package/dist/lib/db/emergency.js.map +1 -0
  104. package/dist/lib/db/health.d.ts +14 -0
  105. package/dist/lib/db/health.d.ts.map +1 -0
  106. package/dist/lib/db/health.js +177 -0
  107. package/dist/lib/db/health.js.map +1 -0
  108. package/dist/lib/db/index.d.ts +56 -0
  109. package/dist/lib/db/index.d.ts.map +1 -0
  110. package/dist/lib/db/index.js +107 -0
  111. package/dist/lib/db/index.js.map +1 -0
  112. package/dist/lib/db/lockdown.d.ts +15 -0
  113. package/dist/lib/db/lockdown.d.ts.map +1 -0
  114. package/dist/lib/db/lockdown.js +67 -0
  115. package/dist/lib/db/lockdown.js.map +1 -0
  116. package/dist/lib/db/tenants.d.ts +14 -0
  117. package/dist/lib/db/tenants.d.ts.map +1 -0
  118. package/dist/lib/db/tenants.js +88 -0
  119. package/dist/lib/db/tenants.js.map +1 -0
  120. package/dist/lib/db/types.d.ts +95 -0
  121. package/dist/lib/db/types.d.ts.map +1 -0
  122. package/dist/lib/db/types.js +3 -0
  123. package/dist/lib/db/types.js.map +1 -0
  124. package/dist/lib/db/users.d.ts +16 -0
  125. package/dist/lib/db/users.d.ts.map +1 -0
  126. package/dist/lib/db/users.js +95 -0
  127. package/dist/lib/db/users.js.map +1 -0
  128. package/dist/lib/db.d.ts +4 -112
  129. package/dist/lib/db.d.ts.map +1 -1
  130. package/dist/lib/db.js +4 -726
  131. package/dist/lib/db.js.map +1 -1
  132. package/package.json +5 -2
@@ -1,773 +1,7 @@
1
- // Path: znvault-cli/src/commands/policy.ts
2
- import ora from 'ora';
3
- import * as fs from 'fs';
4
- import { client } from '../lib/client.js';
5
- import { promptConfirm } from '../lib/prompts.js';
6
- import * as output from '../lib/output.js';
7
- // ============ Helper Functions ============
1
+ // Path: src/commands/policy.ts
8
2
  /**
9
- * Safely read a file with proper error handling
3
+ * Policy command re-exports for backward compatibility.
4
+ * The actual implementation has been modularized into src/commands/policy/
10
5
  */
11
- function safeReadFile(filePath) {
12
- if (!fs.existsSync(filePath)) {
13
- throw new Error(`File not found: ${filePath}`);
14
- }
15
- try {
16
- return fs.readFileSync(filePath, 'utf-8');
17
- }
18
- catch (err) {
19
- if (err instanceof Error) {
20
- if (err.message.includes('EACCES')) {
21
- throw new Error(`Permission denied: ${filePath}`);
22
- }
23
- if (err.message.includes('EISDIR')) {
24
- throw new Error(`Path is a directory, not a file: ${filePath}`);
25
- }
26
- }
27
- throw new Error(`Failed to read file: ${filePath}`);
28
- }
29
- }
30
- /**
31
- * Safely parse JSON with proper error handling
32
- */
33
- function safeParseJson(content, context) {
34
- try {
35
- return JSON.parse(content);
36
- }
37
- catch (err) {
38
- if (err instanceof SyntaxError) {
39
- throw new Error(`Invalid JSON in ${context}: ${err.message}`);
40
- }
41
- throw new Error(`Failed to parse ${context} as JSON`);
42
- }
43
- }
44
- /**
45
- * Safely write a file with proper error handling
46
- */
47
- function safeWriteFile(filePath, content) {
48
- try {
49
- fs.writeFileSync(filePath, content);
50
- }
51
- catch (err) {
52
- if (err instanceof Error) {
53
- if (err.message.includes('EACCES')) {
54
- throw new Error(`Permission denied: ${filePath}`);
55
- }
56
- if (err.message.includes('ENOENT')) {
57
- throw new Error(`Directory does not exist: ${filePath}`);
58
- }
59
- }
60
- throw new Error(`Failed to write file: ${filePath}`);
61
- }
62
- }
63
- export function registerPolicyCommands(program) {
64
- const policy = program
65
- .command('policy')
66
- .description('ABAC policy management commands');
67
- // ============ List Policies ============
68
- policy
69
- .command('list')
70
- .description('List ABAC policies')
71
- .option('--tenant <id>', 'Filter by tenant ID (superadmin only)')
72
- .option('--enabled', 'Show only enabled policies')
73
- .option('--disabled', 'Show only disabled policies')
74
- .option('--effect <effect>', 'Filter by effect (allow|deny)')
75
- .option('--search <term>', 'Search by name or description')
76
- .option('--json', 'Output as JSON')
77
- .action(async (options) => {
78
- const spinner = ora('Fetching policies...').start();
79
- try {
80
- const result = await client.listPolicies({
81
- tenantId: options.tenant,
82
- enabled: options.enabled ? true : options.disabled ? false : undefined,
83
- effect: options.effect,
84
- search: options.search,
85
- });
86
- spinner.stop();
87
- if (options.json) {
88
- output.json(result.items);
89
- return;
90
- }
91
- if (result.items.length === 0) {
92
- output.info('No policies found');
93
- return;
94
- }
95
- output.table(['ID', 'Name', 'Effect', 'Priority', 'Actions', 'Status', 'Tenant'], result.items.map(p => [
96
- p.id.substring(0, 8),
97
- p.name.length > 25 ? p.name.substring(0, 22) + '...' : p.name,
98
- p.effect.toUpperCase(),
99
- p.priority.toString(),
100
- p.actions.length > 2 ? `${p.actions.slice(0, 2).join(', ')}...` : p.actions.join(', '),
101
- p.isActive ? 'Enabled' : 'Disabled',
102
- p.tenantId ?? '-',
103
- ]));
104
- output.info(`Total: ${result.pagination.total} policy(s)${result.pagination.hasMore ? ' (more available)' : ''}`);
105
- }
106
- catch (err) {
107
- spinner.fail('Failed to list policies');
108
- output.error(err instanceof Error ? err.message : String(err));
109
- process.exit(1);
110
- }
111
- });
112
- // ============ Get Policy ============
113
- policy
114
- .command('get <id>')
115
- .description('Get policy details')
116
- .option('--json', 'Output as JSON')
117
- .action(async (id, options) => {
118
- const spinner = ora('Fetching policy...').start();
119
- try {
120
- const result = await client.getPolicy(id);
121
- spinner.stop();
122
- if (options.json) {
123
- output.json(result);
124
- return;
125
- }
126
- output.section('Policy Details');
127
- output.keyValue({
128
- 'ID': result.id,
129
- 'Name': result.name,
130
- 'Description': result.description ?? '-',
131
- 'Effect': result.effect.toUpperCase(),
132
- 'Priority': result.priority.toString(),
133
- 'Status': result.isActive ? 'Enabled' : 'Disabled',
134
- 'Tenant': result.tenantId ?? 'Global',
135
- 'Created': output.formatDate(result.createdAt),
136
- 'Updated': output.formatDate(result.updatedAt),
137
- });
138
- console.log();
139
- output.section('Actions');
140
- for (const action of result.actions) {
141
- console.log(` - ${action}`);
142
- }
143
- if (result.resources && result.resources.length > 0) {
144
- console.log();
145
- output.section('Resources');
146
- for (const resource of result.resources) {
147
- const parts = [`type: ${resource.type}`];
148
- if (resource.id)
149
- parts.push(`id: ${resource.id}`);
150
- if (resource.tenantId)
151
- parts.push(`tenant: ${resource.tenantId}`);
152
- if (resource.tags)
153
- parts.push(`tags: ${JSON.stringify(resource.tags)}`);
154
- console.log(` - ${parts.join(', ')}`);
155
- }
156
- }
157
- if (result.conditions && result.conditions.length > 0) {
158
- console.log();
159
- output.section('Conditions');
160
- for (const condition of result.conditions) {
161
- const op = condition.operator ? ` ${condition.operator}` : '';
162
- console.log(` - ${condition.type}${op}: ${JSON.stringify(condition.value)}`);
163
- }
164
- }
165
- console.log();
166
- }
167
- catch (err) {
168
- spinner.fail('Failed to get policy');
169
- output.error(err instanceof Error ? err.message : String(err));
170
- process.exit(1);
171
- }
172
- });
173
- // ============ Create Policy ============
174
- policy
175
- .command('create')
176
- .description('Create a new ABAC policy')
177
- .requiredOption('--name <name>', 'Policy name')
178
- .requiredOption('--effect <effect>', 'Policy effect (allow|deny)')
179
- .requiredOption('--actions <actions>', 'Comma-separated list of actions (e.g., secret:read:value,secret:update)')
180
- .option('--description <desc>', 'Policy description')
181
- .option('--priority <num>', 'Priority (higher = evaluated first)', '0')
182
- .option('--tenant <id>', 'Tenant ID (omit for global policy)')
183
- .option('--resources <json>', 'Resources JSON array')
184
- .option('--conditions <json>', 'Conditions JSON array')
185
- .option('--from-file <path>', 'Load policy definition from JSON file')
186
- .option('--json', 'Output as JSON')
187
- .action(async (options) => {
188
- try {
189
- let policyData;
190
- if (options.fromFile) {
191
- // Load from file
192
- const content = safeReadFile(options.fromFile);
193
- policyData = safeParseJson(content, options.fromFile);
194
- }
195
- else {
196
- // Parse and validate priority
197
- const priority = parseInt(options.priority, 10);
198
- if (isNaN(priority) || priority < 0) {
199
- output.error('Priority must be a non-negative number');
200
- process.exit(1);
201
- }
202
- // Build from options
203
- policyData = {
204
- name: options.name,
205
- description: options.description,
206
- effect: options.effect,
207
- actions: options.actions.split(',').map((a) => a.trim()),
208
- priority,
209
- tenantId: options.tenant,
210
- };
211
- if (options.resources) {
212
- policyData.resources = safeParseJson(options.resources, '--resources');
213
- }
214
- if (options.conditions) {
215
- policyData.conditions = safeParseJson(options.conditions, '--conditions');
216
- }
217
- }
218
- // Validate priority from file if loaded (ensure default and validate)
219
- if (policyData.priority === undefined) {
220
- policyData.priority = 0;
221
- }
222
- else if (typeof policyData.priority !== 'number' || isNaN(policyData.priority) || policyData.priority < 0) {
223
- output.error('Priority must be a non-negative number');
224
- process.exit(1);
225
- }
226
- const spinner = ora('Creating policy...').start();
227
- const result = await client.createPolicy(policyData);
228
- spinner.succeed('Policy created successfully');
229
- if (options.json) {
230
- output.json(result);
231
- }
232
- else {
233
- output.keyValue({
234
- 'ID': result.id,
235
- 'Name': result.name,
236
- 'Effect': result.effect.toUpperCase(),
237
- 'Priority': result.priority.toString(),
238
- 'Status': result.isActive ? 'Enabled' : 'Disabled',
239
- });
240
- }
241
- }
242
- catch (err) {
243
- output.error(err instanceof Error ? err.message : String(err));
244
- process.exit(1);
245
- }
246
- });
247
- // ============ Update Policy ============
248
- policy
249
- .command('update <id>')
250
- .description('Update an ABAC policy')
251
- .option('--name <name>', 'New policy name')
252
- .option('--description <desc>', 'New description')
253
- .option('--effect <effect>', 'New effect (allow|deny)')
254
- .option('--actions <actions>', 'New comma-separated list of actions')
255
- .option('--priority <num>', 'New priority')
256
- .option('--resources <json>', 'New resources JSON array')
257
- .option('--conditions <json>', 'New conditions JSON array')
258
- .option('--from-file <path>', 'Load updates from JSON file')
259
- .option('--json', 'Output as JSON')
260
- .action(async (id, options) => {
261
- try {
262
- let updates;
263
- if (options.fromFile) {
264
- const content = safeReadFile(options.fromFile);
265
- updates = safeParseJson(content, options.fromFile);
266
- }
267
- else {
268
- updates = {};
269
- if (options.name)
270
- updates.name = options.name;
271
- if (options.description)
272
- updates.description = options.description;
273
- if (options.effect)
274
- updates.effect = options.effect;
275
- if (options.actions)
276
- updates.actions = options.actions.split(',').map((a) => a.trim());
277
- if (options.priority) {
278
- const priority = parseInt(options.priority, 10);
279
- if (isNaN(priority) || priority < 0) {
280
- output.error('Priority must be a non-negative number');
281
- process.exit(1);
282
- }
283
- updates.priority = priority;
284
- }
285
- if (options.resources)
286
- updates.resources = safeParseJson(options.resources, '--resources');
287
- if (options.conditions)
288
- updates.conditions = safeParseJson(options.conditions, '--conditions');
289
- }
290
- if (Object.keys(updates).length === 0) {
291
- output.error('No updates specified');
292
- process.exit(1);
293
- }
294
- const spinner = ora('Updating policy...').start();
295
- const result = await client.updatePolicy(id, updates);
296
- spinner.succeed('Policy updated successfully');
297
- if (options.json) {
298
- output.json(result);
299
- }
300
- else {
301
- output.keyValue({
302
- 'ID': result.id,
303
- 'Name': result.name,
304
- 'Effect': result.effect.toUpperCase(),
305
- 'Updated': output.formatDate(result.updatedAt),
306
- });
307
- }
308
- }
309
- catch (err) {
310
- output.error(err instanceof Error ? err.message : String(err));
311
- process.exit(1);
312
- }
313
- });
314
- // ============ Delete Policy ============
315
- policy
316
- .command('delete <id>')
317
- .description('Delete an ABAC policy')
318
- .option('-y, --yes', 'Skip confirmation')
319
- .option('--json', 'Output as JSON')
320
- .action(async (id, options) => {
321
- try {
322
- if (!options.yes) {
323
- const confirmed = await promptConfirm(`Are you sure you want to delete policy '${id}'? This cannot be undone.`);
324
- if (!confirmed) {
325
- output.info('Delete cancelled');
326
- return;
327
- }
328
- }
329
- const spinner = ora('Deleting policy...').start();
330
- await client.deletePolicy(id);
331
- spinner.succeed(`Policy '${id}' deleted successfully`);
332
- if (options.json) {
333
- output.json({ success: true, id, message: 'Policy deleted successfully' });
334
- }
335
- }
336
- catch (err) {
337
- output.error(err instanceof Error ? err.message : String(err));
338
- process.exit(1);
339
- }
340
- });
341
- // ============ Enable Policy ============
342
- policy
343
- .command('enable <id>')
344
- .description('Enable an ABAC policy')
345
- .option('--json', 'Output as JSON')
346
- .action(async (id, options) => {
347
- const spinner = ora('Enabling policy...').start();
348
- try {
349
- const result = await client.togglePolicy(id, true);
350
- spinner.succeed('Policy enabled successfully');
351
- if (options.json) {
352
- output.json(result);
353
- }
354
- else {
355
- output.keyValue({
356
- 'ID': result.id,
357
- 'Name': result.name,
358
- 'Status': 'Enabled',
359
- });
360
- }
361
- }
362
- catch (err) {
363
- spinner.fail('Failed to enable policy');
364
- output.error(err instanceof Error ? err.message : String(err));
365
- process.exit(1);
366
- }
367
- });
368
- // ============ Disable Policy ============
369
- policy
370
- .command('disable <id>')
371
- .description('Disable an ABAC policy')
372
- .option('--json', 'Output as JSON')
373
- .action(async (id, options) => {
374
- const spinner = ora('Disabling policy...').start();
375
- try {
376
- const result = await client.togglePolicy(id, false);
377
- spinner.succeed('Policy disabled successfully');
378
- if (options.json) {
379
- output.json(result);
380
- }
381
- else {
382
- output.keyValue({
383
- 'ID': result.id,
384
- 'Name': result.name,
385
- 'Status': 'Disabled',
386
- });
387
- }
388
- }
389
- catch (err) {
390
- spinner.fail('Failed to disable policy');
391
- output.error(err instanceof Error ? err.message : String(err));
392
- process.exit(1);
393
- }
394
- });
395
- // ============ Validate Policy ============
396
- policy
397
- .command('validate')
398
- .description('Validate a policy definition without creating it')
399
- .requiredOption('--name <name>', 'Policy name')
400
- .requiredOption('--effect <effect>', 'Policy effect (allow|deny)')
401
- .requiredOption('--actions <actions>', 'Comma-separated list of actions')
402
- .option('--description <desc>', 'Policy description')
403
- .option('--priority <num>', 'Priority', '0')
404
- .option('--resources <json>', 'Resources JSON array')
405
- .option('--conditions <json>', 'Conditions JSON array')
406
- .option('--from-file <path>', 'Load policy from JSON file')
407
- .action(async (options) => {
408
- try {
409
- let policyData;
410
- if (options.fromFile) {
411
- const content = safeReadFile(options.fromFile);
412
- policyData = safeParseJson(content, options.fromFile);
413
- }
414
- else {
415
- const priority = parseInt(options.priority, 10);
416
- if (isNaN(priority) || priority < 0) {
417
- output.error('Priority must be a non-negative number');
418
- process.exit(1);
419
- }
420
- policyData = {
421
- name: options.name,
422
- description: options.description,
423
- effect: options.effect,
424
- actions: options.actions.split(',').map((a) => a.trim()),
425
- priority,
426
- };
427
- if (options.resources) {
428
- policyData.resources = safeParseJson(options.resources, '--resources');
429
- }
430
- if (options.conditions) {
431
- policyData.conditions = safeParseJson(options.conditions, '--conditions');
432
- }
433
- }
434
- const spinner = ora('Validating policy...').start();
435
- const result = await client.validatePolicy(policyData);
436
- if (result.valid) {
437
- spinner.succeed('Policy is valid');
438
- }
439
- else {
440
- spinner.fail('Policy validation failed');
441
- if (result.errors) {
442
- for (const error of result.errors) {
443
- output.error(` - ${error}`);
444
- }
445
- }
446
- process.exit(1);
447
- }
448
- }
449
- catch (err) {
450
- output.error(err instanceof Error ? err.message : String(err));
451
- process.exit(1);
452
- }
453
- });
454
- // ============ Show Policy Attachments ============
455
- policy
456
- .command('attachments <id>')
457
- .description('Show users and roles attached to a policy')
458
- .option('--json', 'Output as JSON')
459
- .action(async (id, options) => {
460
- const spinner = ora('Fetching attachments...').start();
461
- try {
462
- const result = await client.getPolicyAttachments(id);
463
- spinner.stop();
464
- if (options.json) {
465
- output.json(result);
466
- return;
467
- }
468
- if (result.users.length === 0 && result.roles.length === 0) {
469
- output.info('No attachments found for this policy');
470
- return;
471
- }
472
- if (result.users.length > 0) {
473
- output.section('Attached Users');
474
- output.table(['User ID', 'Username', 'Attached At'], result.users.map(a => [
475
- a.userId?.substring(0, 8) ?? '-',
476
- a.username ?? '-',
477
- output.formatDate(a.attachedAt),
478
- ]));
479
- }
480
- if (result.roles.length > 0) {
481
- console.log();
482
- output.section('Attached Roles');
483
- output.table(['Role ID', 'Role Name', 'Attached At'], result.roles.map(a => [
484
- a.roleId?.substring(0, 8) ?? '-',
485
- a.roleName ?? '-',
486
- output.formatDate(a.attachedAt),
487
- ]));
488
- }
489
- }
490
- catch (err) {
491
- spinner.fail('Failed to get attachments');
492
- output.error(err instanceof Error ? err.message : String(err));
493
- process.exit(1);
494
- }
495
- });
496
- // ============ Attach Policy to User ============
497
- policy
498
- .command('attach-user <policyId> <userId>')
499
- .description('Attach a policy to a user')
500
- .option('--json', 'Output as JSON')
501
- .action(async (policyId, userId, options) => {
502
- const spinner = ora('Attaching policy to user...').start();
503
- try {
504
- await client.attachPolicyToUser(policyId, userId);
505
- spinner.succeed('Policy attached to user successfully');
506
- if (options.json) {
507
- output.json({ success: true, policyId, userId, message: 'Policy attached to user successfully' });
508
- }
509
- }
510
- catch (err) {
511
- spinner.fail('Failed to attach policy');
512
- output.error(err instanceof Error ? err.message : String(err));
513
- process.exit(1);
514
- }
515
- });
516
- // ============ Attach Policy to Role ============
517
- policy
518
- .command('attach-role <policyId> <roleId>')
519
- .description('Attach a policy to a role')
520
- .option('--json', 'Output as JSON')
521
- .action(async (policyId, roleId, options) => {
522
- const spinner = ora('Attaching policy to role...').start();
523
- try {
524
- await client.attachPolicyToRole(policyId, roleId);
525
- spinner.succeed('Policy attached to role successfully');
526
- if (options.json) {
527
- output.json({ success: true, policyId, roleId, message: 'Policy attached to role successfully' });
528
- }
529
- }
530
- catch (err) {
531
- spinner.fail('Failed to attach policy');
532
- output.error(err instanceof Error ? err.message : String(err));
533
- process.exit(1);
534
- }
535
- });
536
- // ============ Detach Policy from User ============
537
- policy
538
- .command('detach-user <policyId> <userId>')
539
- .description('Detach a policy from a user')
540
- .option('--json', 'Output as JSON')
541
- .action(async (policyId, userId, options) => {
542
- const spinner = ora('Detaching policy from user...').start();
543
- try {
544
- await client.detachPolicyFromUser(policyId, userId);
545
- spinner.succeed('Policy detached from user successfully');
546
- if (options.json) {
547
- output.json({ success: true, policyId, userId, message: 'Policy detached from user successfully' });
548
- }
549
- }
550
- catch (err) {
551
- spinner.fail('Failed to detach policy');
552
- output.error(err instanceof Error ? err.message : String(err));
553
- process.exit(1);
554
- }
555
- });
556
- // ============ Detach Policy from Role ============
557
- policy
558
- .command('detach-role <policyId> <roleId>')
559
- .description('Detach a policy from a role')
560
- .option('--json', 'Output as JSON')
561
- .action(async (policyId, roleId, options) => {
562
- const spinner = ora('Detaching policy from role...').start();
563
- try {
564
- await client.detachPolicyFromRole(policyId, roleId);
565
- spinner.succeed('Policy detached from role successfully');
566
- if (options.json) {
567
- output.json({ success: true, policyId, roleId, message: 'Policy detached from role successfully' });
568
- }
569
- }
570
- catch (err) {
571
- spinner.fail('Failed to detach policy');
572
- output.error(err instanceof Error ? err.message : String(err));
573
- process.exit(1);
574
- }
575
- });
576
- // ============ List User's Policies ============
577
- policy
578
- .command('user-policies <userId>')
579
- .description('List policies attached to a user (directly or via roles)')
580
- .option('--json', 'Output as JSON')
581
- .action(async (userId, options) => {
582
- const spinner = ora('Fetching user policies...').start();
583
- try {
584
- const policies = await client.getUserPolicies(userId);
585
- spinner.stop();
586
- if (options.json) {
587
- output.json(policies);
588
- return;
589
- }
590
- if (policies.length === 0) {
591
- output.info('No policies attached to this user');
592
- return;
593
- }
594
- output.table(['ID', 'Name', 'Effect', 'Priority', 'Status'], policies.map(p => [
595
- p.id.substring(0, 8),
596
- p.name,
597
- p.effect.toUpperCase(),
598
- p.priority.toString(),
599
- p.isActive ? 'Enabled' : 'Disabled',
600
- ]));
601
- output.info(`Total: ${policies.length} policy(s)`);
602
- }
603
- catch (err) {
604
- spinner.fail('Failed to get user policies');
605
- output.error(err instanceof Error ? err.message : String(err));
606
- process.exit(1);
607
- }
608
- });
609
- // ============ List Role's Policies ============
610
- policy
611
- .command('role-policies <roleId>')
612
- .description('List policies attached to a role')
613
- .option('--json', 'Output as JSON')
614
- .action(async (roleId, options) => {
615
- const spinner = ora('Fetching role policies...').start();
616
- try {
617
- const policies = await client.getRolePolicies(roleId);
618
- spinner.stop();
619
- if (options.json) {
620
- output.json(policies);
621
- return;
622
- }
623
- if (policies.length === 0) {
624
- output.info('No policies attached to this role');
625
- return;
626
- }
627
- output.table(['ID', 'Name', 'Effect', 'Priority', 'Status'], policies.map(p => [
628
- p.id.substring(0, 8),
629
- p.name,
630
- p.effect.toUpperCase(),
631
- p.priority.toString(),
632
- p.isActive ? 'Enabled' : 'Disabled',
633
- ]));
634
- output.info(`Total: ${policies.length} policy(s)`);
635
- }
636
- catch (err) {
637
- spinner.fail('Failed to get role policies');
638
- output.error(err instanceof Error ? err.message : String(err));
639
- process.exit(1);
640
- }
641
- });
642
- // ============ Test Policy Evaluation ============
643
- policy
644
- .command('test')
645
- .description('Test ABAC policy evaluation for a user and action')
646
- .requiredOption('--user <userId>', 'User ID to test')
647
- .requiredOption('--action <action>', 'Action to test (e.g., secret:read:value)')
648
- .option('--resource-type <type>', 'Resource type (secret|kms_key|certificate|...)')
649
- .option('--resource-id <id>', 'Resource ID')
650
- .option('--resource-tenant <id>', 'Resource tenant ID')
651
- .option('--ip <ip>', 'Simulated client IP address')
652
- .option('--mfa', 'Simulate MFA verified')
653
- .option('--json', 'Output as JSON')
654
- .action(async (options) => {
655
- const spinner = ora('Testing policy evaluation...').start();
656
- try {
657
- const request = {
658
- userId: options.user,
659
- action: options.action,
660
- resource: options.resourceType ? {
661
- type: options.resourceType,
662
- id: options.resourceId,
663
- tenantId: options.resourceTenant,
664
- } : undefined,
665
- requestContext: (options.ip !== undefined || options.mfa !== undefined) ? {
666
- ip: options.ip,
667
- mfaVerified: options.mfa ?? false,
668
- } : undefined,
669
- };
670
- const result = await client.testPolicy(request);
671
- spinner.stop();
672
- if (options.json) {
673
- output.json(result);
674
- return;
675
- }
676
- // Display result with color
677
- const statusIcon = result.allowed ? '[OK]' : '[X]';
678
- const statusText = result.allowed ? 'ALLOWED' : 'DENIED';
679
- console.log();
680
- console.log(` ${statusIcon} Access: ${statusText}`);
681
- console.log(` Effect: ${result.effect.toUpperCase()}`);
682
- console.log(` Reason: ${result.reason}`);
683
- console.log();
684
- output.keyValue({
685
- 'Policies Evaluated': result.evaluatedPolicies.toString(),
686
- 'Policies Matched': result.matchedPolicies.length.toString(),
687
- 'Evaluation Time': `${result.evaluationTimeMs}ms`,
688
- });
689
- if (result.matchedPolicies.length > 0) {
690
- console.log();
691
- output.section('Matched Policies');
692
- output.table(['Name', 'Effect', 'Priority'], result.matchedPolicies.map(p => [
693
- p.name,
694
- p.effect.toUpperCase(),
695
- p.priority.toString(),
696
- ]));
697
- }
698
- }
699
- catch (err) {
700
- spinner.fail('Failed to test policy');
701
- output.error(err instanceof Error ? err.message : String(err));
702
- process.exit(1);
703
- }
704
- });
705
- // ============ Export Policy ============
706
- policy
707
- .command('export <id>')
708
- .description('Export a policy as JSON')
709
- .option('-o, --output <path>', 'Output file path')
710
- .action(async (id, options) => {
711
- const spinner = ora('Exporting policy...').start();
712
- try {
713
- const result = await client.getPolicy(id);
714
- spinner.stop();
715
- const exportData = {
716
- name: result.name,
717
- description: result.description,
718
- effect: result.effect,
719
- actions: result.actions,
720
- resources: result.resources,
721
- conditions: result.conditions,
722
- priority: result.priority,
723
- };
724
- const jsonString = JSON.stringify(exportData, null, 2);
725
- if (options.output) {
726
- safeWriteFile(options.output, jsonString);
727
- output.success(`Policy exported to ${options.output}`);
728
- }
729
- else {
730
- console.log(jsonString);
731
- }
732
- }
733
- catch (err) {
734
- spinner.fail('Failed to export policy');
735
- output.error(err instanceof Error ? err.message : String(err));
736
- process.exit(1);
737
- }
738
- });
739
- // ============ Import Policy ============
740
- policy
741
- .command('import <path>')
742
- .description('Import a policy from JSON file')
743
- .option('--tenant <id>', 'Override tenant ID')
744
- .option('--json', 'Output as JSON')
745
- .action(async (path, options) => {
746
- try {
747
- const content = safeReadFile(path);
748
- const policyData = safeParseJson(content, path);
749
- if (options.tenant) {
750
- policyData.tenantId = options.tenant;
751
- }
752
- const spinner = ora('Importing policy...').start();
753
- const result = await client.createPolicy(policyData);
754
- spinner.succeed('Policy imported successfully');
755
- if (options.json) {
756
- output.json(result);
757
- }
758
- else {
759
- output.keyValue({
760
- 'ID': result.id,
761
- 'Name': result.name,
762
- 'Effect': result.effect.toUpperCase(),
763
- 'Status': result.isActive ? 'Enabled' : 'Disabled',
764
- });
765
- }
766
- }
767
- catch (err) {
768
- output.error(err instanceof Error ? err.message : String(err));
769
- process.exit(1);
770
- }
771
- });
772
- }
6
+ export { registerPolicyCommands } from './policy/index.js';
773
7
  //# sourceMappingURL=policy.js.map