@indigoai-us/hq-cli 5.53.1 → 5.54.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/CHANGELOG.md +12 -0
- package/dist/commands/secrets.js +45 -21
- package/package.json +1 -1
- package/src/commands/secrets.test.ts +86 -0
- package/src/commands/secrets.ts +56 -19
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.54.0]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`hq secrets share/unshare … @all` — share a secret with the entire company.**
|
|
10
|
+
`--with @all` (share) and `--from @all` (unshare) now grant or revoke a
|
|
11
|
+
company-wide secret ACL — every active member of the company receives the
|
|
12
|
+
permission — instead of being refused. Output reads `@all (entire company)`.
|
|
13
|
+
Backed by vault-service company-wide ACL support; secrets with no explicit ACL
|
|
14
|
+
remain open to all members as before, so `@all` matters for re-opening a
|
|
15
|
+
restricted secret to the whole company.
|
|
16
|
+
|
|
5
17
|
## [5.53.1]
|
|
6
18
|
|
|
7
19
|
### Changed
|
package/dist/commands/secrets.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1dfcad18-3626-5c68-8b4c-0c147e896113")}catch(e){}}();
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import * as readline from "node:readline";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
@@ -7,7 +7,7 @@ import * as nodePath from "node:path";
|
|
|
7
7
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
8
8
|
import { DEFAULT_SECRETS_CACHE_TTL_MS, readCache, writeCache, removeCacheEntry, clearAllCache, } from "../utils/secrets-cache.js";
|
|
9
9
|
import { computeSha256 } from "../utils/integrity.js";
|
|
10
|
-
import { SECRET_NAME_PATTERN, GROUP_ID_PATTERN } from "./_patterns.js";
|
|
10
|
+
import { SECRET_NAME_PATTERN, GROUP_ID_PATTERN, EMAIL_PATTERN } from "./_patterns.js";
|
|
11
11
|
import { describeSecretsScope, formatSecretSaved, formatSecretsListEmpty, formatSecretsListHeader, } from "./secrets-scope.js";
|
|
12
12
|
import { vaultApiFetch, getCompanyUid, getEntityUid, } from "../utils/vault-api.js";
|
|
13
13
|
export { vaultApiFetch, getCompanyUid, getEntityUid };
|
|
@@ -131,6 +131,27 @@ function promptSecretInteractively() {
|
|
|
131
131
|
// large --only list is chunked client-side rather than 400'd whole by the
|
|
132
132
|
// server (the legacy per-key GET path had no such cap).
|
|
133
133
|
const MAX_BATCH_NAMES = 100;
|
|
134
|
+
function parseSecretAclPrincipal(principal) {
|
|
135
|
+
const p = principal.trim();
|
|
136
|
+
if (p === "@all") {
|
|
137
|
+
// Company-wide grant: every active member of the company. The server
|
|
138
|
+
// carries no specific grantee id for this axis, so granteeId is empty.
|
|
139
|
+
return { granteeType: "company-wide", granteeId: "" };
|
|
140
|
+
}
|
|
141
|
+
if (EMAIL_PATTERN.test(p)) {
|
|
142
|
+
return { granteeType: "email", granteeId: p.toLowerCase() };
|
|
143
|
+
}
|
|
144
|
+
if (GROUP_ID_PATTERN.test(p)) {
|
|
145
|
+
return { granteeType: "group", granteeId: p };
|
|
146
|
+
}
|
|
147
|
+
console.error(chalk.red(`Invalid principal '${principal}': must be '@all', an email address, or a group id matching grp_<alphanumeric, underscore, hyphen>`));
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function describeSecretAclPrincipal(principal) {
|
|
151
|
+
return principal.granteeType === "company-wide"
|
|
152
|
+
? "@all (entire company)"
|
|
153
|
+
: principal.granteeId;
|
|
154
|
+
}
|
|
134
155
|
function normalizeSecretTier(tier) {
|
|
135
156
|
return tier === "sensitive" || tier === "nuclear" ? tier : "standard";
|
|
136
157
|
}
|
|
@@ -960,8 +981,8 @@ export function registerSecretsCommand(program) {
|
|
|
960
981
|
});
|
|
961
982
|
secrets
|
|
962
983
|
.command("share <path>")
|
|
963
|
-
.description("Share a secret with
|
|
964
|
-
.requiredOption("--with <principal>", "Email address
|
|
984
|
+
.description("Share a secret with an email address, group, or the entire company (@all)")
|
|
985
|
+
.requiredOption("--with <principal>", "Email address, group id, or @all (entire company) to share with")
|
|
965
986
|
.requiredOption("--permission <level>", "Permission level: read | write | admin")
|
|
966
987
|
.action(async (path, opts) => {
|
|
967
988
|
try {
|
|
@@ -974,20 +995,22 @@ export function registerSecretsCommand(program) {
|
|
|
974
995
|
console.error(chalk.red(`Invalid permission '${opts.permission}': must be one of read, write, admin`));
|
|
975
996
|
process.exit(1);
|
|
976
997
|
}
|
|
977
|
-
const
|
|
978
|
-
if (!
|
|
979
|
-
console.error(chalk.red(`Invalid principal '${opts.with}': must be an email address or a group id matching grp_<alphanumeric>`));
|
|
998
|
+
const principal = parseSecretAclPrincipal(opts.with);
|
|
999
|
+
if (!principal) {
|
|
980
1000
|
process.exit(1);
|
|
981
1001
|
}
|
|
982
|
-
const granteeType = isEmail ? "email" : "group";
|
|
983
|
-
const granteeId = opts.with;
|
|
984
1002
|
const token = await ensureCognitoToken();
|
|
985
1003
|
const companyUid = await getEntityUid(token, scopeOpts(secrets.opts()));
|
|
986
1004
|
const res = await vaultApiFetch({
|
|
987
1005
|
token,
|
|
988
1006
|
path: `/secrets/${encodeURIComponent(companyUid)}/acl/grant`,
|
|
989
1007
|
method: "POST",
|
|
990
|
-
body: {
|
|
1008
|
+
body: {
|
|
1009
|
+
path,
|
|
1010
|
+
granteeType: principal.granteeType,
|
|
1011
|
+
granteeId: principal.granteeId,
|
|
1012
|
+
permission: opts.permission,
|
|
1013
|
+
},
|
|
991
1014
|
});
|
|
992
1015
|
if (!res.ok) {
|
|
993
1016
|
const body = await res.json().catch(() => ({}));
|
|
@@ -1011,7 +1034,7 @@ export function registerSecretsCommand(program) {
|
|
|
1011
1034
|
}
|
|
1012
1035
|
process.exit(1);
|
|
1013
1036
|
}
|
|
1014
|
-
console.log(chalk.green(`Shared '${path}' with ${
|
|
1037
|
+
console.log(chalk.green(`Shared '${path}' with ${describeSecretAclPrincipal(principal)} (${opts.permission})`));
|
|
1015
1038
|
}
|
|
1016
1039
|
catch (err) {
|
|
1017
1040
|
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
@@ -1021,7 +1044,7 @@ export function registerSecretsCommand(program) {
|
|
|
1021
1044
|
secrets
|
|
1022
1045
|
.command("unshare <path>")
|
|
1023
1046
|
.description("Remove a grant from a secret")
|
|
1024
|
-
.requiredOption("--from <principal>", "Email address
|
|
1047
|
+
.requiredOption("--from <principal>", "Email address, group id, or @all (entire company) to remove")
|
|
1025
1048
|
.action(async (path, opts) => {
|
|
1026
1049
|
try {
|
|
1027
1050
|
rejectIfPersonal(secrets.opts(), "unshare");
|
|
@@ -1029,20 +1052,21 @@ export function registerSecretsCommand(program) {
|
|
|
1029
1052
|
console.error(chalk.red(`Invalid secret path '${path}': must match ^[A-Z][A-Z0-9_]*(/[A-Z][A-Z0-9_]+)*$ (e.g. MY_KEY or PROD/DB_PASSWORD)`));
|
|
1030
1053
|
process.exit(1);
|
|
1031
1054
|
}
|
|
1032
|
-
const
|
|
1033
|
-
if (!
|
|
1034
|
-
console.error(chalk.red(`Invalid principal '${opts.from}': must be an email address or a group id matching grp_<alphanumeric>`));
|
|
1055
|
+
const principal = parseSecretAclPrincipal(opts.from);
|
|
1056
|
+
if (!principal) {
|
|
1035
1057
|
process.exit(1);
|
|
1036
1058
|
}
|
|
1037
|
-
const granteeType = isEmailFrom ? "email" : "group";
|
|
1038
|
-
const granteeId = opts.from;
|
|
1039
1059
|
const token = await ensureCognitoToken();
|
|
1040
1060
|
const companyUid = await getEntityUid(token, scopeOpts(secrets.opts()));
|
|
1041
1061
|
const res = await vaultApiFetch({
|
|
1042
1062
|
token,
|
|
1043
1063
|
path: `/secrets/${encodeURIComponent(companyUid)}/acl/revoke`,
|
|
1044
1064
|
method: "POST",
|
|
1045
|
-
body: {
|
|
1065
|
+
body: {
|
|
1066
|
+
path,
|
|
1067
|
+
granteeType: principal.granteeType,
|
|
1068
|
+
granteeId: principal.granteeId,
|
|
1069
|
+
},
|
|
1046
1070
|
});
|
|
1047
1071
|
if (!res.ok) {
|
|
1048
1072
|
const body = await res.json().catch(() => ({}));
|
|
@@ -1053,7 +1077,7 @@ export function registerSecretsCommand(program) {
|
|
|
1053
1077
|
console.error(chalk.red("Not authorized to modify this secret's ACL"));
|
|
1054
1078
|
}
|
|
1055
1079
|
else if (res.status === 404) {
|
|
1056
|
-
console.log(chalk.green(`Grant already absent for '${path}' / ${
|
|
1080
|
+
console.log(chalk.green(`Grant already absent for '${path}' / ${describeSecretAclPrincipal(principal)}`));
|
|
1057
1081
|
return;
|
|
1058
1082
|
}
|
|
1059
1083
|
else if (res.status >= 500) {
|
|
@@ -1064,7 +1088,7 @@ export function registerSecretsCommand(program) {
|
|
|
1064
1088
|
}
|
|
1065
1089
|
process.exit(1);
|
|
1066
1090
|
}
|
|
1067
|
-
console.log(chalk.green(`Removed grant for ${
|
|
1091
|
+
console.log(chalk.green(`Removed grant for ${describeSecretAclPrincipal(principal)} on '${path}'`));
|
|
1068
1092
|
}
|
|
1069
1093
|
catch (err) {
|
|
1070
1094
|
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
@@ -1163,4 +1187,4 @@ export function registerSecretsCommand(program) {
|
|
|
1163
1187
|
});
|
|
1164
1188
|
}
|
|
1165
1189
|
//# sourceMappingURL=secrets.js.map
|
|
1166
|
-
//# debugId=
|
|
1190
|
+
//# debugId=1dfcad18-3626-5c68-8b4c-0c147e896113
|
package/package.json
CHANGED
|
@@ -273,6 +273,92 @@ describe("secrets generate-link", () => {
|
|
|
273
273
|
});
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
+
describe("secrets share ACL principals", () => {
|
|
277
|
+
it("grants a secret to a group principal", async () => {
|
|
278
|
+
vi.mocked(vaultApiFetch).mockResolvedValueOnce(jsonRes({ ok: true }));
|
|
279
|
+
|
|
280
|
+
const program = buildProgram();
|
|
281
|
+
await program.parseAsync([
|
|
282
|
+
"node",
|
|
283
|
+
"hq",
|
|
284
|
+
"secrets",
|
|
285
|
+
"share",
|
|
286
|
+
"MY_KEY",
|
|
287
|
+
"--with",
|
|
288
|
+
"grp_ops",
|
|
289
|
+
"--permission",
|
|
290
|
+
"read",
|
|
291
|
+
]);
|
|
292
|
+
|
|
293
|
+
expect(vaultApiFetch).toHaveBeenCalledWith({
|
|
294
|
+
token: "test-token",
|
|
295
|
+
path: "/secrets/prs_alice/acl/grant",
|
|
296
|
+
method: "POST",
|
|
297
|
+
body: {
|
|
298
|
+
path: "MY_KEY",
|
|
299
|
+
granteeType: "group",
|
|
300
|
+
granteeId: "grp_ops",
|
|
301
|
+
permission: "read",
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("grants a secret company-wide with @all", async () => {
|
|
307
|
+
vi.mocked(vaultApiFetch).mockResolvedValueOnce(jsonRes({ ok: true }));
|
|
308
|
+
|
|
309
|
+
const program = buildProgram();
|
|
310
|
+
await program.parseAsync([
|
|
311
|
+
"node",
|
|
312
|
+
"hq",
|
|
313
|
+
"secrets",
|
|
314
|
+
"share",
|
|
315
|
+
"MY_KEY",
|
|
316
|
+
"--with",
|
|
317
|
+
"@all",
|
|
318
|
+
"--permission",
|
|
319
|
+
"read",
|
|
320
|
+
]);
|
|
321
|
+
|
|
322
|
+
expect(vaultApiFetch).toHaveBeenCalledWith({
|
|
323
|
+
token: "test-token",
|
|
324
|
+
path: "/secrets/prs_alice/acl/grant",
|
|
325
|
+
method: "POST",
|
|
326
|
+
body: {
|
|
327
|
+
path: "MY_KEY",
|
|
328
|
+
granteeType: "company-wide",
|
|
329
|
+
granteeId: "",
|
|
330
|
+
permission: "read",
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("revokes a company-wide grant with @all", async () => {
|
|
336
|
+
vi.mocked(vaultApiFetch).mockResolvedValueOnce(jsonRes({ ok: true }));
|
|
337
|
+
|
|
338
|
+
const program = buildProgram();
|
|
339
|
+
await program.parseAsync([
|
|
340
|
+
"node",
|
|
341
|
+
"hq",
|
|
342
|
+
"secrets",
|
|
343
|
+
"unshare",
|
|
344
|
+
"MY_KEY",
|
|
345
|
+
"--from",
|
|
346
|
+
"@all",
|
|
347
|
+
]);
|
|
348
|
+
|
|
349
|
+
expect(vaultApiFetch).toHaveBeenCalledWith({
|
|
350
|
+
token: "test-token",
|
|
351
|
+
path: "/secrets/prs_alice/acl/revoke",
|
|
352
|
+
method: "POST",
|
|
353
|
+
body: {
|
|
354
|
+
path: "MY_KEY",
|
|
355
|
+
granteeType: "company-wide",
|
|
356
|
+
granteeId: "",
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
276
362
|
// HQ-4H — `hq secrets exec`/`env` load secrets through the BATCH-LOAD endpoint
|
|
277
363
|
// (`POST /secrets/{uid}/load`), which returns 200 + per-name `errors[]` and
|
|
278
364
|
// never fires the server's "Secret not found" Sentry warning. The old per-key
|
package/src/commands/secrets.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
clearAllCache,
|
|
13
13
|
} from "../utils/secrets-cache.js";
|
|
14
14
|
import { computeSha256 } from "../utils/integrity.js";
|
|
15
|
-
import { SECRET_NAME_PATTERN, GROUP_ID_PATTERN } from "./_patterns.js";
|
|
15
|
+
import { SECRET_NAME_PATTERN, GROUP_ID_PATTERN, EMAIL_PATTERN } from "./_patterns.js";
|
|
16
16
|
import {
|
|
17
17
|
describeSecretsScope,
|
|
18
18
|
formatSecretSaved,
|
|
@@ -240,6 +240,40 @@ interface SecretPolicyResponse {
|
|
|
240
240
|
scripts?: SecretPolicyScript[];
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
interface SecretAclPrincipal {
|
|
244
|
+
granteeType: "email" | "group" | "company-wide";
|
|
245
|
+
granteeId: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function parseSecretAclPrincipal(
|
|
249
|
+
principal: string,
|
|
250
|
+
): SecretAclPrincipal | null {
|
|
251
|
+
const p = principal.trim();
|
|
252
|
+
if (p === "@all") {
|
|
253
|
+
// Company-wide grant: every active member of the company. The server
|
|
254
|
+
// carries no specific grantee id for this axis, so granteeId is empty.
|
|
255
|
+
return { granteeType: "company-wide", granteeId: "" };
|
|
256
|
+
}
|
|
257
|
+
if (EMAIL_PATTERN.test(p)) {
|
|
258
|
+
return { granteeType: "email", granteeId: p.toLowerCase() };
|
|
259
|
+
}
|
|
260
|
+
if (GROUP_ID_PATTERN.test(p)) {
|
|
261
|
+
return { granteeType: "group", granteeId: p };
|
|
262
|
+
}
|
|
263
|
+
console.error(
|
|
264
|
+
chalk.red(
|
|
265
|
+
`Invalid principal '${principal}': must be '@all', an email address, or a group id matching grp_<alphanumeric, underscore, hyphen>`,
|
|
266
|
+
),
|
|
267
|
+
);
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function describeSecretAclPrincipal(principal: SecretAclPrincipal): string {
|
|
272
|
+
return principal.granteeType === "company-wide"
|
|
273
|
+
? "@all (entire company)"
|
|
274
|
+
: principal.granteeId;
|
|
275
|
+
}
|
|
276
|
+
|
|
243
277
|
function normalizeSecretTier(tier?: string): SecretTier {
|
|
244
278
|
return tier === "sensitive" || tier === "nuclear" ? tier : "standard";
|
|
245
279
|
}
|
|
@@ -1365,8 +1399,8 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1365
1399
|
|
|
1366
1400
|
secrets
|
|
1367
1401
|
.command("share <path>")
|
|
1368
|
-
.description("Share a secret with
|
|
1369
|
-
.requiredOption("--with <principal>", "Email address
|
|
1402
|
+
.description("Share a secret with an email address, group, or the entire company (@all)")
|
|
1403
|
+
.requiredOption("--with <principal>", "Email address, group id, or @all (entire company) to share with")
|
|
1370
1404
|
.requiredOption("--permission <level>", "Permission level: read | write | admin")
|
|
1371
1405
|
.action(async (path: string, opts: { with: string; permission: string }) => {
|
|
1372
1406
|
try {
|
|
@@ -1382,13 +1416,10 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1382
1416
|
process.exit(1);
|
|
1383
1417
|
}
|
|
1384
1418
|
|
|
1385
|
-
const
|
|
1386
|
-
if (!
|
|
1387
|
-
console.error(chalk.red(`Invalid principal '${opts.with}': must be an email address or a group id matching grp_<alphanumeric>`));
|
|
1419
|
+
const principal = parseSecretAclPrincipal(opts.with);
|
|
1420
|
+
if (!principal) {
|
|
1388
1421
|
process.exit(1);
|
|
1389
1422
|
}
|
|
1390
|
-
const granteeType = isEmail ? "email" : "group";
|
|
1391
|
-
const granteeId = opts.with;
|
|
1392
1423
|
|
|
1393
1424
|
const token = await ensureCognitoToken();
|
|
1394
1425
|
const companyUid = await getEntityUid(
|
|
@@ -1400,7 +1431,12 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1400
1431
|
token,
|
|
1401
1432
|
path: `/secrets/${encodeURIComponent(companyUid)}/acl/grant`,
|
|
1402
1433
|
method: "POST",
|
|
1403
|
-
body: {
|
|
1434
|
+
body: {
|
|
1435
|
+
path,
|
|
1436
|
+
granteeType: principal.granteeType,
|
|
1437
|
+
granteeId: principal.granteeId,
|
|
1438
|
+
permission: opts.permission,
|
|
1439
|
+
},
|
|
1404
1440
|
});
|
|
1405
1441
|
|
|
1406
1442
|
if (!res.ok) {
|
|
@@ -1421,7 +1457,7 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1421
1457
|
process.exit(1);
|
|
1422
1458
|
}
|
|
1423
1459
|
|
|
1424
|
-
console.log(chalk.green(`Shared '${path}' with ${
|
|
1460
|
+
console.log(chalk.green(`Shared '${path}' with ${describeSecretAclPrincipal(principal)} (${opts.permission})`));
|
|
1425
1461
|
} catch (err) {
|
|
1426
1462
|
console.error(
|
|
1427
1463
|
chalk.red("Error:"),
|
|
@@ -1434,7 +1470,7 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1434
1470
|
secrets
|
|
1435
1471
|
.command("unshare <path>")
|
|
1436
1472
|
.description("Remove a grant from a secret")
|
|
1437
|
-
.requiredOption("--from <principal>", "Email address
|
|
1473
|
+
.requiredOption("--from <principal>", "Email address, group id, or @all (entire company) to remove")
|
|
1438
1474
|
.action(async (path: string, opts: { from: string }) => {
|
|
1439
1475
|
try {
|
|
1440
1476
|
rejectIfPersonal(secrets.opts(), "unshare");
|
|
@@ -1444,13 +1480,10 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1444
1480
|
process.exit(1);
|
|
1445
1481
|
}
|
|
1446
1482
|
|
|
1447
|
-
const
|
|
1448
|
-
if (!
|
|
1449
|
-
console.error(chalk.red(`Invalid principal '${opts.from}': must be an email address or a group id matching grp_<alphanumeric>`));
|
|
1483
|
+
const principal = parseSecretAclPrincipal(opts.from);
|
|
1484
|
+
if (!principal) {
|
|
1450
1485
|
process.exit(1);
|
|
1451
1486
|
}
|
|
1452
|
-
const granteeType = isEmailFrom ? "email" : "group";
|
|
1453
|
-
const granteeId = opts.from;
|
|
1454
1487
|
|
|
1455
1488
|
const token = await ensureCognitoToken();
|
|
1456
1489
|
const companyUid = await getEntityUid(
|
|
@@ -1462,7 +1495,11 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1462
1495
|
token,
|
|
1463
1496
|
path: `/secrets/${encodeURIComponent(companyUid)}/acl/revoke`,
|
|
1464
1497
|
method: "POST",
|
|
1465
|
-
body: {
|
|
1498
|
+
body: {
|
|
1499
|
+
path,
|
|
1500
|
+
granteeType: principal.granteeType,
|
|
1501
|
+
granteeId: principal.granteeId,
|
|
1502
|
+
},
|
|
1466
1503
|
});
|
|
1467
1504
|
|
|
1468
1505
|
if (!res.ok) {
|
|
@@ -1472,7 +1509,7 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1472
1509
|
} else if (res.status === 403) {
|
|
1473
1510
|
console.error(chalk.red("Not authorized to modify this secret's ACL"));
|
|
1474
1511
|
} else if (res.status === 404) {
|
|
1475
|
-
console.log(chalk.green(`Grant already absent for '${path}' / ${
|
|
1512
|
+
console.log(chalk.green(`Grant already absent for '${path}' / ${describeSecretAclPrincipal(principal)}`));
|
|
1476
1513
|
return;
|
|
1477
1514
|
} else if (res.status >= 500) {
|
|
1478
1515
|
console.error(chalk.red(`Server error: ${body.error ?? res.statusText}`));
|
|
@@ -1482,7 +1519,7 @@ export function registerSecretsCommand(program: Command): void {
|
|
|
1482
1519
|
process.exit(1);
|
|
1483
1520
|
}
|
|
1484
1521
|
|
|
1485
|
-
console.log(chalk.green(`Removed grant for ${
|
|
1522
|
+
console.log(chalk.green(`Removed grant for ${describeSecretAclPrincipal(principal)} on '${path}'`));
|
|
1486
1523
|
} catch (err) {
|
|
1487
1524
|
console.error(
|
|
1488
1525
|
chalk.red("Error:"),
|