@zincapp/znvault-cli 2.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.
- package/README.md +310 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +660 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/apikey.d.ts +3 -0
- package/dist/commands/apikey.d.ts.map +1 -0
- package/dist/commands/apikey.js +767 -0
- package/dist/commands/apikey.js.map +1 -0
- package/dist/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +147 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +426 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/cert.d.ts +3 -0
- package/dist/commands/cert.d.ts.map +1 -0
- package/dist/commands/cert.js +398 -0
- package/dist/commands/cert.js.map +1 -0
- package/dist/commands/cluster.d.ts +3 -0
- package/dist/commands/cluster.d.ts.map +1 -0
- package/dist/commands/cluster.js +228 -0
- package/dist/commands/cluster.js.map +1 -0
- package/dist/commands/emergency.d.ts +3 -0
- package/dist/commands/emergency.d.ts.map +1 -0
- package/dist/commands/emergency.js +223 -0
- package/dist/commands/emergency.js.map +1 -0
- package/dist/commands/health.d.ts +3 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +188 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/lockdown.d.ts +3 -0
- package/dist/commands/lockdown.d.ts.map +1 -0
- package/dist/commands/lockdown.js +232 -0
- package/dist/commands/lockdown.js.map +1 -0
- package/dist/commands/permissions.d.ts +3 -0
- package/dist/commands/permissions.d.ts.map +1 -0
- package/dist/commands/permissions.js +168 -0
- package/dist/commands/permissions.js.map +1 -0
- package/dist/commands/policy.d.ts +3 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +660 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/superadmin.d.ts +3 -0
- package/dist/commands/superadmin.d.ts.map +1 -0
- package/dist/commands/superadmin.js +203 -0
- package/dist/commands/superadmin.js.map +1 -0
- package/dist/commands/tenant.d.ts +3 -0
- package/dist/commands/tenant.d.ts.map +1 -0
- package/dist/commands/tenant.js +277 -0
- package/dist/commands/tenant.js.map +1 -0
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +359 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/user.d.ts +3 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +363 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/client.d.ts +246 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +734 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/config.d.ts +130 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +342 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/db.d.ts +111 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/db.js +698 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/local.d.ts +41 -0
- package/dist/lib/local.d.ts.map +1 -0
- package/dist/lib/local.js +236 -0
- package/dist/lib/local.js.map +1 -0
- package/dist/lib/mode.d.ts +210 -0
- package/dist/lib/mode.d.ts.map +1 -0
- package/dist/lib/mode.js +389 -0
- package/dist/lib/mode.js.map +1 -0
- package/dist/lib/output.d.ts +61 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +190 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/prompts.d.ts +32 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +96 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/services/auto-update-daemon.d.ts +48 -0
- package/dist/services/auto-update-daemon.d.ts.map +1 -0
- package/dist/services/auto-update-daemon.js +296 -0
- package/dist/services/auto-update-daemon.js.map +1 -0
- package/dist/services/signature-verifier.d.ts +38 -0
- package/dist/services/signature-verifier.d.ts.map +1 -0
- package/dist/services/signature-verifier.js +209 -0
- package/dist/services/signature-verifier.js.map +1 -0
- package/dist/services/update-checker.d.ts +39 -0
- package/dist/services/update-checker.d.ts.map +1 -0
- package/dist/services/update-checker.js +198 -0
- package/dist/services/update-checker.js.map +1 -0
- package/dist/services/update-installer.d.ts +54 -0
- package/dist/services/update-installer.d.ts.map +1 -0
- package/dist/services/update-installer.js +360 -0
- package/dist/services/update-installer.js.map +1 -0
- package/dist/types/index.d.ts +411 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/update.d.ts +137 -0
- package/dist/types/update.d.ts.map +1 -0
- package/dist/types/update.js +27 -0
- package/dist/types/update.js.map +1 -0
- package/dist/utils/platform.d.ts +35 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +115 -0
- package/dist/utils/platform.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
import https from 'node:https';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import { getConfig, getCredentials, getApiKey, hasApiKey, storeCredentials, isTokenExpired, getEnvCredentials, hasEnvCredentials, } from './config.js';
|
|
4
|
+
class VaultClient {
|
|
5
|
+
baseUrl;
|
|
6
|
+
insecure;
|
|
7
|
+
timeout;
|
|
8
|
+
constructor() {
|
|
9
|
+
const config = getConfig();
|
|
10
|
+
this.baseUrl = config.url;
|
|
11
|
+
this.insecure = config.insecure;
|
|
12
|
+
this.timeout = config.timeout;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Update client configuration
|
|
16
|
+
*/
|
|
17
|
+
configure(url, insecure) {
|
|
18
|
+
if (url)
|
|
19
|
+
this.baseUrl = url;
|
|
20
|
+
if (insecure !== undefined)
|
|
21
|
+
this.insecure = insecure;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Make an HTTP request
|
|
25
|
+
*/
|
|
26
|
+
async request(options) {
|
|
27
|
+
const url = new URL(this.baseUrl);
|
|
28
|
+
url.pathname = options.path;
|
|
29
|
+
if (options.query) {
|
|
30
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
31
|
+
if (value !== undefined) {
|
|
32
|
+
url.searchParams.set(key, String(value));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const headers = {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
'Accept': 'application/json',
|
|
39
|
+
};
|
|
40
|
+
// Add authentication
|
|
41
|
+
if (!options.skipAuth) {
|
|
42
|
+
if (hasApiKey()) {
|
|
43
|
+
headers['X-API-Key'] = getApiKey();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const credentials = getCredentials();
|
|
47
|
+
if (credentials) {
|
|
48
|
+
// Check if token is expired and try to refresh
|
|
49
|
+
if (isTokenExpired() && credentials.refreshToken) {
|
|
50
|
+
await this.refreshToken();
|
|
51
|
+
}
|
|
52
|
+
const updatedCredentials = getCredentials();
|
|
53
|
+
if (updatedCredentials) {
|
|
54
|
+
headers['Authorization'] = `Bearer ${updatedCredentials.accessToken}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (hasEnvCredentials()) {
|
|
58
|
+
// Auto-login with env credentials
|
|
59
|
+
const envCreds = getEnvCredentials();
|
|
60
|
+
await this.login(envCreds.username, envCreds.password);
|
|
61
|
+
const newCredentials = getCredentials();
|
|
62
|
+
if (newCredentials) {
|
|
63
|
+
headers['Authorization'] = `Bearer ${newCredentials.accessToken}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const requestOptions = {
|
|
69
|
+
hostname: url.hostname,
|
|
70
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
71
|
+
path: url.pathname + url.search,
|
|
72
|
+
method: options.method,
|
|
73
|
+
headers,
|
|
74
|
+
timeout: this.timeout,
|
|
75
|
+
rejectUnauthorized: !this.insecure,
|
|
76
|
+
};
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
79
|
+
const req = protocol.request(requestOptions, (res) => {
|
|
80
|
+
let data = '';
|
|
81
|
+
res.on('data', (chunk) => (data += chunk));
|
|
82
|
+
res.on('end', () => {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = data ? JSON.parse(data) : {};
|
|
85
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
86
|
+
const error = parsed;
|
|
87
|
+
reject(new Error(error.message || `Request failed with status ${res.statusCode}`));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
resolve(parsed);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
95
|
+
reject(new Error(`Request failed with status ${res.statusCode}`));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
resolve(data);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
req.on('error', reject);
|
|
104
|
+
req.on('timeout', () => {
|
|
105
|
+
req.destroy();
|
|
106
|
+
reject(new Error('Request timeout'));
|
|
107
|
+
});
|
|
108
|
+
if (options.body) {
|
|
109
|
+
req.write(JSON.stringify(options.body));
|
|
110
|
+
}
|
|
111
|
+
req.end();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// ============ Authentication ============
|
|
115
|
+
async login(username, password, totp) {
|
|
116
|
+
const response = await this.request({
|
|
117
|
+
method: 'POST',
|
|
118
|
+
path: '/auth/login',
|
|
119
|
+
body: { username, password, totp },
|
|
120
|
+
skipAuth: true,
|
|
121
|
+
});
|
|
122
|
+
// Store credentials
|
|
123
|
+
storeCredentials({
|
|
124
|
+
accessToken: response.accessToken,
|
|
125
|
+
refreshToken: response.refreshToken,
|
|
126
|
+
expiresAt: Date.now() + response.expiresIn * 1000,
|
|
127
|
+
userId: response.user.id,
|
|
128
|
+
username: response.user.username,
|
|
129
|
+
role: response.user.role,
|
|
130
|
+
tenantId: response.user.tenantId,
|
|
131
|
+
});
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
async refreshToken() {
|
|
135
|
+
const credentials = getCredentials();
|
|
136
|
+
if (!credentials?.refreshToken) {
|
|
137
|
+
throw new Error('No refresh token available');
|
|
138
|
+
}
|
|
139
|
+
const response = await this.request({
|
|
140
|
+
method: 'POST',
|
|
141
|
+
path: '/auth/refresh',
|
|
142
|
+
body: { refreshToken: credentials.refreshToken },
|
|
143
|
+
skipAuth: true,
|
|
144
|
+
});
|
|
145
|
+
storeCredentials({
|
|
146
|
+
...credentials,
|
|
147
|
+
accessToken: response.accessToken,
|
|
148
|
+
refreshToken: response.refreshToken,
|
|
149
|
+
expiresAt: Date.now() + response.expiresIn * 1000,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// ============ Health ============
|
|
153
|
+
async health() {
|
|
154
|
+
return this.request({
|
|
155
|
+
method: 'GET',
|
|
156
|
+
path: '/v1/health',
|
|
157
|
+
skipAuth: true,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async leaderHealth() {
|
|
161
|
+
return this.request({
|
|
162
|
+
method: 'GET',
|
|
163
|
+
path: '/v1/health/leader',
|
|
164
|
+
skipAuth: true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// ============ Cluster ============
|
|
168
|
+
async clusterStatus() {
|
|
169
|
+
return this.request({
|
|
170
|
+
method: 'GET',
|
|
171
|
+
path: '/v1/admin/cluster',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async clusterTakeover() {
|
|
175
|
+
return this.request({
|
|
176
|
+
method: 'POST',
|
|
177
|
+
path: '/v1/admin/cluster/takeover',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async clusterPromote(nodeId) {
|
|
181
|
+
return this.request({
|
|
182
|
+
method: 'POST',
|
|
183
|
+
path: `/v1/admin/cluster/nodes/${nodeId}/promote`,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async clusterRelease() {
|
|
187
|
+
return this.request({
|
|
188
|
+
method: 'POST',
|
|
189
|
+
path: '/v1/admin/cluster/release',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async clusterMaintenance(enable) {
|
|
193
|
+
return this.request({
|
|
194
|
+
method: 'POST',
|
|
195
|
+
path: '/v1/admin/cluster/maintenance',
|
|
196
|
+
body: { enable },
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// ============ Tenants ============
|
|
200
|
+
async listTenants(options) {
|
|
201
|
+
const response = await this.request({
|
|
202
|
+
method: 'GET',
|
|
203
|
+
path: '/v1/tenants',
|
|
204
|
+
query: {
|
|
205
|
+
status: options?.status,
|
|
206
|
+
withUsage: options?.withUsage,
|
|
207
|
+
pageSize: 1000,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
return response.items;
|
|
211
|
+
}
|
|
212
|
+
async createTenant(data) {
|
|
213
|
+
return this.request({
|
|
214
|
+
method: 'POST',
|
|
215
|
+
path: '/v1/tenants',
|
|
216
|
+
body: data,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
async getTenant(id, withUsage) {
|
|
220
|
+
return this.request({
|
|
221
|
+
method: 'GET',
|
|
222
|
+
path: `/v1/tenants/${id}`,
|
|
223
|
+
query: { withUsage },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async updateTenant(id, data) {
|
|
227
|
+
return this.request({
|
|
228
|
+
method: 'PATCH',
|
|
229
|
+
path: `/v1/tenants/${id}`,
|
|
230
|
+
body: data,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async deleteTenant(id) {
|
|
234
|
+
await this.request({
|
|
235
|
+
method: 'DELETE',
|
|
236
|
+
path: `/v1/tenants/${id}`,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
async getTenantUsage(id) {
|
|
240
|
+
return this.request({
|
|
241
|
+
method: 'GET',
|
|
242
|
+
path: `/v1/tenants/${id}/usage`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// ============ Users ============
|
|
246
|
+
async listUsers(options) {
|
|
247
|
+
const response = await this.request({
|
|
248
|
+
method: 'GET',
|
|
249
|
+
path: '/v1/users',
|
|
250
|
+
query: {
|
|
251
|
+
tenantId: options?.tenantId,
|
|
252
|
+
role: options?.role,
|
|
253
|
+
status: options?.status,
|
|
254
|
+
pageSize: 1000,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
return response.items;
|
|
258
|
+
}
|
|
259
|
+
async createUser(data) {
|
|
260
|
+
return this.request({
|
|
261
|
+
method: 'POST',
|
|
262
|
+
path: '/v1/users',
|
|
263
|
+
body: data,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
async getUser(id) {
|
|
267
|
+
return this.request({
|
|
268
|
+
method: 'GET',
|
|
269
|
+
path: `/v1/users/${id}`,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async updateUser(id, data) {
|
|
273
|
+
return this.request({
|
|
274
|
+
method: 'PUT',
|
|
275
|
+
path: `/v1/users/${id}`,
|
|
276
|
+
body: data,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async deleteUser(id) {
|
|
280
|
+
await this.request({
|
|
281
|
+
method: 'DELETE',
|
|
282
|
+
path: `/v1/users/${id}`,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async unlockUser(id) {
|
|
286
|
+
return this.request({
|
|
287
|
+
method: 'PUT',
|
|
288
|
+
path: `/v1/users/${id}`,
|
|
289
|
+
body: { status: 'active', failedAttempts: 0, lockedUntil: null },
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
async resetUserPassword(id, newPassword) {
|
|
293
|
+
return this.request({
|
|
294
|
+
method: 'POST',
|
|
295
|
+
path: `/v1/users/${id}/reset-password`,
|
|
296
|
+
body: { newPassword },
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
async disableUserTotp(id) {
|
|
300
|
+
return this.request({
|
|
301
|
+
method: 'POST',
|
|
302
|
+
path: `/v1/users/${id}/totp/disable`,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
// ============ Superadmins ============
|
|
306
|
+
async listSuperadmins() {
|
|
307
|
+
const users = await this.listUsers({ role: 'superadmin' });
|
|
308
|
+
return users.map(u => ({
|
|
309
|
+
id: u.id,
|
|
310
|
+
username: u.username,
|
|
311
|
+
email: u.email,
|
|
312
|
+
status: u.status,
|
|
313
|
+
totpEnabled: u.totpEnabled,
|
|
314
|
+
failedAttempts: u.failedAttempts,
|
|
315
|
+
lockedUntil: u.lockedUntil,
|
|
316
|
+
lastLogin: u.lastLogin,
|
|
317
|
+
createdAt: u.createdAt,
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
async createSuperadmin(data) {
|
|
321
|
+
return this.request({
|
|
322
|
+
method: 'POST',
|
|
323
|
+
path: '/v1/superadmins',
|
|
324
|
+
body: data,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
async resetSuperadminPassword(username, password) {
|
|
328
|
+
return this.request({
|
|
329
|
+
method: 'POST',
|
|
330
|
+
path: `/v1/superadmins/${username}/password`,
|
|
331
|
+
body: { password },
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
async unlockSuperadmin(username) {
|
|
335
|
+
return this.request({
|
|
336
|
+
method: 'POST',
|
|
337
|
+
path: `/v1/superadmins/${username}/unlock`,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
async disableSuperadmin(username) {
|
|
341
|
+
return this.request({
|
|
342
|
+
method: 'POST',
|
|
343
|
+
path: `/v1/superadmins/${username}/disable`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
async enableSuperadmin(username) {
|
|
347
|
+
return this.request({
|
|
348
|
+
method: 'POST',
|
|
349
|
+
path: `/v1/superadmins/${username}/enable`,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
// ============ Lockdown ============
|
|
353
|
+
async getLockdownStatus() {
|
|
354
|
+
return this.request({
|
|
355
|
+
method: 'GET',
|
|
356
|
+
path: '/v1/admin/lockdown/status',
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
async triggerLockdown(level, reason) {
|
|
360
|
+
return this.request({
|
|
361
|
+
method: 'POST',
|
|
362
|
+
path: '/v1/admin/lockdown/trigger',
|
|
363
|
+
body: { level, reason },
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
async clearLockdown(reason) {
|
|
367
|
+
return this.request({
|
|
368
|
+
method: 'POST',
|
|
369
|
+
path: '/v1/admin/lockdown/clear',
|
|
370
|
+
body: { reason },
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async getLockdownHistory(limit) {
|
|
374
|
+
const response = await this.request({
|
|
375
|
+
method: 'GET',
|
|
376
|
+
path: '/v1/admin/lockdown/history',
|
|
377
|
+
query: { limit: limit || 50 },
|
|
378
|
+
});
|
|
379
|
+
return response.items;
|
|
380
|
+
}
|
|
381
|
+
async getThreats(options) {
|
|
382
|
+
const response = await this.request({
|
|
383
|
+
method: 'GET',
|
|
384
|
+
path: '/v1/admin/lockdown/threats',
|
|
385
|
+
query: {
|
|
386
|
+
category: options?.category,
|
|
387
|
+
since: options?.since,
|
|
388
|
+
limit: options?.limit || 100,
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
return response.items;
|
|
392
|
+
}
|
|
393
|
+
// ============ Audit ============
|
|
394
|
+
async listAudit(options) {
|
|
395
|
+
const response = await this.request({
|
|
396
|
+
method: 'GET',
|
|
397
|
+
path: '/v1/audit',
|
|
398
|
+
query: {
|
|
399
|
+
client_cn: options?.user,
|
|
400
|
+
action: options?.action,
|
|
401
|
+
start_date: options?.startDate,
|
|
402
|
+
end_date: options?.endDate,
|
|
403
|
+
limit: options?.limit || 100,
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
return response.items;
|
|
407
|
+
}
|
|
408
|
+
async verifyAuditChain() {
|
|
409
|
+
return this.request({
|
|
410
|
+
method: 'GET',
|
|
411
|
+
path: '/v1/audit/verify',
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async exportAudit(options) {
|
|
415
|
+
return this.request({
|
|
416
|
+
method: 'GET',
|
|
417
|
+
path: '/v1/audit',
|
|
418
|
+
query: {
|
|
419
|
+
format: options?.format || 'json',
|
|
420
|
+
start_date: options?.startDate,
|
|
421
|
+
end_date: options?.endDate,
|
|
422
|
+
limit: 10000,
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
// ============ API Keys (Independent, tenant-scoped) ============
|
|
427
|
+
async createApiKey(data) {
|
|
428
|
+
return this.request({
|
|
429
|
+
method: 'POST',
|
|
430
|
+
path: '/auth/api-keys',
|
|
431
|
+
query: data.tenantId ? { tenantId: data.tenantId } : undefined,
|
|
432
|
+
body: {
|
|
433
|
+
name: data.name,
|
|
434
|
+
description: data.description,
|
|
435
|
+
expiresInDays: data.expiresInDays,
|
|
436
|
+
permissions: data.permissions,
|
|
437
|
+
ipAllowlist: data.ipAllowlist,
|
|
438
|
+
conditions: data.conditions,
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
async listApiKeys(tenantId) {
|
|
443
|
+
return this.request({
|
|
444
|
+
method: 'GET',
|
|
445
|
+
path: '/auth/api-keys',
|
|
446
|
+
query: tenantId ? { tenantId } : undefined,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
async getApiKey(id, tenantId) {
|
|
450
|
+
return this.request({
|
|
451
|
+
method: 'GET',
|
|
452
|
+
path: `/auth/api-keys/${id}`,
|
|
453
|
+
query: tenantId ? { tenantId } : undefined,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
async deleteApiKey(id, tenantId) {
|
|
457
|
+
await this.request({
|
|
458
|
+
method: 'DELETE',
|
|
459
|
+
path: `/auth/api-keys/${id}`,
|
|
460
|
+
query: tenantId ? { tenantId } : undefined,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
async rotateApiKey(id, name, tenantId) {
|
|
464
|
+
return this.request({
|
|
465
|
+
method: 'POST',
|
|
466
|
+
path: `/auth/api-keys/${id}/rotate`,
|
|
467
|
+
query: tenantId ? { tenantId } : undefined,
|
|
468
|
+
body: name ? { name } : {},
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async updateApiKeyPermissions(id, permissions, tenantId) {
|
|
472
|
+
return this.request({
|
|
473
|
+
method: 'PATCH',
|
|
474
|
+
path: `/auth/api-keys/${id}/permissions`,
|
|
475
|
+
query: tenantId ? { tenantId } : undefined,
|
|
476
|
+
body: { permissions },
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
async updateApiKeyConditions(id, conditions, tenantId) {
|
|
480
|
+
const response = await this.request({
|
|
481
|
+
method: 'PATCH',
|
|
482
|
+
path: `/auth/api-keys/${id}/conditions`,
|
|
483
|
+
query: tenantId ? { tenantId } : undefined,
|
|
484
|
+
body: { conditions },
|
|
485
|
+
});
|
|
486
|
+
return response.apiKey;
|
|
487
|
+
}
|
|
488
|
+
async setApiKeyEnabled(id, enabled, tenantId) {
|
|
489
|
+
const response = await this.request({
|
|
490
|
+
method: 'PATCH',
|
|
491
|
+
path: `/auth/api-keys/${id}/enabled`,
|
|
492
|
+
query: tenantId ? { tenantId } : undefined,
|
|
493
|
+
body: { enabled },
|
|
494
|
+
});
|
|
495
|
+
return response.apiKey;
|
|
496
|
+
}
|
|
497
|
+
// =========================================================================
|
|
498
|
+
// Permissions API
|
|
499
|
+
// =========================================================================
|
|
500
|
+
/**
|
|
501
|
+
* Get all available permissions from the database
|
|
502
|
+
* This is the single source of truth for valid permission IDs
|
|
503
|
+
*/
|
|
504
|
+
async getPermissions(category) {
|
|
505
|
+
return this.request({
|
|
506
|
+
method: 'GET',
|
|
507
|
+
path: '/v1/permissions',
|
|
508
|
+
query: category ? { category } : undefined,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Validate a list of permission IDs against the database
|
|
513
|
+
*/
|
|
514
|
+
async validatePermissions(permissions) {
|
|
515
|
+
return this.request({
|
|
516
|
+
method: 'POST',
|
|
517
|
+
path: '/v1/permissions/validate',
|
|
518
|
+
body: { permissions },
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
async getApiKeyPolicies(id, tenantId) {
|
|
522
|
+
return this.request({
|
|
523
|
+
method: 'GET',
|
|
524
|
+
path: `/auth/api-keys/${id}/policies`,
|
|
525
|
+
query: tenantId ? { tenantId } : undefined,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
async attachApiKeyPolicy(keyId, policyId, tenantId) {
|
|
529
|
+
return this.request({
|
|
530
|
+
method: 'POST',
|
|
531
|
+
path: `/auth/api-keys/${keyId}/policies/${policyId}`,
|
|
532
|
+
query: tenantId ? { tenantId } : undefined,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
async detachApiKeyPolicy(keyId, policyId, tenantId) {
|
|
536
|
+
return this.request({
|
|
537
|
+
method: 'DELETE',
|
|
538
|
+
path: `/auth/api-keys/${keyId}/policies/${policyId}`,
|
|
539
|
+
query: tenantId ? { tenantId } : undefined,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
async getApiKeySelf() {
|
|
543
|
+
return this.request({
|
|
544
|
+
method: 'GET',
|
|
545
|
+
path: '/auth/api-keys/self',
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
async rotateApiKeySelf(name) {
|
|
549
|
+
return this.request({
|
|
550
|
+
method: 'POST',
|
|
551
|
+
path: '/auth/api-keys/self/rotate',
|
|
552
|
+
body: name ? { name } : {},
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
// ============ ABAC Policies ============
|
|
556
|
+
async listPolicies(options) {
|
|
557
|
+
return this.request({
|
|
558
|
+
method: 'GET',
|
|
559
|
+
path: '/v1/policies',
|
|
560
|
+
query: {
|
|
561
|
+
tenantId: options?.tenantId,
|
|
562
|
+
enabled: options?.enabled,
|
|
563
|
+
effect: options?.effect,
|
|
564
|
+
search: options?.search,
|
|
565
|
+
page: options?.page,
|
|
566
|
+
pageSize: options?.pageSize || 100,
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
async getPolicy(id) {
|
|
571
|
+
return this.request({
|
|
572
|
+
method: 'GET',
|
|
573
|
+
path: `/v1/policies/${id}`,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
async createPolicy(data) {
|
|
577
|
+
return this.request({
|
|
578
|
+
method: 'POST',
|
|
579
|
+
path: '/v1/policies',
|
|
580
|
+
body: data,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
async updatePolicy(id, data) {
|
|
584
|
+
return this.request({
|
|
585
|
+
method: 'PATCH',
|
|
586
|
+
path: `/v1/policies/${id}`,
|
|
587
|
+
body: data,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
async deletePolicy(id) {
|
|
591
|
+
await this.request({
|
|
592
|
+
method: 'DELETE',
|
|
593
|
+
path: `/v1/policies/${id}`,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
async togglePolicy(id, enabled) {
|
|
597
|
+
return this.request({
|
|
598
|
+
method: 'POST',
|
|
599
|
+
path: `/v1/policies/${id}/toggle`,
|
|
600
|
+
body: { enabled },
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
async validatePolicy(policy) {
|
|
604
|
+
return this.request({
|
|
605
|
+
method: 'POST',
|
|
606
|
+
path: '/v1/policies/validate',
|
|
607
|
+
body: policy,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
async getPolicyAttachments(policyId) {
|
|
611
|
+
return this.request({
|
|
612
|
+
method: 'GET',
|
|
613
|
+
path: `/v1/policies/${policyId}/attachments`,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
async attachPolicyToUser(policyId, userId) {
|
|
617
|
+
return this.request({
|
|
618
|
+
method: 'POST',
|
|
619
|
+
path: `/v1/policies/${policyId}/attach/user`,
|
|
620
|
+
body: { userId },
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
async attachPolicyToRole(policyId, roleId) {
|
|
624
|
+
return this.request({
|
|
625
|
+
method: 'POST',
|
|
626
|
+
path: `/v1/policies/${policyId}/attach/role`,
|
|
627
|
+
body: { roleId },
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
async detachPolicyFromUser(policyId, userId) {
|
|
631
|
+
return this.request({
|
|
632
|
+
method: 'DELETE',
|
|
633
|
+
path: `/v1/policies/${policyId}/attach/user/${userId}`,
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
async detachPolicyFromRole(policyId, roleId) {
|
|
637
|
+
return this.request({
|
|
638
|
+
method: 'DELETE',
|
|
639
|
+
path: `/v1/policies/${policyId}/attach/role/${roleId}`,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
async getUserPolicies(userId) {
|
|
643
|
+
const response = await this.request({
|
|
644
|
+
method: 'GET',
|
|
645
|
+
path: `/v1/users/${userId}/policies`,
|
|
646
|
+
});
|
|
647
|
+
return response.policies;
|
|
648
|
+
}
|
|
649
|
+
async getRolePolicies(roleId) {
|
|
650
|
+
const response = await this.request({
|
|
651
|
+
method: 'GET',
|
|
652
|
+
path: `/v1/roles/${roleId}/policies`,
|
|
653
|
+
});
|
|
654
|
+
return response.policies;
|
|
655
|
+
}
|
|
656
|
+
async testPolicy(request) {
|
|
657
|
+
return this.request({
|
|
658
|
+
method: 'POST',
|
|
659
|
+
path: '/v1/policies/test',
|
|
660
|
+
body: request,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
// ============ Generic methods for arbitrary endpoints ============
|
|
664
|
+
/**
|
|
665
|
+
* Generic GET request
|
|
666
|
+
*/
|
|
667
|
+
async get(path) {
|
|
668
|
+
return this.request({ method: 'GET', path });
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Generic POST request
|
|
672
|
+
*/
|
|
673
|
+
async post(path, body) {
|
|
674
|
+
return this.request({ method: 'POST', path, body });
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Generic DELETE request
|
|
678
|
+
*/
|
|
679
|
+
async delete(path) {
|
|
680
|
+
return this.request({ method: 'DELETE', path });
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Generic PATCH request
|
|
684
|
+
*/
|
|
685
|
+
async patch(path, body) {
|
|
686
|
+
return this.request({ method: 'PATCH', path, body });
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Get WebSocket URL for a given endpoint path
|
|
690
|
+
*/
|
|
691
|
+
getWebSocketUrl(wsPath) {
|
|
692
|
+
const url = new URL(this.baseUrl);
|
|
693
|
+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
694
|
+
url.pathname = wsPath;
|
|
695
|
+
return url.toString();
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Get authentication headers for WebSocket connection
|
|
699
|
+
*/
|
|
700
|
+
async getAuthHeaders() {
|
|
701
|
+
const headers = {};
|
|
702
|
+
if (hasApiKey()) {
|
|
703
|
+
headers['X-API-Key'] = getApiKey();
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
const credentials = getCredentials();
|
|
707
|
+
if (credentials) {
|
|
708
|
+
// Check if token is expired and try to refresh
|
|
709
|
+
if (isTokenExpired() && credentials.refreshToken) {
|
|
710
|
+
await this.refreshToken();
|
|
711
|
+
}
|
|
712
|
+
const updatedCredentials = getCredentials();
|
|
713
|
+
if (updatedCredentials) {
|
|
714
|
+
headers['Authorization'] = `Bearer ${updatedCredentials.accessToken}`;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else if (hasEnvCredentials()) {
|
|
718
|
+
// Auto-login with env credentials
|
|
719
|
+
const envCreds = getEnvCredentials();
|
|
720
|
+
await this.login(envCreds.username, envCreds.password);
|
|
721
|
+
const newCredentials = getCredentials();
|
|
722
|
+
if (newCredentials) {
|
|
723
|
+
headers['Authorization'] = `Bearer ${newCredentials.accessToken}`;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return headers;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
// Export singleton instance
|
|
731
|
+
export const client = new VaultClient();
|
|
732
|
+
// Export class for testing
|
|
733
|
+
export { VaultClient };
|
|
734
|
+
//# sourceMappingURL=client.js.map
|