@brunsforge/aarm 0.1.0

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 (43) hide show
  1. package/README.md +1180 -0
  2. package/dist/aarm.cjs +39260 -0
  3. package/dist/aarm.mjs +39262 -0
  4. package/dist/commands/apps.d.ts +2 -0
  5. package/dist/commands/apps.js +32 -0
  6. package/dist/commands/apps.js.map +1 -0
  7. package/dist/commands/preflight.d.ts +2 -0
  8. package/dist/commands/preflight.js +160 -0
  9. package/dist/commands/preflight.js.map +1 -0
  10. package/dist/commands/report.d.ts +2 -0
  11. package/dist/commands/report.js +166 -0
  12. package/dist/commands/report.js.map +1 -0
  13. package/dist/commands/secrets.d.ts +2 -0
  14. package/dist/commands/secrets.js +88 -0
  15. package/dist/commands/secrets.js.map +1 -0
  16. package/dist/commands/tenants.d.ts +2 -0
  17. package/dist/commands/tenants.js +209 -0
  18. package/dist/commands/tenants.js.map +1 -0
  19. package/dist/commands/usage.d.ts +2 -0
  20. package/dist/commands/usage.js +203 -0
  21. package/dist/commands/usage.js.map +1 -0
  22. package/dist/config/ConfigStore.d.ts +12 -0
  23. package/dist/config/ConfigStore.js +53 -0
  24. package/dist/config/ConfigStore.js.map +1 -0
  25. package/dist/config/CredentialStore.d.ts +8 -0
  26. package/dist/config/CredentialStore.js +29 -0
  27. package/dist/config/CredentialStore.js.map +1 -0
  28. package/dist/config/HistoryStore.d.ts +19 -0
  29. package/dist/config/HistoryStore.js +64 -0
  30. package/dist/config/HistoryStore.js.map +1 -0
  31. package/dist/exitCodes.d.ts +9 -0
  32. package/dist/exitCodes.js +10 -0
  33. package/dist/exitCodes.js.map +1 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +38 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/output/formatters.d.ts +10 -0
  38. package/dist/output/formatters.js +129 -0
  39. package/dist/output/formatters.js.map +1 -0
  40. package/dist/shared/context.d.ts +31 -0
  41. package/dist/shared/context.js +124 -0
  42. package/dist/shared/context.js.map +1 -0
  43. package/package.json +43 -0
