opal-security 3.1.1-beta.50f7f9f → 3.1.1-beta.5457bdf
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 +36 -18
- package/lib/commands/login.js +6 -2
- package/lib/commands/request/create.d.ts +2 -0
- package/lib/commands/request/create.js +31 -7
- package/lib/commands/whoami.d.ts +8 -0
- package/lib/commands/whoami.js +34 -0
- package/lib/lib/apollo.js +3 -4
- package/lib/lib/credentials/index.d.ts +2 -1
- package/lib/lib/credentials/index.js +2 -1
- package/lib/lib/requests.d.ts +6 -3
- package/lib/lib/requests.js +119 -107
- package/lib/utils/displays.js +4 -2
- package/oclif.manifest.json +46 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ $ npm install -g opal-security
|
|
|
22
22
|
$ opal COMMAND
|
|
23
23
|
running command...
|
|
24
24
|
$ opal (--version)
|
|
25
|
-
opal-security/3.1.1-beta.
|
|
25
|
+
opal-security/3.1.1-beta.5457bdf linux-x64 node-v20.19.2
|
|
26
26
|
$ opal --help [COMMAND]
|
|
27
27
|
USAGE
|
|
28
28
|
$ opal COMMAND
|
|
@@ -53,6 +53,7 @@ USAGE
|
|
|
53
53
|
* [`opal ssh copyTo`](#opal-ssh-copyto)
|
|
54
54
|
* [`opal ssh start`](#opal-ssh-start)
|
|
55
55
|
* [`opal version`](#opal-version)
|
|
56
|
+
* [`opal whoami`](#opal-whoami)
|
|
56
57
|
|
|
57
58
|
## `opal autocomplete [SHELL]`
|
|
58
59
|
|
|
@@ -101,7 +102,7 @@ EXAMPLES
|
|
|
101
102
|
$ opal aws:identity
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
_See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
105
|
+
_See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/aws/identity.ts)_
|
|
105
106
|
|
|
106
107
|
## `opal clear-auth-provider`
|
|
107
108
|
|
|
@@ -121,7 +122,7 @@ EXAMPLES
|
|
|
121
122
|
$ opal clear-auth-provider
|
|
122
123
|
```
|
|
123
124
|
|
|
124
|
-
_See code: [src/commands/clear-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
125
|
+
_See code: [src/commands/clear-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/clear-auth-provider.ts)_
|
|
125
126
|
|
|
126
127
|
## `opal curl-example`
|
|
127
128
|
|
|
@@ -138,7 +139,7 @@ DESCRIPTION
|
|
|
138
139
|
Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.
|
|
139
140
|
```
|
|
140
141
|
|
|
141
|
-
_See code: [src/commands/curl-example.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
142
|
+
_See code: [src/commands/curl-example.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/curl-example.ts)_
|
|
142
143
|
|
|
143
144
|
## `opal groups get`
|
|
144
145
|
|
|
@@ -159,7 +160,7 @@ EXAMPLES
|
|
|
159
160
|
$ opal groups:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
_See code: [src/commands/groups/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
163
|
+
_See code: [src/commands/groups/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/groups/get.ts)_
|
|
163
164
|
|
|
164
165
|
## `opal help [COMMANDS]`
|
|
165
166
|
|
|
@@ -209,7 +210,7 @@ EXAMPLES
|
|
|
209
210
|
$ opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --profileName "custom-profile"
|
|
210
211
|
```
|
|
211
212
|
|
|
212
|
-
_See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
213
|
+
_See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/iam-roles/start.ts)_
|
|
213
214
|
|
|
214
215
|
## `opal kube-roles start`
|
|
215
216
|
|
|
@@ -240,7 +241,7 @@ EXAMPLES
|
|
|
240
241
|
$ opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId "arn:aws:iam::712234975475:role/acme-eks-cluster-admin-role"
|
|
241
242
|
```
|
|
242
243
|
|
|
243
|
-
_See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
244
|
+
_See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/kube-roles/start.ts)_
|
|
244
245
|
|
|
245
246
|
## `opal login`
|
|
246
247
|
|
|
@@ -261,7 +262,7 @@ EXAMPLES
|
|
|
261
262
|
$ opal login
|
|
262
263
|
```
|
|
263
264
|
|
|
264
|
-
_See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
265
|
+
_See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/login.ts)_
|
|
265
266
|
|
|
266
267
|
## `opal logout`
|
|
267
268
|
|
|
@@ -281,7 +282,7 @@ EXAMPLES
|
|
|
281
282
|
$ opal logout
|
|
282
283
|
```
|
|
283
284
|
|
|
284
|
-
_See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
285
|
+
_See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/logout.ts)_
|
|
285
286
|
|
|
286
287
|
## `opal postgres-instances start`
|
|
287
288
|
|
|
@@ -318,7 +319,7 @@ EXAMPLES
|
|
|
318
319
|
$ opal postgres-instances:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId fullaccess --action view
|
|
319
320
|
```
|
|
320
321
|
|
|
321
|
-
_See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
322
|
+
_See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/postgres-instances/start.ts)_
|
|
322
323
|
|
|
323
324
|
## `opal resources get`
|
|
324
325
|
|
|
@@ -339,7 +340,7 @@ EXAMPLES
|
|
|
339
340
|
$ opal resources:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
|
|
340
341
|
```
|
|
341
342
|
|
|
342
|
-
_See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
343
|
+
_See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/resources/get.ts)_
|
|
343
344
|
|
|
344
345
|
## `opal set-auth-provider`
|
|
345
346
|
|
|
@@ -365,7 +366,7 @@ EXAMPLES
|
|
|
365
366
|
$ opal set-auth-provider --clientID 1234asdf --issuerUrl https://auth.example.com
|
|
366
367
|
```
|
|
367
368
|
|
|
368
|
-
_See code: [src/commands/set-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
369
|
+
_See code: [src/commands/set-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/set-auth-provider.ts)_
|
|
369
370
|
|
|
370
371
|
## `opal set-custom-header`
|
|
371
372
|
|
|
@@ -386,7 +387,7 @@ EXAMPLES
|
|
|
386
387
|
$ opal set-custom-header --header 'cf-access-token: $TOKEN'
|
|
387
388
|
```
|
|
388
389
|
|
|
389
|
-
_See code: [src/commands/set-custom-header.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
390
|
+
_See code: [src/commands/set-custom-header.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/set-custom-header.ts)_
|
|
390
391
|
|
|
391
392
|
## `opal set-token`
|
|
392
393
|
|
|
@@ -406,7 +407,7 @@ EXAMPLES
|
|
|
406
407
|
$ opal set-token
|
|
407
408
|
```
|
|
408
409
|
|
|
409
|
-
_See code: [src/commands/set-token.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
410
|
+
_See code: [src/commands/set-token.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/set-token.ts)_
|
|
410
411
|
|
|
411
412
|
## `opal set-url [URL]`
|
|
412
413
|
|
|
@@ -430,7 +431,7 @@ EXAMPLES
|
|
|
430
431
|
$ opal set-url
|
|
431
432
|
```
|
|
432
433
|
|
|
433
|
-
_See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
434
|
+
_See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/set-url.ts)_
|
|
434
435
|
|
|
435
436
|
## `opal ssh copyFrom`
|
|
436
437
|
|
|
@@ -461,7 +462,7 @@ EXAMPLES
|
|
|
461
462
|
$ opal ssh:copyFrom --src instance/dir --dest my/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
|
|
462
463
|
```
|
|
463
464
|
|
|
464
|
-
_See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
465
|
+
_See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/ssh/copyFrom.ts)_
|
|
465
466
|
|
|
466
467
|
## `opal ssh copyTo`
|
|
467
468
|
|
|
@@ -492,7 +493,7 @@ EXAMPLES
|
|
|
492
493
|
$ opal ssh:copyTo --src my/dir --dest instance/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
|
|
493
494
|
```
|
|
494
495
|
|
|
495
|
-
_See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
496
|
+
_See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/ssh/copyTo.ts)_
|
|
496
497
|
|
|
497
498
|
## `opal ssh start`
|
|
498
499
|
|
|
@@ -519,7 +520,7 @@ EXAMPLES
|
|
|
519
520
|
$ opal ssh:start --id 51f7176b-0464-4a6f-8369-e951e187b398
|
|
520
521
|
```
|
|
521
522
|
|
|
522
|
-
_See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.
|
|
523
|
+
_See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/ssh/start.ts)_
|
|
523
524
|
|
|
524
525
|
## `opal version`
|
|
525
526
|
|
|
@@ -540,4 +541,21 @@ FLAG DESCRIPTIONS
|
|
|
540
541
|
```
|
|
541
542
|
|
|
542
543
|
_See code: [@oclif/plugin-version](https://github.com/oclif/plugin-version/blob/v2.2.27/src/commands/version.ts)_
|
|
544
|
+
|
|
545
|
+
## `opal whoami`
|
|
546
|
+
|
|
547
|
+
Describes current url set, organization name, and logged in user if applicabled.
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
USAGE
|
|
551
|
+
$ opal whoami [-h]
|
|
552
|
+
|
|
553
|
+
FLAGS
|
|
554
|
+
-h, --help Show CLI help.
|
|
555
|
+
|
|
556
|
+
DESCRIPTION
|
|
557
|
+
Describes current url set, organization name, and logged in user if applicabled.
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
_See code: [src/commands/whoami.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.5457bdf/src/commands/whoami.ts)_
|
|
543
561
|
<!-- commandsstop -->
|
package/lib/commands/login.js
CHANGED
|
@@ -88,12 +88,14 @@ class Login extends core_1.Command {
|
|
|
88
88
|
const configData = (0, config_1.getOrCreateConfigData)(configDir);
|
|
89
89
|
let email = flags.email;
|
|
90
90
|
let organizationId;
|
|
91
|
+
let organizationName;
|
|
91
92
|
let clientIDCandidate;
|
|
92
93
|
const existingCreds = await (0, credentials_1.getOpalCredentials)(this, false);
|
|
93
94
|
// Only use the previous email + organizationID if email isn't explicitly specified.
|
|
94
95
|
if (!email) {
|
|
95
96
|
email = existingCreds.email;
|
|
96
97
|
organizationId = existingCreds.organizationID;
|
|
98
|
+
organizationName = existingCreds.organizationName;
|
|
97
99
|
clientIDCandidate = existingCreds.clientIDCandidate;
|
|
98
100
|
}
|
|
99
101
|
await (0, credentials_1.removeOpalCredentials)(this);
|
|
@@ -149,6 +151,7 @@ class Login extends core_1.Command {
|
|
|
149
151
|
if (signInOrganizations && signInOrganizations.length > 0) {
|
|
150
152
|
if (signInOrganizations.length === 1) {
|
|
151
153
|
organizationId = signInOrganizations[0].organizationId;
|
|
154
|
+
organizationName = signInOrganizations[0].organizationName;
|
|
152
155
|
clientIDCandidate = signInOrganizations[0].cliClientId;
|
|
153
156
|
}
|
|
154
157
|
else {
|
|
@@ -164,6 +167,7 @@ class Login extends core_1.Command {
|
|
|
164
167
|
},
|
|
165
168
|
]);
|
|
166
169
|
organizationId = responses.signInOrganization.organizationId;
|
|
170
|
+
organizationName = responses.signInOrganization.organizationName;
|
|
167
171
|
clientIDCandidate = responses.signInOrganization.cliClientId;
|
|
168
172
|
}
|
|
169
173
|
}
|
|
@@ -238,10 +242,10 @@ class Login extends core_1.Command {
|
|
|
238
242
|
if (tokenExchangeError) {
|
|
239
243
|
this.log("WARN: Failed to exchange access token for session in Opal. Falling back to using access token for authenticating requests\n");
|
|
240
244
|
// TODO: consider adding a warn line recommending upgrading Opal to version XYZ, once accompanying PR is pushed to prod
|
|
241
|
-
await (0, credentials_1.setOpalCredentials)(this, email, organizationId !== null && organizationId !== void 0 ? organizationId : "", clientIDCandidate, (tokenSet === null || tokenSet === void 0 ? void 0 : tokenSet.access_token) || "", credentials_1.SecretType.ApiToken);
|
|
245
|
+
await (0, credentials_1.setOpalCredentials)(this, email, organizationId !== null && organizationId !== void 0 ? organizationId : "", clientIDCandidate, (tokenSet === null || tokenSet === void 0 ? void 0 : tokenSet.access_token) || "", credentials_1.SecretType.ApiToken, organizationName);
|
|
242
246
|
}
|
|
243
247
|
else {
|
|
244
|
-
await (0, credentials_1.setOpalCredentials)(this, email, organizationId !== null && organizationId !== void 0 ? organizationId : "", clientIDCandidate, apollo_1.cookieStr, credentials_1.SecretType.Cookie);
|
|
248
|
+
await (0, credentials_1.setOpalCredentials)(this, email, organizationId !== null && organizationId !== void 0 ? organizationId : "", clientIDCandidate, apollo_1.cookieStr, credentials_1.SecretType.Cookie, organizationName);
|
|
245
249
|
}
|
|
246
250
|
// "Representative" authenticated call to check the log-in worked as expected.
|
|
247
251
|
const { resp: authCheckResp, error: authCheckErr } = await (0, handler_1.runQueryDeprecated)({
|
|
@@ -5,6 +5,8 @@ export default class RequestCreate extends Command {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
7
7
|
id: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
reason: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
duration: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
10
|
};
|
|
9
11
|
run(): Promise<void>;
|
|
10
12
|
}
|
|
@@ -14,7 +14,8 @@ class RequestCreate extends core_1.Command {
|
|
|
14
14
|
const { flags } = await this.parse(RequestCreate);
|
|
15
15
|
const metadata = (0, requests_1.initEmptyRequestMetadata)();
|
|
16
16
|
if (flags.id) {
|
|
17
|
-
|
|
17
|
+
// if IDs are provided, bypass the interactive selection process
|
|
18
|
+
await (0, requests_1.bypassRequestSelection)(this, client, flags.id, metadata);
|
|
18
19
|
}
|
|
19
20
|
else {
|
|
20
21
|
(0, displays_1.headerMessage)(this);
|
|
@@ -32,13 +33,27 @@ class RequestCreate extends core_1.Command {
|
|
|
32
33
|
// Step 4: Set Request Defaults
|
|
33
34
|
await (0, requests_1.setRequestDefaults)(this, client, metadata);
|
|
34
35
|
// Step 4: Prompt for request reason
|
|
35
|
-
|
|
36
|
+
if (flags.reason) {
|
|
37
|
+
metadata.reason = flags.reason;
|
|
38
|
+
}
|
|
39
|
+
else if (!(metadata.requestDefaults.reasonOptional && flags.id && flags.duration)) {
|
|
40
|
+
await (0, requests_1.promptForReason)(metadata);
|
|
41
|
+
}
|
|
36
42
|
// Step 5: Prompt for expiration
|
|
37
|
-
|
|
43
|
+
if (flags.duration) {
|
|
44
|
+
(0, requests_1.bypassDuration)(this, flags.duration, metadata);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
await (0, requests_1.promptForExpiration)(metadata);
|
|
48
|
+
}
|
|
38
49
|
// Step 6: Display final summary of request
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
let canSubmit = true;
|
|
51
|
+
if (!(flags.id && flags.reason && flags.duration)) {
|
|
52
|
+
canSubmit = await (0, requests_1.promptRequestSubmission)(this, metadata);
|
|
53
|
+
}
|
|
54
|
+
// Step 7: Prompt for final submission
|
|
55
|
+
if (canSubmit)
|
|
56
|
+
await (0, requests_1.submitFinalRequest)(this, client, metadata);
|
|
42
57
|
}
|
|
43
58
|
}
|
|
44
59
|
RequestCreate.hidden = true;
|
|
@@ -48,7 +63,16 @@ RequestCreate.flags = {
|
|
|
48
63
|
id: core_1.Flags.string({
|
|
49
64
|
char: "i",
|
|
50
65
|
multiple: true,
|
|
51
|
-
description: "The id of the asset (resource, group) to request access to. Append a role ID using a colon if needed, e.g. `--id 123:456
|
|
66
|
+
description: "The id of the asset (resource, group) to request access to. Append a role ID using a colon if needed, e.g. `--id 123:456`.\
|
|
67
|
+
\n If not provided, an interactive selection flow will be available to select assets to request.",
|
|
68
|
+
}),
|
|
69
|
+
reason: core_1.Flags.string({
|
|
70
|
+
char: "r",
|
|
71
|
+
description: "The reason for the request, contained in quotes. If not provided, you will be prompted.",
|
|
72
|
+
}),
|
|
73
|
+
duration: core_1.Flags.integer({
|
|
74
|
+
char: "d",
|
|
75
|
+
description: "The duration of access for the request in minutes. If not provided, you will be prompted.",
|
|
52
76
|
}),
|
|
53
77
|
};
|
|
54
78
|
exports.default = RequestCreate;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const credentials_1 = require("../lib/credentials");
|
|
6
|
+
const flags_1 = require("../lib/flags");
|
|
7
|
+
class WhoAmI extends core_1.Command {
|
|
8
|
+
async run() {
|
|
9
|
+
const opalCreds = await (0, credentials_1.getOpalCredentials)(this, false);
|
|
10
|
+
const organizationName = opalCreds === null || opalCreds === void 0 ? void 0 : opalCreds.organizationName;
|
|
11
|
+
const email = opalCreds === null || opalCreds === void 0 ? void 0 : opalCreds.email;
|
|
12
|
+
const configData = (0, config_1.getOrCreateConfigData)(this.config.configDir);
|
|
13
|
+
const url = configData[config_1.urlKey];
|
|
14
|
+
if (email) {
|
|
15
|
+
this.log(`User: ${email}`);
|
|
16
|
+
}
|
|
17
|
+
if (organizationName) {
|
|
18
|
+
if (organizationName === "unset-org-id") {
|
|
19
|
+
this.log("Authenticated with Opal API Token.");
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.log(`Organization: ${organizationName}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (url) {
|
|
26
|
+
this.log(`Server: ${url}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
WhoAmI.description = "Describes current url set, organization name, and logged in user if applicabled.";
|
|
31
|
+
WhoAmI.flags = {
|
|
32
|
+
help: flags_1.SHARED_FLAGS.help,
|
|
33
|
+
};
|
|
34
|
+
exports.default = WhoAmI;
|
package/lib/lib/apollo.js
CHANGED
|
@@ -155,7 +155,7 @@ const initClient = async (command, fetchAccessToken = true) => {
|
|
|
155
155
|
return response;
|
|
156
156
|
});
|
|
157
157
|
});
|
|
158
|
-
const errorLink = (0, error_1.onError)(({ networkError, operation }) => {
|
|
158
|
+
const errorLink = (0, error_1.onError)(({ networkError, operation, forward }) => {
|
|
159
159
|
var _a;
|
|
160
160
|
// There's a few GQL operations where we don't want to use this error handler:
|
|
161
161
|
const customErrorOperations = [
|
|
@@ -186,7 +186,7 @@ const initClient = async (command, fetchAccessToken = true) => {
|
|
|
186
186
|
case 401: {
|
|
187
187
|
command.log("Your session is invalid or expired. Authenticating now...\n");
|
|
188
188
|
const loginCommand = new login_1.default([], command.config);
|
|
189
|
-
loginCommand.run().then(() => {
|
|
189
|
+
return (0, core_1.fromPromise)(loginCommand.run().then(() => {
|
|
190
190
|
if (cmd_1.mostRecentCommandTime && cmd_1.mostRecentCommand) {
|
|
191
191
|
const lastCommandReexecutionDuration = moment.duration(2, "minutes");
|
|
192
192
|
const lastCommandReexecutionDurationHasElapsed = cmd_1.mostRecentCommandTime.add(lastCommandReexecutionDuration) >
|
|
@@ -195,8 +195,7 @@ const initClient = async (command, fetchAccessToken = true) => {
|
|
|
195
195
|
cmd_1.mostRecentCommand.run();
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
-
});
|
|
199
|
-
break;
|
|
198
|
+
})).flatMap(() => forward(operation));
|
|
200
199
|
}
|
|
201
200
|
default:
|
|
202
201
|
return (0, exports.handleError)(command, `Received status code ${networkError.statusCode} from server${errorMessage ? ` with message "${errorMessage}"` : ""}`);
|
|
@@ -5,12 +5,13 @@ interface OpalCredentials {
|
|
|
5
5
|
clientIDCandidate?: string;
|
|
6
6
|
secret?: string;
|
|
7
7
|
secretType?: SecretType;
|
|
8
|
+
organizationName?: string;
|
|
8
9
|
}
|
|
9
10
|
export declare enum SecretType {
|
|
10
11
|
Cookie = "COOKIE",
|
|
11
12
|
ApiToken = "API_TOKEN"
|
|
12
13
|
}
|
|
13
|
-
export declare const setOpalCredentials: (command: Command, email: string | undefined, organizationID: string, clientIDCandidate: string | undefined | null, secret: string, secretType: SecretType) => Promise<void>;
|
|
14
|
+
export declare const setOpalCredentials: (command: Command, email: string | undefined, organizationID: string, clientIDCandidate: string | undefined | null, secret: string, secretType: SecretType, organizationName?: string) => Promise<void>;
|
|
14
15
|
export declare const getOpalCredentials: (command: Command, includeAuthSecret?: boolean) => Promise<OpalCredentials>;
|
|
15
16
|
export declare const removeOpalCredentials: (command: Command) => Promise<void>;
|
|
16
17
|
export {};
|
|
@@ -9,13 +9,14 @@ var SecretType;
|
|
|
9
9
|
SecretType["Cookie"] = "COOKIE";
|
|
10
10
|
SecretType["ApiToken"] = "API_TOKEN";
|
|
11
11
|
})(SecretType || (exports.SecretType = SecretType = {}));
|
|
12
|
-
const setOpalCredentials = async (command, email, organizationID, clientIDCandidate, secret, secretType) => {
|
|
12
|
+
const setOpalCredentials = async (command, email, organizationID, clientIDCandidate, secret, secretType, organizationName) => {
|
|
13
13
|
const givenEmail = email || "email-unset";
|
|
14
14
|
const configData = (0, config_1.getOrCreateConfigData)(command.config.configDir);
|
|
15
15
|
configData.creds = {
|
|
16
16
|
clientIDCandidate,
|
|
17
17
|
email,
|
|
18
18
|
organizationID,
|
|
19
|
+
organizationName,
|
|
19
20
|
secretType,
|
|
20
21
|
};
|
|
21
22
|
(0, config_1.writeConfigData)(command.config.configDir, configData);
|
package/lib/lib/requests.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { NormalizedCacheObject } from "@apollo/client/core";
|
|
2
2
|
import type { ApolloClient } from "@apollo/client/core/ApolloClient";
|
|
3
3
|
import type { Command } from "@oclif/core/lib/command";
|
|
4
|
+
import { type AppType, type ConnectionType, EntityType } from "../graphql/graphql";
|
|
4
5
|
type AppNode = {
|
|
5
6
|
appId: string;
|
|
6
7
|
appName: string;
|
|
7
|
-
appType?:
|
|
8
|
+
appType?: AppType | ConnectionType;
|
|
8
9
|
assets: Record<string, AssetNode>;
|
|
9
10
|
};
|
|
10
11
|
type AssetNode = {
|
|
11
12
|
assetId: string;
|
|
12
13
|
assetName: string;
|
|
13
|
-
type:
|
|
14
|
+
type: EntityType;
|
|
14
15
|
roles?: Record<string, RoleNode>;
|
|
15
16
|
};
|
|
16
17
|
type RoleNode = {
|
|
@@ -44,6 +45,8 @@ export declare function doneSelectingAssets(): Promise<boolean>;
|
|
|
44
45
|
export declare function setRequestDefaults(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
|
|
45
46
|
export declare function promptForReason(metadata: RequestMetadata): Promise<void>;
|
|
46
47
|
export declare function promptForExpiration(metadata: RequestMetadata): Promise<void>;
|
|
48
|
+
export declare function promptRequestSubmission(cmd: Command, metadata: RequestMetadata): Promise<boolean>;
|
|
47
49
|
export declare function submitFinalRequest(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
|
|
48
|
-
export declare function bypassRequestSelection(cmd: Command, client: ApolloClient<NormalizedCacheObject>,
|
|
50
|
+
export declare function bypassRequestSelection(cmd: Command, client: ApolloClient<NormalizedCacheObject>, flagValue: string[], metadata: RequestMetadata): Promise<void>;
|
|
51
|
+
export declare function bypassDuration(cmd: Command, duration: number, metadata: RequestMetadata): void;
|
|
49
52
|
export {};
|
package/lib/lib/requests.js
CHANGED
|
@@ -6,15 +6,27 @@ exports.doneSelectingAssets = doneSelectingAssets;
|
|
|
6
6
|
exports.setRequestDefaults = setRequestDefaults;
|
|
7
7
|
exports.promptForReason = promptForReason;
|
|
8
8
|
exports.promptForExpiration = promptForExpiration;
|
|
9
|
+
exports.promptRequestSubmission = promptRequestSubmission;
|
|
9
10
|
exports.submitFinalRequest = submitFinalRequest;
|
|
10
11
|
exports.bypassRequestSelection = bypassRequestSelection;
|
|
12
|
+
exports.bypassDuration = bypassDuration;
|
|
11
13
|
const chalk_1 = require("chalk");
|
|
12
|
-
const inquirer = require("inquirer");
|
|
13
14
|
const graphql_1 = require("../graphql");
|
|
14
15
|
const graphql_2 = require("../graphql/graphql");
|
|
15
16
|
const displays_1 = require("../utils/displays");
|
|
16
17
|
const config_1 = require("./config");
|
|
17
|
-
const { AutoComplete, Select, prompt, Form } = require("enquirer");
|
|
18
|
+
const { AutoComplete, Select, prompt, Form, confirm } = require("enquirer");
|
|
19
|
+
function entityTypeFromString(str) {
|
|
20
|
+
const capStr = str === null || str === void 0 ? void 0 : str.toLocaleUpperCase();
|
|
21
|
+
if (capStr === "RESOURCE") {
|
|
22
|
+
return graphql_2.EntityType.Resource;
|
|
23
|
+
}
|
|
24
|
+
if (capStr === "GROUP") {
|
|
25
|
+
return graphql_2.EntityType.Group;
|
|
26
|
+
}
|
|
27
|
+
// if type unknown, default to resource
|
|
28
|
+
return graphql_2.EntityType.Resource;
|
|
29
|
+
}
|
|
18
30
|
function initEmptyRequestMetadata() {
|
|
19
31
|
// Initialize with empty defaults
|
|
20
32
|
const requestDefaults = {
|
|
@@ -167,7 +179,7 @@ async function queryRequestableAssets(cmd, client, appId, input) {
|
|
|
167
179
|
value: {
|
|
168
180
|
name: name || "",
|
|
169
181
|
id: id || "",
|
|
170
|
-
type: ((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename)
|
|
182
|
+
type: entityTypeFromString(((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename)),
|
|
171
183
|
},
|
|
172
184
|
};
|
|
173
185
|
});
|
|
@@ -611,11 +623,14 @@ async function queryAssociatedItems(cmd, client, id, input) {
|
|
|
611
623
|
}
|
|
612
624
|
}
|
|
613
625
|
// Helper functions
|
|
626
|
+
const selectInstructions = chalk_1.default.dim("[↑↓] Navigate · [Enter] Select · Type to filter");
|
|
627
|
+
const multiSelectInstructions = chalk_1.default.dim("[↑↓] Navigate · [Space] Select · [Enter] Confirm · Type to filter");
|
|
614
628
|
async function selectRequestableItems(cmd, client, requestMap) {
|
|
615
629
|
const initial = (await queryRequestableApps(cmd, client, "")) || [];
|
|
616
630
|
const appPrompt = new AutoComplete({
|
|
617
631
|
name: "App",
|
|
618
|
-
message: "Select an app
|
|
632
|
+
message: "Select an app",
|
|
633
|
+
hint: selectInstructions,
|
|
619
634
|
limit: 15,
|
|
620
635
|
choices: initial,
|
|
621
636
|
async suggest(input) {
|
|
@@ -643,6 +658,7 @@ async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
|
|
|
643
658
|
const rolePrompt = new AutoComplete({
|
|
644
659
|
name: "Roles",
|
|
645
660
|
message: `Select a role for ${app.name}:`,
|
|
661
|
+
hint: multiSelectInstructions,
|
|
646
662
|
limit: 15,
|
|
647
663
|
multiple: true,
|
|
648
664
|
async choices(input) {
|
|
@@ -653,7 +669,7 @@ async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
|
|
|
653
669
|
},
|
|
654
670
|
validate: (answer) => {
|
|
655
671
|
if (answer.length !== 1) {
|
|
656
|
-
return "
|
|
672
|
+
return "Only one role is allowed to be requested for on Okta or Azure apps.";
|
|
657
673
|
}
|
|
658
674
|
return true;
|
|
659
675
|
},
|
|
@@ -665,7 +681,7 @@ async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
|
|
|
665
681
|
entry.assets[role.id] = {
|
|
666
682
|
assetId: role.id,
|
|
667
683
|
assetName: role.name,
|
|
668
|
-
type: role.type
|
|
684
|
+
type: entityTypeFromString(role.type),
|
|
669
685
|
roles: {},
|
|
670
686
|
};
|
|
671
687
|
}
|
|
@@ -717,6 +733,7 @@ async function chooseAssets(cmd, client, appId, requestMap) {
|
|
|
717
733
|
const assetPrompt = new AutoComplete({
|
|
718
734
|
name: "Assets",
|
|
719
735
|
message: "Select one or more assets:",
|
|
736
|
+
hint: multiSelectInstructions,
|
|
720
737
|
limit: 15,
|
|
721
738
|
multiple: true,
|
|
722
739
|
async choices(input) {
|
|
@@ -766,6 +783,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
|
|
|
766
783
|
const rolePrompt = new AutoComplete({
|
|
767
784
|
name: "Roles",
|
|
768
785
|
message: `Select one or more roles for ${assetEntry.assetName}:`,
|
|
786
|
+
hint: multiSelectInstructions,
|
|
769
787
|
limit: 15,
|
|
770
788
|
multiple: true,
|
|
771
789
|
choices: assetRoles,
|
|
@@ -856,7 +874,7 @@ async function promptForReason(metadata) {
|
|
|
856
874
|
const { reason } = await prompt([
|
|
857
875
|
{
|
|
858
876
|
name: "reason",
|
|
859
|
-
message: "
|
|
877
|
+
message: "Why do you need access?",
|
|
860
878
|
type: "input",
|
|
861
879
|
validate: (answer) => {
|
|
862
880
|
if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
|
|
@@ -978,148 +996,135 @@ async function setCustomDuration(metadata) {
|
|
|
978
996
|
label: durationLabel,
|
|
979
997
|
};
|
|
980
998
|
}
|
|
981
|
-
async function
|
|
982
|
-
|
|
999
|
+
async function promptRequestSubmission(cmd, metadata) {
|
|
1000
|
+
(0, displays_1.displayFinalRequestSummary)(cmd, metadata);
|
|
983
1001
|
const submitMessage = "✅ Yes, submit request";
|
|
984
1002
|
const cancelMessage = "❌ No, cancel request";
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1003
|
+
const prompt = new Select({
|
|
1004
|
+
name: "submitOrCancel",
|
|
1005
|
+
message: "Is this all you want to request?",
|
|
1006
|
+
choices: [submitMessage, cancelMessage],
|
|
1007
|
+
});
|
|
1008
|
+
const submitOrCancel = await prompt.run();
|
|
1009
|
+
return submitOrCancel === submitMessage;
|
|
1010
|
+
}
|
|
1011
|
+
async function submitFinalRequest(cmd, client, metadata) {
|
|
1012
|
+
var _a, _b, _c, _d;
|
|
1013
|
+
// Build requested assets lists for the mutation
|
|
1014
|
+
const requestedResources = [];
|
|
1015
|
+
const requestedGroups = [];
|
|
1016
|
+
for (const appNode of Object.values(metadata.requestMap)) {
|
|
1017
|
+
// This extraction is different than the one in setRequestDefaults.
|
|
1018
|
+
// Both extract the requestedResources and requestedGroups,
|
|
1019
|
+
// use different formats.
|
|
1020
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
1021
|
+
if (assetNode.roles) {
|
|
1022
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
1023
|
+
return roleId;
|
|
1024
|
+
});
|
|
1025
|
+
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
1026
|
+
for (const roleId of roleIds) {
|
|
1027
|
+
switch (assetNode.type) {
|
|
1028
|
+
case graphql_2.EntityType.Resource: {
|
|
1029
|
+
requestedResources.push({
|
|
1030
|
+
resourceId: assetId,
|
|
1031
|
+
accessLevel: {
|
|
1032
|
+
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
1033
|
+
accessLevelRemoteId: roleId,
|
|
1034
|
+
},
|
|
1035
|
+
});
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
case graphql_2.EntityType.Group: {
|
|
1039
|
+
requestedGroups.push({
|
|
1040
|
+
groupId: assetId,
|
|
1041
|
+
accessLevel: {
|
|
1042
|
+
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
1043
|
+
accessLevelRemoteId: roleId,
|
|
1044
|
+
},
|
|
1045
|
+
});
|
|
1046
|
+
break;
|
|
1031
1047
|
}
|
|
1032
1048
|
}
|
|
1033
1049
|
}
|
|
1034
1050
|
}
|
|
1035
|
-
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
1036
|
-
// Build link to request
|
|
1037
|
-
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
1038
|
-
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
1039
|
-
cmd.log("\n🎉 Your Access Request has been submitted!\n");
|
|
1040
|
-
cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
|
|
1041
|
-
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
1042
|
-
cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
|
|
1043
|
-
}
|
|
1044
|
-
const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
|
|
1045
|
-
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
1046
|
-
}
|
|
1047
|
-
return;
|
|
1048
1051
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1052
|
+
}
|
|
1053
|
+
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
1054
|
+
// Build link to request
|
|
1055
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
1056
|
+
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
1057
|
+
cmd.log("\n🎉 Your Access Request has been submitted!\n");
|
|
1058
|
+
cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
|
|
1059
|
+
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
1060
|
+
cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
|
|
1055
1061
|
}
|
|
1062
|
+
const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
|
|
1063
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
1056
1064
|
}
|
|
1065
|
+
return;
|
|
1057
1066
|
}
|
|
1058
|
-
async function bypassRequestSelection(cmd, client,
|
|
1067
|
+
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
1059
1068
|
var _a, _b;
|
|
1060
1069
|
try {
|
|
1061
|
-
|
|
1070
|
+
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
1071
|
+
for (const id of flagValue) {
|
|
1072
|
+
const [assetId, roleName] = id.split(":");
|
|
1062
1073
|
const resp = await client.query({
|
|
1063
1074
|
query: CATALOG_ITEM,
|
|
1064
1075
|
variables: {
|
|
1065
|
-
uuid:
|
|
1076
|
+
uuid: assetId || "",
|
|
1066
1077
|
},
|
|
1067
1078
|
fetchPolicy: "network-only", // to avoid caching
|
|
1068
1079
|
});
|
|
1069
1080
|
switch (resp.data.catalogItem.__typename) {
|
|
1081
|
+
case "Group":
|
|
1070
1082
|
case "Resource": {
|
|
1071
|
-
const
|
|
1072
|
-
const
|
|
1083
|
+
const item = resp.data.catalogItem;
|
|
1084
|
+
const assetName = item.__typename === "Resource" ? item.displayName : item.name;
|
|
1085
|
+
const requestableRoles = (item.accessLevels || [])
|
|
1073
1086
|
// TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
|
|
1074
1087
|
.map((role) => ({
|
|
1075
1088
|
id: role.accessLevelRemoteId,
|
|
1076
1089
|
name: role.accessLevelName,
|
|
1077
1090
|
}));
|
|
1078
|
-
|
|
1079
|
-
!(roles.length === 1 && roles[0].name === "")) {
|
|
1080
|
-
cmd.log(`Roles not implemented yet for resource ${resourceName}. Skipping roles selection.`); //TODO: Implement roles support
|
|
1081
|
-
continue;
|
|
1082
|
-
}
|
|
1083
|
-
const appId = ((_a = resp.data.catalogItem.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
1091
|
+
const appId = ((_a = item.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
1084
1092
|
if (!(appId in metadata.requestMap)) {
|
|
1085
1093
|
metadata.requestMap[appId] = {
|
|
1086
|
-
appName: ((_b =
|
|
1094
|
+
appName: ((_b = item.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
|
|
1087
1095
|
appId: appId,
|
|
1088
1096
|
assets: {},
|
|
1089
1097
|
};
|
|
1090
1098
|
}
|
|
1091
1099
|
const assetEntry = metadata.requestMap[appId].assets[id];
|
|
1092
1100
|
if (!assetEntry) {
|
|
1093
|
-
metadata.requestMap[appId].assets[
|
|
1094
|
-
assetId:
|
|
1095
|
-
assetName:
|
|
1096
|
-
type:
|
|
1101
|
+
metadata.requestMap[appId].assets[assetId] = {
|
|
1102
|
+
assetId: assetId,
|
|
1103
|
+
assetName: assetName,
|
|
1104
|
+
type: entityTypeFromString(item.__typename),
|
|
1097
1105
|
roles: {},
|
|
1098
1106
|
};
|
|
1099
1107
|
}
|
|
1100
|
-
if (
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
+
if (requestableRoles.length > 0 &&
|
|
1109
|
+
!(requestableRoles.length === 1 && requestableRoles[0].name === "")) {
|
|
1110
|
+
const selectedRole = requestableRoles.find((role) => role.name === roleName);
|
|
1111
|
+
if (selectedRole !== undefined) {
|
|
1112
|
+
if (!metadata.requestMap[appId].assets[assetId].roles) {
|
|
1113
|
+
metadata.requestMap[appId].assets[assetId].roles = {};
|
|
1114
|
+
}
|
|
1115
|
+
metadata.requestMap[appId].assets[assetId].roles[selectedRole.id] = {
|
|
1116
|
+
roleId: selectedRole.id,
|
|
1117
|
+
roleName: selectedRole.name,
|
|
1108
1118
|
};
|
|
1109
1119
|
}
|
|
1120
|
+
else {
|
|
1121
|
+
cmd.error(`Access level specified does not match one of ${assetName}'s defined access levels: ${requestableRoles.map((role) => `"${role.name}"`)}`);
|
|
1122
|
+
}
|
|
1110
1123
|
}
|
|
1111
1124
|
break;
|
|
1112
1125
|
}
|
|
1113
|
-
case "Group": {
|
|
1114
|
-
//TODO
|
|
1115
|
-
break;
|
|
1116
|
-
}
|
|
1117
|
-
case "Connection": {
|
|
1118
|
-
//TODO
|
|
1119
|
-
break;
|
|
1120
|
-
}
|
|
1121
1126
|
default:
|
|
1122
|
-
cmd.error("
|
|
1127
|
+
cmd.error("Invalid asset id was passed in using the --id flag.");
|
|
1123
1128
|
}
|
|
1124
1129
|
}
|
|
1125
1130
|
}
|
|
@@ -1130,3 +1135,10 @@ async function bypassRequestSelection(cmd, client, flagIds, metadata) {
|
|
|
1130
1135
|
}
|
|
1131
1136
|
return;
|
|
1132
1137
|
}
|
|
1138
|
+
function bypassDuration(cmd, duration, metadata) {
|
|
1139
|
+
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
1140
|
+
if (maxDuration && duration > maxDuration) {
|
|
1141
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${maxDuration}`);
|
|
1142
|
+
}
|
|
1143
|
+
metadata.durationInMinutes = duration;
|
|
1144
|
+
}
|
package/lib/utils/displays.js
CHANGED
|
@@ -35,8 +35,10 @@ function treeifyRequestMap(cmd, requestMap) {
|
|
|
35
35
|
assetsTree[assetKey] = {};
|
|
36
36
|
for (const [_roleId, roleNode] of Object.entries(assetNode.roles)) {
|
|
37
37
|
const roleName = roleNode.roleName;
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (roleName !== "") {
|
|
39
|
+
const roleKey = `${roleName} ${chalk_1.default.dim("[Role]")}`;
|
|
40
|
+
assetsTree[assetKey][roleKey] = null;
|
|
41
|
+
}
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
else {
|
package/oclif.manifest.json
CHANGED
|
@@ -316,6 +316,34 @@
|
|
|
316
316
|
"set-url.js"
|
|
317
317
|
]
|
|
318
318
|
},
|
|
319
|
+
"whoami": {
|
|
320
|
+
"aliases": [],
|
|
321
|
+
"args": {},
|
|
322
|
+
"description": "Describes current url set, organization name, and logged in user if applicabled.",
|
|
323
|
+
"flags": {
|
|
324
|
+
"help": {
|
|
325
|
+
"char": "h",
|
|
326
|
+
"description": "Show CLI help.",
|
|
327
|
+
"name": "help",
|
|
328
|
+
"allowNo": false,
|
|
329
|
+
"type": "boolean"
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
"hasDynamicHelp": false,
|
|
333
|
+
"hiddenAliases": [],
|
|
334
|
+
"id": "whoami",
|
|
335
|
+
"pluginAlias": "opal-security",
|
|
336
|
+
"pluginName": "opal-security",
|
|
337
|
+
"pluginType": "core",
|
|
338
|
+
"strict": true,
|
|
339
|
+
"enableJsonFlag": false,
|
|
340
|
+
"isESM": false,
|
|
341
|
+
"relativePath": [
|
|
342
|
+
"lib",
|
|
343
|
+
"commands",
|
|
344
|
+
"whoami.js"
|
|
345
|
+
]
|
|
346
|
+
},
|
|
319
347
|
"aws:identity": {
|
|
320
348
|
"aliases": [],
|
|
321
349
|
"args": {},
|
|
@@ -608,11 +636,27 @@
|
|
|
608
636
|
},
|
|
609
637
|
"id": {
|
|
610
638
|
"char": "i",
|
|
611
|
-
"description": "The id of the asset (resource, group) to request access to. Append a role ID using a colon if needed, e.g. `--id 123:456`.",
|
|
639
|
+
"description": "The id of the asset (resource, group) to request access to. Append a role ID using a colon if needed, e.g. `--id 123:456`. \n If not provided, an interactive selection flow will be available to select assets to request.",
|
|
612
640
|
"name": "id",
|
|
613
641
|
"hasDynamicHelp": false,
|
|
614
642
|
"multiple": true,
|
|
615
643
|
"type": "option"
|
|
644
|
+
},
|
|
645
|
+
"reason": {
|
|
646
|
+
"char": "r",
|
|
647
|
+
"description": "The reason for the request, contained in quotes. If not provided, you will be prompted.",
|
|
648
|
+
"name": "reason",
|
|
649
|
+
"hasDynamicHelp": false,
|
|
650
|
+
"multiple": false,
|
|
651
|
+
"type": "option"
|
|
652
|
+
},
|
|
653
|
+
"duration": {
|
|
654
|
+
"char": "d",
|
|
655
|
+
"description": "The duration of access for the request in minutes. If not provided, you will be prompted.",
|
|
656
|
+
"name": "duration",
|
|
657
|
+
"hasDynamicHelp": false,
|
|
658
|
+
"multiple": false,
|
|
659
|
+
"type": "option"
|
|
616
660
|
}
|
|
617
661
|
},
|
|
618
662
|
"hasDynamicHelp": false,
|
|
@@ -987,5 +1031,5 @@
|
|
|
987
1031
|
]
|
|
988
1032
|
}
|
|
989
1033
|
},
|
|
990
|
-
"version": "3.1.1-beta.
|
|
1034
|
+
"version": "3.1.1-beta.5457bdf"
|
|
991
1035
|
}
|
package/package.json
CHANGED