@@ -0,0 +1,209 @@
1
+ import readline from 'node:readline';
2
+ import chalk from 'chalk';
3
+ import { createResultEnvelope, envelopeToJson } from '@brunsforge/azure-app-registration-monitor';
4
+ import { ConfigStore } from '../config/ConfigStore.js';
5
+ import { CredentialStore } from '../config/CredentialStore.js';
6
+ import { printTenantsTable } from '../output/formatters.js';
7
+ import { buildContext, handleError } from '../shared/context.js';
8
+ import { EXIT } from '../exitCodes.js';
9
+ function ask(rl, question) {
10
+ return new Promise((resolve) => rl.question(question, resolve));
11
+ }
12
+ function readSecret(prompt) {
13
+ return new Promise((resolve) => {
14
+ process.stdout.write(prompt);
15
+ let input = '';
16
+ const onData = (chunk) => {
17
+ const char = chunk.toString('utf8');
18
+ // Enter or Ctrl-D (EOF)
19
+ if (char === '\r' || char === '\n' || char === '') {
20
+ process.stdin.setRawMode?.(false);
21
+ process.stdin.pause();
22
+ process.stdin.removeListener('data', onData);
23
+ process.stdout.write('\n');
24
+ resolve(input);
25
+ // Ctrl-C
26
+ }
27
+ else if (char === '') {
28
+ process.stdout.write('\n');
29
+ process.exit(130);
30
+ // DEL or Backspace
31
+ }
32
+ else if (char === '' || char === '\b') {
33
+ input = input.slice(0, -1);
34
+ // Ignore ANSI escape sequences
35
+ }
36
+ else if (!char.startsWith('')) {
37
+ input += char;
38
+ }
39
+ };
40
+ readline.emitKeypressEvents(process.stdin);
41
+ if (process.stdin.isTTY)
42
+ process.stdin.setRawMode(true);
43
+ process.stdin.resume();
44
+ process.stdin.on('data', onData);
45
+ });
46
+ }
47
+ const VALID_AUTH_MODES = [
48
+ 'client-secret',
49
+ 'device-code',
50
+ 'interactive-browser',
51
+ 'certificate',
52
+ 'azure-cli',
53
+ 'username-password',
54
+ ];
55
+ export function registerTenantsCommand(program) {
56
+ const tenants = program
57
+ .command('tenants')
58
+ .description('Manage configured tenants')
59
+ .action(() => tenants.help());
60
+ tenants
61
+ .command('list')
62
+ .description('List configured tenants')
63
+ .action(async () => {
64
+ const parent = tenants.parent;
65
+ const opts = parent.opts();
66
+ const store = new ConfigStore(opts.configDir);
67
+ const list = await store.listTenants();
68
+ if (opts.output === 'json') {
69
+ process.stdout.write(envelopeToJson(createResultEnvelope(list, 'local', 'config')) + '\n');
70
+ }
71
+ else {
72
+ printTenantsTable(list);
73
+ }
74
+ });
75
+ tenants
76
+ .command('add')
77
+ .description('Add or update a tenant configuration')
78
+ .option('--tenant-id <id>', 'Entra tenant ID (GUID)')
79
+ .option('--display-name <name>', 'Friendly name for this tenant')
80
+ .option('--auth-mode <mode>', `Authentication mode: ${VALID_AUTH_MODES.join(', ')}`)
81
+ .option('--client-id <id>', 'App Registration client ID used for auth')
82
+ .option('--username <email>', 'UPN / email address (username-password mode only)')
83
+ .option('--workspace-id <id>', 'Log Analytics workspace ID (optional)')
84
+ .action(async (opts) => {
85
+ const parent = tenants.parent;
86
+ const globalOpts = parent.opts();
87
+ const store = new ConfigStore(globalOpts.configDir);
88
+ const credStore = new CredentialStore();
89
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
90
+ const tenantId = opts.tenantId ?? (await ask(rl, 'Tenant ID (GUID): ')).trim();
91
+ const displayName = opts.displayName ?? (await ask(rl, 'Display name: ')).trim();
92
+ const authModeRaw = opts.authMode ??
93
+ (await ask(rl, `Auth mode (${VALID_AUTH_MODES.join('/')}): `)).trim();
94
+ if (!VALID_AUTH_MODES.includes(authModeRaw)) {
95
+ process.stderr.write(`Invalid auth mode: "${authModeRaw}"\n`);
96
+ rl.close();
97
+ process.exit(EXIT.CONFIG_INVALID);
98
+ }
99
+ const authMode = authModeRaw;
100
+ let clientId;
101
+ if (authMode !== 'azure-cli') {
102
+ clientId =
103
+ opts.clientId ?? (await ask(rl, 'Client ID (App Registration GUID): ')).trim();
104
+ }
105
+ const workspaceId = opts.workspaceId ??
106
+ ((await ask(rl, 'Log Analytics Workspace ID (optional, press Enter to skip): ')).trim() ||
107
+ undefined);
108
+ rl.close();
109
+ let username;
110
+ const now = new Date().toISOString();
111
+ const profile = {
112
+ tenantId,
113
+ displayName,
114
+ authMode,
115
+ clientId,
116
+ defaultEnvironmentName: 'default',
117
+ ...(workspaceId ? { logAnalyticsWorkspaceId: workspaceId } : {}),
118
+ createdAt: now,
119
+ updatedAt: now,
120
+ };
121
+ if (authMode === 'client-secret' && clientId) {
122
+ const secret = await readSecret('Client Secret (hidden): ');
123
+ if (!secret) {
124
+ process.stderr.write('Client secret cannot be empty.\n');
125
+ process.exit(EXIT.CONFIG_INVALID);
126
+ }
127
+ await credStore.setClientSecret(tenantId, clientId, secret);
128
+ process.stdout.write(chalk.green('✓') + ' Client secret saved to Windows Credential Manager.\n');
129
+ }
130
+ else if (authMode === 'username-password' && clientId) {
131
+ process.stdout.write(chalk.yellow('⚠') +
132
+ ' username-password mode does not support MFA. Use device-code for accounts with MFA.\n');
133
+ username =
134
+ opts.username ?? (await (async () => {
135
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
136
+ const val = await ask(rl2, 'Username (email / UPN): ');
137
+ rl2.close();
138
+ return val.trim();
139
+ })());
140
+ if (!username) {
141
+ process.stderr.write('Username cannot be empty.\n');
142
+ process.exit(EXIT.CONFIG_INVALID);
143
+ }
144
+ const password = await readSecret('Password (hidden): ');
145
+ if (!password) {
146
+ process.stderr.write('Password cannot be empty.\n');
147
+ process.exit(EXIT.CONFIG_INVALID);
148
+ }
149
+ profile.username = username;
150
+ await credStore.setUserPassword(tenantId, clientId, username, password);
151
+ process.stdout.write(chalk.green('✓') + ' Password saved to Windows Credential Manager.\n');
152
+ }
153
+ await store.upsertTenant(profile);
154
+ process.stdout.write(chalk.green('✓') + ` Tenant "${displayName}" saved.\n`);
155
+ });
156
+ tenants
157
+ .command('remove')
158
+ .description('Remove a tenant configuration')
159
+ .argument('<name-or-id>', 'Tenant display name or ID')
160
+ .action(async (nameOrId) => {
161
+ const parent = tenants.parent;
162
+ const globalOpts = parent.opts();
163
+ const store = new ConfigStore(globalOpts.configDir);
164
+ const tenant = await store.getTenant(nameOrId);
165
+ if (!tenant) {
166
+ process.stderr.write(`Tenant "${nameOrId}" not found.\n`);
167
+ process.exit(EXIT.CONFIG_INVALID);
168
+ }
169
+ await store.removeTenant(tenant.tenantId);
170
+ process.stdout.write(chalk.green('✓') + ` Tenant "${tenant.displayName}" removed.\n`);
171
+ });
172
+ tenants
173
+ .command('validate')
174
+ .description('Validate that a tenant can authenticate and reach Microsoft Graph')
175
+ .action(async () => {
176
+ try {
177
+ const globalOpts = tenants.parent.opts();
178
+ const ctx = await buildContext(globalOpts);
179
+ // Progress to stderr only — stdout must remain clean for JSON consumers
180
+ process.stderr.write(chalk.dim(`Validating tenant ${ctx.tenantId}...\n`));
181
+ const result = await ctx.preflightService.run({
182
+ tenantId: ctx.tenantId,
183
+ environmentName: ctx.environmentName,
184
+ authMode: ctx.authMode,
185
+ });
186
+ if (result.authValid && result.graphReachable) {
187
+ process.stdout.write(chalk.green('✓') +
188
+ ` Tenant "${globalOpts.tenant}" is valid.\n` +
189
+ ` Auth: OK | Graph: reachable\n`);
190
+ if (result.missingPermissions.length > 0) {
191
+ process.stdout.write(chalk.yellow(' Warnings:\n') +
192
+ result.missingPermissions.map((p) => ` • ${p}`).join('\n') +
193
+ '\n');
194
+ }
195
+ }
196
+ else {
197
+ process.stderr.write(chalk.red('✗') + ' Validation failed.\n');
198
+ for (const e of result.errors) {
199
+ process.stderr.write(` ${e}\n`);
200
+ }
201
+ process.exit(EXIT.AUTH_FAILED);
202
+ }
203
+ }
204
+ catch (err) {
205
+ handleError(err);
206
+ }
207
+ });
208
+ }
209
+ //# sourceMappingURL=tenants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenants.js","sourceRoot":"","sources":["../../src/commands/tenants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerUsageCommand(program: Command): void;
@@ -0,0 +1,203 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import { LogAnalyticsClient, createResultEnvelope, envelopeToJson, } from '@brunsforge/azure-app-registration-monitor';
4
+ import { buildContext, handleError } from '../shared/context.js';
5
+ import { EXIT } from '../exitCodes.js';
6
+ async function resolveAppId(ctx, appId, appName) {
7
+ if (appId)
8
+ return appId;
9
+ if (!appName) {
10
+ process.stderr.write('Error: either --app-id or --app-name is required.\n');
11
+ process.exit(EXIT.CONFIG_INVALID);
12
+ }
13
+ const app = await ctx.graphReader.findByDisplayName(appName);
14
+ if (!app) {
15
+ process.stderr.write(`Error: no app registration found with display name "${appName}".\n` +
16
+ ' Check the exact name or use --app-id with the client ID.\n');
17
+ process.exit(EXIT.CONFIG_INVALID);
18
+ }
19
+ return app.appId;
20
+ }
21
+ function requireWorkspace(workspaceId, isJson) {
22
+ if (!workspaceId) {
23
+ if (!isJson) {
24
+ process.stderr.write(chalk.red('✗') +
25
+ ' No Log Analytics workspace configured for this tenant.\n' +
26
+ ' Re-run "aarm tenants add --tenant <name>" and provide a Workspace ID,\n' +
27
+ ' or verify that the tenant was configured with a workspace ID.\n');
28
+ }
29
+ else {
30
+ process.stdout.write(JSON.stringify({ success: false, errors: ['No Log Analytics workspace configured for this tenant.'] }) + '\n');
31
+ }
32
+ process.exit(EXIT.NO_DATA_SOURCE);
33
+ }
34
+ }
35
+ function printAppUsage(result) {
36
+ process.stdout.write(`\nUsage Analysis — App ID: ${chalk.bold(result.appId)}\n`);
37
+ process.stdout.write(`Look-back: ${result.lookBackDays} days | Workspace: ${result.workspaceId}\n\n`);
38
+ process.stdout.write(`Total: ${result.totalCount} ` +
39
+ `Success: ${chalk.green(String(result.successCount))} ` +
40
+ `Failures: ${result.failureCount > 0 ? chalk.red(String(result.failureCount)) : '0'}\n` +
41
+ `First seen: ${result.firstSeen ?? chalk.dim('—')} Last seen: ${result.lastSeen ?? chalk.dim('—')}\n` +
42
+ `Active credential keys: ${result.distinctKeyIds.length > 0 ? result.distinctKeyIds.join(', ') : chalk.dim('none')}\n\n`);
43
+ if (result.rows.length === 0) {
44
+ process.stdout.write(chalk.dim('No sign-in activity found in this window.\n'));
45
+ return;
46
+ }
47
+ const table = new Table({
48
+ head: ['Time', 'Key ID', 'Resource', 'IP', 'Result'],
49
+ style: { head: ['cyan'] },
50
+ });
51
+ for (const row of result.rows.slice(0, 100)) {
52
+ const ts = row.timeGenerated ? new Date(row.timeGenerated).toLocaleString() : '—';
53
+ const keyShort = row.credentialKeyId ? row.credentialKeyId.slice(0, 8) + '…' : '—';
54
+ const result_ = row.resultType === '0' ? chalk.green('OK') : chalk.red(row.resultType);
55
+ table.push([ts, keyShort, row.resourceDisplayName || '—', row.ipAddress || '—', result_]);
56
+ }
57
+ process.stdout.write(table.toString() + '\n');
58
+ if (result.rows.length > 100) {
59
+ process.stdout.write(chalk.dim(`Showing 100 of ${result.rows.length} rows. Use --output json for full data.\n`));
60
+ }
61
+ }
62
+ function printSecretUsage(result, label) {
63
+ process.stdout.write(`\n${label}\n`);
64
+ process.stdout.write(`Look-back: ${result.lookBackDays} days | Total sign-ins: ${result.totalCount}\n`);
65
+ process.stdout.write(`Last seen: ${result.lastSeen ?? chalk.dim('never in this window')}\n\n`);
66
+ if (result.rows.length === 0) {
67
+ process.stdout.write(chalk.dim('No sign-in activity found for this key in this window.\n'));
68
+ return;
69
+ }
70
+ const table = new Table({
71
+ head: ['Last Seen', 'Count', 'Resource', 'IP', 'Result'],
72
+ style: { head: ['cyan'] },
73
+ });
74
+ for (const row of result.rows) {
75
+ const ts = row.lastSeen ? new Date(row.lastSeen).toLocaleString() : '—';
76
+ const result_ = row.resultType === '0' ? chalk.green('OK') : chalk.red(row.resultType);
77
+ table.push([ts, String(row.count), row.resourceDisplayName || '—', row.ipAddress || '—', result_]);
78
+ }
79
+ process.stdout.write(table.toString() + '\n');
80
+ }
81
+ export function registerUsageCommand(program) {
82
+ const usage = program
83
+ .command('usage')
84
+ .description('Analyze Service Principal Sign-in Logs via Log Analytics')
85
+ .action(() => usage.help());
86
+ usage
87
+ .command('analyze')
88
+ .description('Show sign-in history for an App Registration')
89
+ .option('--app-id <client-id>', 'App Registration client ID (GUID)')
90
+ .option('--app-name <name>', 'App Registration display name (resolved via Graph)')
91
+ .option('--days <n>', 'Look-back window in days', '90')
92
+ .action(async (cmdOpts) => {
93
+ try {
94
+ const ctx = await buildContext(program.opts());
95
+ requireWorkspace(ctx.logAnalyticsWorkspaceId, ctx.isJson);
96
+ const appId = await resolveAppId(ctx, cmdOpts.appId, cmdOpts.appName);
97
+ const days = parseInt(cmdOpts.days, 10);
98
+ const client = new LogAnalyticsClient(ctx.credential);
99
+ const result = await client.queryAppUsage(ctx.logAnalyticsWorkspaceId, appId, days);
100
+ if (ctx.isJson) {
101
+ process.stdout.write(envelopeToJson(createResultEnvelope(result, ctx.tenantId, ctx.environmentName)) + '\n');
102
+ }
103
+ else {
104
+ printAppUsage(result);
105
+ }
106
+ }
107
+ catch (err) {
108
+ handleError(err);
109
+ }
110
+ });
111
+ usage
112
+ .command('analyze-secret')
113
+ .description('Show sign-in history for a specific credential key ID')
114
+ .requiredOption('--key-id <secret-key-id>', 'Credential key ID (GUID from the secret)')
115
+ .option('--days <n>', 'Look-back window in days', '90')
116
+ .action(async (cmdOpts) => {
117
+ try {
118
+ const ctx = await buildContext(program.opts());
119
+ requireWorkspace(ctx.logAnalyticsWorkspaceId, ctx.isJson);
120
+ const days = parseInt(cmdOpts.days, 10);
121
+ const client = new LogAnalyticsClient(ctx.credential);
122
+ const result = await client.querySecretUsage(ctx.logAnalyticsWorkspaceId, cmdOpts.keyId, days);
123
+ if (ctx.isJson) {
124
+ process.stdout.write(envelopeToJson(createResultEnvelope(result, ctx.tenantId, ctx.environmentName)) + '\n');
125
+ }
126
+ else {
127
+ printSecretUsage(result, `Usage — Key ID: ${chalk.bold(cmdOpts.keyId)}`);
128
+ }
129
+ }
130
+ catch (err) {
131
+ handleError(err);
132
+ }
133
+ });
134
+ usage
135
+ .command('last-seen')
136
+ .description('Show when an App Registration was last used (summarized)')
137
+ .option('--app-id <client-id>', 'App Registration client ID (GUID)')
138
+ .option('--app-name <name>', 'App Registration display name (resolved via Graph)')
139
+ .option('--days <n>', 'Look-back window in days', '90')
140
+ .action(async (cmdOpts) => {
141
+ try {
142
+ const ctx = await buildContext(program.opts());
143
+ requireWorkspace(ctx.logAnalyticsWorkspaceId, ctx.isJson);
144
+ const appId = await resolveAppId(ctx, cmdOpts.appId, cmdOpts.appName);
145
+ const days = parseInt(cmdOpts.days, 10);
146
+ const client = new LogAnalyticsClient(ctx.credential);
147
+ const result = await client.queryAppUsage(ctx.logAnalyticsWorkspaceId, appId, days);
148
+ const summary = {
149
+ appId: result.appId,
150
+ lastSeen: result.lastSeen,
151
+ totalSignIns: result.totalCount,
152
+ activeKeyIds: result.distinctKeyIds,
153
+ lookBackDays: days,
154
+ };
155
+ if (ctx.isJson) {
156
+ process.stdout.write(envelopeToJson(createResultEnvelope(summary, ctx.tenantId, ctx.environmentName)) + '\n');
157
+ }
158
+ else {
159
+ process.stdout.write(`App ID : ${result.appId}\n`);
160
+ process.stdout.write(`Last seen: ${result.lastSeen ?? chalk.dim('not seen in this window')}\n`);
161
+ process.stdout.write(`Sign-ins : ${result.totalCount} (last ${days} days)\n`);
162
+ process.stdout.write(`Keys used: ${result.distinctKeyIds.join(', ') || chalk.dim('none')}\n`);
163
+ }
164
+ }
165
+ catch (err) {
166
+ handleError(err);
167
+ }
168
+ });
169
+ usage
170
+ .command('rotation-check')
171
+ .description('Check whether an old key ID is still being used after rotation')
172
+ .requiredOption('--app-id <client-id>', 'App Registration client ID')
173
+ .requiredOption('--old-key-id <secret-key-id>', 'The old credential key ID to watch')
174
+ .option('--days <n>', 'Look-back window in days', '14')
175
+ .action(async (cmdOpts) => {
176
+ try {
177
+ const ctx = await buildContext(program.opts());
178
+ requireWorkspace(ctx.logAnalyticsWorkspaceId, ctx.isJson);
179
+ const days = parseInt(cmdOpts.days, 10);
180
+ const client = new LogAnalyticsClient(ctx.credential);
181
+ const result = await client.queryRotationCheck(ctx.logAnalyticsWorkspaceId, cmdOpts.appId, cmdOpts.oldKeyId, days);
182
+ if (ctx.isJson) {
183
+ process.stdout.write(envelopeToJson(createResultEnvelope(result, ctx.tenantId, ctx.environmentName)) + '\n');
184
+ }
185
+ else {
186
+ const label = `Rotation Check — App: ${chalk.bold(cmdOpts.appId)} Old Key: ${chalk.bold(cmdOpts.oldKeyId)}`;
187
+ if (result.totalCount === 0) {
188
+ process.stdout.write(chalk.green('✓') + ` Old key not seen in the last ${days} days. Safe to delete.\n`);
189
+ }
190
+ else {
191
+ process.stdout.write(chalk.yellow('⚠') +
192
+ ` Old key still active: ${result.totalCount} sign-in(s) in the last ${days} days.\n` +
193
+ ` Last seen: ${result.lastSeen}\n`);
194
+ printSecretUsage(result, label);
195
+ }
196
+ }
197
+ }
198
+ catch (err) {
199
+ handleError(err);
200
+ }
201
+ });
202
+ }
203
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/commands/usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { TenantProfile } from '@brunsforge/azure-app-registration-monitor';
2
+ export declare class ConfigStore {
3
+ private readonly configDir;
4
+ constructor(configDir?: string);
5
+ getConfigDir(): string;
6
+ private get tenantsPath();
7
+ listTenants(): Promise<TenantProfile[]>;
8
+ getTenant(nameOrId: string): Promise<TenantProfile | undefined>;
9
+ upsertTenant(profile: TenantProfile): Promise<void>;
10
+ removeTenant(tenantId: string): Promise<boolean>;
11
+ private save;
12
+ }
@@ -0,0 +1,53 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const DEFAULT_CONFIG_DIR = join(homedir(), '.aarm');
5
+ export class ConfigStore {
6
+ configDir;
7
+ constructor(configDir = DEFAULT_CONFIG_DIR) {
8
+ this.configDir = configDir;
9
+ }
10
+ getConfigDir() {
11
+ return this.configDir;
12
+ }
13
+ get tenantsPath() {
14
+ return join(this.configDir, 'tenants.json');
15
+ }
16
+ async listTenants() {
17
+ try {
18
+ const raw = await readFile(this.tenantsPath, 'utf8');
19
+ return JSON.parse(raw);
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
25
+ async getTenant(nameOrId) {
26
+ const tenants = await this.listTenants();
27
+ return tenants.find((t) => t.tenantId === nameOrId || t.displayName === nameOrId);
28
+ }
29
+ async upsertTenant(profile) {
30
+ const tenants = await this.listTenants();
31
+ const idx = tenants.findIndex((t) => t.tenantId === profile.tenantId);
32
+ if (idx >= 0) {
33
+ tenants[idx] = profile;
34
+ }
35
+ else {
36
+ tenants.push(profile);
37
+ }
38
+ await this.save(tenants);
39
+ }
40
+ async removeTenant(tenantId) {
41
+ const tenants = await this.listTenants();
42
+ const filtered = tenants.filter((t) => t.tenantId !== tenantId);
43
+ if (filtered.length === tenants.length)
44
+ return false;
45
+ await this.save(filtered);
46
+ return true;
47
+ }
48
+ async save(tenants) {
49
+ await mkdir(this.configDir, { recursive: true });
50
+ await writeFile(this.tenantsPath, JSON.stringify(tenants, null, 2), 'utf8');
51
+ }
52
+ }
53
+ //# sourceMappingURL=ConfigStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigStore.js","sourceRoot":"","sources":["../../src/config/ConfigStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,YAAoB,kBAAkB;QAAtC,cAAS,GAAT,SAAS,CAA6B;IAAG,CAAC;IAEvE,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,IAAY,WAAW;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzC,OAAO,OAAO,CAAC,IAAI,CACjB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,KAAK,QAAQ,CAC7D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAsB;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,QAAgB;QACjC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAChE,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACrD,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,OAAwB;QACzC,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC9E,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export declare class CredentialStore {
2
+ getClientSecret(tenantId: string, clientId: string): Promise<string | null>;
3
+ setClientSecret(tenantId: string, clientId: string, secret: string): Promise<void>;
4
+ deleteClientSecret(tenantId: string, clientId: string): Promise<boolean>;
5
+ getUserPassword(tenantId: string, clientId: string, username: string): Promise<string | null>;
6
+ setUserPassword(tenantId: string, clientId: string, username: string, password: string): Promise<void>;
7
+ deleteUserPassword(tenantId: string, clientId: string, username: string): Promise<boolean>;
8
+ }
@@ -0,0 +1,29 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ // keytar is a CommonJS native addon — import via createRequire for ESM compatibility.
4
+ const keytar = require('keytar');
5
+ const SERVICE = 'aarm';
6
+ export class CredentialStore {
7
+ // ── Client secrets (service-principal / client-secret mode) ──────────────
8
+ async getClientSecret(tenantId, clientId) {
9
+ return keytar.getPassword(SERVICE, `${tenantId}:${clientId}`);
10
+ }
11
+ async setClientSecret(tenantId, clientId, secret) {
12
+ await keytar.setPassword(SERVICE, `${tenantId}:${clientId}`, secret);
13
+ }
14
+ async deleteClientSecret(tenantId, clientId) {
15
+ return keytar.deletePassword(SERVICE, `${tenantId}:${clientId}`);
16
+ }
17
+ // ── User passwords (username-password / ROPC mode) ───────────────────────
18
+ // Key includes the UPN so a single client-id can hold credentials for different users.
19
+ async getUserPassword(tenantId, clientId, username) {
20
+ return keytar.getPassword(SERVICE, `${tenantId}:${clientId}:upw:${username}`);
21
+ }
22
+ async setUserPassword(tenantId, clientId, username, password) {
23
+ await keytar.setPassword(SERVICE, `${tenantId}:${clientId}:upw:${username}`, password);
24
+ }
25
+ async deleteUserPassword(tenantId, clientId, username) {
26
+ return keytar.deletePassword(SERVICE, `${tenantId}:${clientId}:upw:${username}`);
27
+ }
28
+ }
29
+ //# sourceMappingURL=CredentialStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialStore.js","sourceRoot":"","sources":["../../src/config/CredentialStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,sFAAsF;AACtF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAI9B,CAAC;AAEF,MAAM,OAAO,GAAG,MAAM,CAAC;AAEvB,MAAM,OAAO,eAAe;IAC1B,4EAA4E;IAE5E,KAAK,CAAC,eAAe,CAAC,QAAgB,EAAE,QAAgB;QACtD,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAAgB,EAAE,QAAgB,EAAE,MAAc;QACtE,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,QAAQ,IAAI,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,QAAgB,EAAE,QAAgB;QACzD,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,4EAA4E;IAC5E,uFAAuF;IAEvF,KAAK,CAAC,eAAe,CACnB,QAAgB,EAChB,QAAgB,EAChB,QAAgB;QAEhB,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,QAAgB,EAChB,QAAgB,EAChB,QAAgB,EAChB,QAAgB;QAEhB,MAAM,MAAM,CAAC,WAAW,CACtB,OAAO,EACP,GAAG,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,EAAE,EACzC,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,QAAgB,EAChB,QAAgB,EAChB,QAAgB;QAEhB,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,EAAE,CAAC,CAAC;IACnF,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ type HistoryType = 'secrets' | 'preflight';
2
+ export declare class HistoryStore {
3
+ private readonly configDir;
4
+ constructor(configDir: string);
5
+ /**
6
+ * Persist a scan or preflight result.
7
+ * Failures are silently swallowed — history is a side effect, never a blocker.
8
+ */
9
+ save(type: HistoryType, tenantId: string, data: unknown): Promise<void>;
10
+ /**
11
+ * Load the most recent saved result for the given type.
12
+ * Returns null if no history exists or on any read error.
13
+ */
14
+ loadLatest<T>(type: HistoryType, tenantId: string): Promise<T | null>;
15
+ private slotDir;
16
+ private listFiles;
17
+ private prune;
18
+ }
19
+ export {};