opal-security 3.1.1-beta.68e6f48 → 3.1.1-beta.6b4868b

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 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.68e6f48 linux-x64 node-v20.19.2
25
+ opal-security/3.1.1-beta.6b4868b linux-x64 node-v20.19.2
26
26
  $ opal --help [COMMAND]
27
27
  USAGE
28
28
  $ opal COMMAND
@@ -101,7 +101,7 @@ EXAMPLES
101
101
  $ opal aws:identity
102
102
  ```
103
103
 
104
- _See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/aws/identity.ts)_
104
+ _See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/aws/identity.ts)_
105
105
 
106
106
  ## `opal clear-auth-provider`
107
107
 
@@ -121,7 +121,7 @@ EXAMPLES
121
121
  $ opal clear-auth-provider
122
122
  ```
123
123
 
124
- _See code: [src/commands/clear-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/clear-auth-provider.ts)_
124
+ _See code: [src/commands/clear-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/clear-auth-provider.ts)_
125
125
 
126
126
  ## `opal curl-example`
127
127
 
@@ -138,7 +138,7 @@ DESCRIPTION
138
138
  Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.
139
139
  ```
140
140
 
141
- _See code: [src/commands/curl-example.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/curl-example.ts)_
141
+ _See code: [src/commands/curl-example.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/curl-example.ts)_
142
142
 
143
143
  ## `opal groups get`
144
144
 
@@ -159,7 +159,7 @@ EXAMPLES
159
159
  $ opal groups:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
160
160
  ```
161
161
 
162
- _See code: [src/commands/groups/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/groups/get.ts)_
162
+ _See code: [src/commands/groups/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/groups/get.ts)_
163
163
 
164
164
  ## `opal help [COMMANDS]`
165
165
 
@@ -209,7 +209,7 @@ EXAMPLES
209
209
  $ opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --profileName "custom-profile"
210
210
  ```
211
211
 
212
- _See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/iam-roles/start.ts)_
212
+ _See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/iam-roles/start.ts)_
213
213
 
214
214
  ## `opal kube-roles start`
215
215
 
@@ -240,7 +240,7 @@ EXAMPLES
240
240
  $ opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId "arn:aws:iam::712234975475:role/acme-eks-cluster-admin-role"
241
241
  ```
242
242
 
243
- _See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/kube-roles/start.ts)_
243
+ _See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/kube-roles/start.ts)_
244
244
 
245
245
  ## `opal login`
246
246
 
@@ -261,7 +261,7 @@ EXAMPLES
261
261
  $ opal login
262
262
  ```
263
263
 
264
- _See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/login.ts)_
264
+ _See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/login.ts)_
265
265
 
266
266
  ## `opal logout`
267
267
 
@@ -281,7 +281,7 @@ EXAMPLES
281
281
  $ opal logout
282
282
  ```
283
283
 
284
- _See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/logout.ts)_
284
+ _See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/logout.ts)_
285
285
 
286
286
  ## `opal postgres-instances start`
287
287
 
@@ -318,7 +318,7 @@ EXAMPLES
318
318
  $ opal postgres-instances:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId fullaccess --action view
319
319
  ```
320
320
 
321
- _See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/postgres-instances/start.ts)_
321
+ _See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/postgres-instances/start.ts)_
322
322
 
323
323
  ## `opal resources get`
324
324
 
@@ -339,7 +339,7 @@ EXAMPLES
339
339
  $ opal resources:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
340
340
  ```
341
341
 
342
- _See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/resources/get.ts)_
342
+ _See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/resources/get.ts)_
343
343
 
344
344
  ## `opal set-auth-provider`
345
345
 
@@ -365,7 +365,7 @@ EXAMPLES
365
365
  $ opal set-auth-provider --clientID 1234asdf --issuerUrl https://auth.example.com
366
366
  ```
367
367
 
368
- _See code: [src/commands/set-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/set-auth-provider.ts)_
368
+ _See code: [src/commands/set-auth-provider.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/set-auth-provider.ts)_
369
369
 
370
370
  ## `opal set-custom-header`
371
371
 
@@ -386,7 +386,7 @@ EXAMPLES
386
386
  $ opal set-custom-header --header 'cf-access-token: $TOKEN'
387
387
  ```
388
388
 
389
- _See code: [src/commands/set-custom-header.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/set-custom-header.ts)_
389
+ _See code: [src/commands/set-custom-header.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/set-custom-header.ts)_
390
390
 
391
391
  ## `opal set-token`
392
392
 
@@ -406,7 +406,7 @@ EXAMPLES
406
406
  $ opal set-token
407
407
  ```
408
408
 
409
- _See code: [src/commands/set-token.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/set-token.ts)_
409
+ _See code: [src/commands/set-token.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/set-token.ts)_
410
410
 
411
411
  ## `opal set-url [URL]`
412
412
 
@@ -430,7 +430,7 @@ EXAMPLES
430
430
  $ opal set-url
431
431
  ```
432
432
 
433
- _See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/set-url.ts)_
433
+ _See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/set-url.ts)_
434
434
 
435
435
  ## `opal ssh copyFrom`
436
436
 
@@ -461,7 +461,7 @@ EXAMPLES
461
461
  $ opal ssh:copyFrom --src instance/dir --dest my/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
462
462
  ```
463
463
 
464
- _See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/ssh/copyFrom.ts)_
464
+ _See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/ssh/copyFrom.ts)_
465
465
 
466
466
  ## `opal ssh copyTo`
467
467
 
@@ -492,7 +492,7 @@ EXAMPLES
492
492
  $ opal ssh:copyTo --src my/dir --dest instance/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
493
493
  ```
494
494
 
495
- _See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/ssh/copyTo.ts)_
495
+ _See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/ssh/copyTo.ts)_
496
496
 
497
497
  ## `opal ssh start`
498
498
 
@@ -519,7 +519,7 @@ EXAMPLES
519
519
  $ opal ssh:start --id 51f7176b-0464-4a6f-8369-e951e187b398
520
520
  ```
521
521
 
522
- _See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.68e6f48/src/commands/ssh/start.ts)_
522
+ _See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v3.1.1-beta.6b4868b/src/commands/ssh/start.ts)_
523
523
 
524
524
  ## `opal version`
525
525
 
@@ -6,6 +6,7 @@ export default class RequestCreate extends Command {
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
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>;
9
10
  };
10
11
  run(): Promise<void>;
11
12
  }
@@ -15,7 +15,7 @@ class RequestCreate extends core_1.Command {
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
- (0, requests_1.bypassRequestSelection)(this, client, flags.id, metadata);
18
+ await (0, requests_1.bypassRequestSelection)(this, client, flags.id, metadata);
19
19
  }
20
20
  else {
21
21
  (0, displays_1.headerMessage)(this);
@@ -40,10 +40,17 @@ class RequestCreate extends core_1.Command {
40
40
  await (0, requests_1.promptForReason)(metadata);
41
41
  }
42
42
  // Step 5: Prompt for expiration
43
- await (0, requests_1.promptForExpiration)(metadata);
43
+ if (flags.duration) {
44
+ (0, requests_1.bypassDuration)(this, flags.duration, metadata);
45
+ }
46
+ else {
47
+ await (0, requests_1.promptForExpiration)(metadata);
48
+ }
44
49
  // Step 6: Display final summary of request
45
- (0, displays_1.displayFinalRequestSummary)(this, metadata);
46
- // Step 7: Prompt for final submition
50
+ if (!(flags.id && flags.reason && flags.duration)) {
51
+ await (0, requests_1.promptRequestSubmission)(this, metadata);
52
+ }
53
+ // Step 7: Prompt for final submission
47
54
  await (0, requests_1.submitFinalRequest)(this, client, metadata);
48
55
  }
49
56
  }
@@ -61,5 +68,9 @@ RequestCreate.flags = {
61
68
  char: "r",
62
69
  description: "The reason for the request, contained in quotes. If not provided, you will be prompted.",
63
70
  }),
71
+ duration: core_1.Flags.integer({
72
+ char: "d",
73
+ description: "The duration of access for the request in minutes. If not provided, you will be prompted.",
74
+ }),
64
75
  };
65
76
  exports.default = RequestCreate;
@@ -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?: string;
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: string;
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<void>;
47
49
  export declare function submitFinalRequest(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
48
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 {};
@@ -6,8 +6,10 @@ 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
14
  const inquirer = require("inquirer");
13
15
  const graphql_1 = require("../graphql");
@@ -15,6 +17,16 @@ const graphql_2 = require("../graphql/graphql");
15
17
  const displays_1 = require("../utils/displays");
16
18
  const config_1 = require("./config");
17
19
  const { AutoComplete, Select, prompt, Form } = require("enquirer");
20
+ function entityTypeFromString(str) {
21
+ if (str === "Resource") {
22
+ return graphql_2.EntityType.Resource;
23
+ }
24
+ if (str === "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 = {
@@ -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) {
@@ -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 || graphql_2.EntityType.Resource,
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: "I need access to this because...",
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,8 +996,8 @@ async function setCustomDuration(metadata) {
978
996
  label: durationLabel,
979
997
  };
980
998
  }
981
- async function submitFinalRequest(cmd, client, metadata) {
982
- var _a, _b, _c, _d;
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
1003
  const { submit } = await inquirer.prompt([
@@ -992,58 +1010,6 @@ async function submitFinalRequest(cmd, client, metadata) {
992
1010
  ]);
993
1011
  switch (submit) {
994
1012
  case submitMessage: {
995
- // Build requested assets lists for the mutation
996
- const requestedResources = [];
997
- const requestedGroups = [];
998
- for (const appNode of Object.values(metadata.requestMap)) {
999
- // This extraction is different than the one in setRequestDefaults.
1000
- // Both extract the requestedResources and requestedGroups,
1001
- // use different formats.
1002
- for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
1003
- if (assetNode.roles !== undefined) {
1004
- const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
1005
- return roleId;
1006
- });
1007
- const roleIds = mappedRoles.length ? mappedRoles : [""];
1008
- for (const roleId of roleIds) {
1009
- switch (assetNode.type) {
1010
- case graphql_2.EntityType.Resource: {
1011
- requestedResources.push({
1012
- resourceId: assetId,
1013
- accessLevel: {
1014
- accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
1015
- accessLevelRemoteId: roleId,
1016
- },
1017
- });
1018
- break;
1019
- }
1020
- case graphql_2.EntityType.Group: {
1021
- requestedGroups.push({
1022
- groupId: assetId,
1023
- accessLevel: {
1024
- accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
1025
- accessLevelRemoteId: roleId,
1026
- },
1027
- });
1028
- break;
1029
- }
1030
- }
1031
- }
1032
- }
1033
- }
1034
- }
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
1013
  return;
1048
1014
  }
1049
1015
  case cancelMessage: {
@@ -1055,9 +1021,66 @@ async function submitFinalRequest(cmd, client, metadata) {
1055
1021
  }
1056
1022
  }
1057
1023
  }
1024
+ async function submitFinalRequest(cmd, client, metadata) {
1025
+ var _a, _b, _c, _d;
1026
+ // Build requested assets lists for the mutation
1027
+ const requestedResources = [];
1028
+ const requestedGroups = [];
1029
+ for (const appNode of Object.values(metadata.requestMap)) {
1030
+ // This extraction is different than the one in setRequestDefaults.
1031
+ // Both extract the requestedResources and requestedGroups,
1032
+ // use different formats.
1033
+ for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
1034
+ if (assetNode.roles) {
1035
+ const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
1036
+ return roleId;
1037
+ });
1038
+ const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
1039
+ for (const roleId of roleIds) {
1040
+ switch (assetNode.type) {
1041
+ case graphql_2.EntityType.Resource: {
1042
+ requestedResources.push({
1043
+ resourceId: assetId,
1044
+ accessLevel: {
1045
+ accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
1046
+ accessLevelRemoteId: roleId,
1047
+ },
1048
+ });
1049
+ break;
1050
+ }
1051
+ case graphql_2.EntityType.Group: {
1052
+ requestedGroups.push({
1053
+ groupId: assetId,
1054
+ accessLevel: {
1055
+ accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
1056
+ accessLevelRemoteId: roleId,
1057
+ },
1058
+ });
1059
+ break;
1060
+ }
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1065
+ }
1066
+ const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
1067
+ // Build link to request
1068
+ const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
1069
+ if (resp === null || resp === void 0 ? void 0 : resp.id) {
1070
+ cmd.log("\nšŸŽ‰ Your Access Request has been submitted!\n");
1071
+ cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
1072
+ if (resp === null || resp === void 0 ? void 0 : resp.status) {
1073
+ cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
1074
+ }
1075
+ const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
1076
+ cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
1077
+ }
1078
+ return;
1079
+ }
1058
1080
  async function bypassRequestSelection(cmd, client, flagValue, metadata) {
1059
- var _a, _b;
1081
+ var _a, _b, _c, _d;
1060
1082
  try {
1083
+ // Query Catalog Item endpoint to identify what the id belongs to (resource or group)
1061
1084
  for (const id of flagValue) {
1062
1085
  const resp = await client.query({
1063
1086
  query: CATALOG_ITEM,
@@ -1093,11 +1116,11 @@ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
1093
1116
  metadata.requestMap[appId].assets[id] = {
1094
1117
  assetId: id,
1095
1118
  assetName: resourceName,
1096
- type: "Resource",
1119
+ type: graphql_2.EntityType.Resource,
1097
1120
  roles: {},
1098
1121
  };
1099
1122
  }
1100
- if (roles) {
1123
+ if (roles && !(roles.length === 1 && roles[0].name === "")) {
1101
1124
  if (!metadata.requestMap[appId].assets[id].roles) {
1102
1125
  metadata.requestMap[appId].assets[id].roles = {};
1103
1126
  }
@@ -1111,7 +1134,44 @@ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
1111
1134
  break;
1112
1135
  }
1113
1136
  case "Group": {
1114
- //TODO
1137
+ const groupName = resp.data.catalogItem.name;
1138
+ const roles = (resp.data.catalogItem.accessLevels || []).map((role) => ({
1139
+ id: role.accessLevelRemoteId,
1140
+ name: role.accessLevelName,
1141
+ }));
1142
+ if (roles.length > 0 &&
1143
+ !(roles.length === 1 && roles[0].name === "")) {
1144
+ cmd.log(`Roles not implemented yet for group ${groupName}. Skipping roles selection.`); //TODO: Implement group roles support
1145
+ continue;
1146
+ }
1147
+ const appId = ((_c = resp.data.catalogItem.connection) === null || _c === void 0 ? void 0 : _c.id) || "";
1148
+ if (!(appId in metadata.requestMap)) {
1149
+ metadata.requestMap[appId] = {
1150
+ appName: ((_d = resp.data.catalogItem.connection) === null || _d === void 0 ? void 0 : _d.displayName) || "",
1151
+ appId: appId,
1152
+ assets: {},
1153
+ };
1154
+ }
1155
+ const assetEntry = metadata.requestMap[appId].assets[id];
1156
+ if (!assetEntry) {
1157
+ metadata.requestMap[appId].assets[id] = {
1158
+ assetId: id,
1159
+ assetName: groupName,
1160
+ type: graphql_2.EntityType.Group,
1161
+ roles: {},
1162
+ };
1163
+ }
1164
+ if (roles) {
1165
+ if (!metadata.requestMap[appId].assets[id].roles) {
1166
+ metadata.requestMap[appId].assets[id].roles = {};
1167
+ }
1168
+ for (const role of roles) {
1169
+ metadata.requestMap[appId].assets[id].roles[role.id] = {
1170
+ roleId: role.id,
1171
+ roleName: role.name,
1172
+ };
1173
+ }
1174
+ }
1115
1175
  break;
1116
1176
  }
1117
1177
  case "Connection": {
@@ -1130,3 +1190,10 @@ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
1130
1190
  }
1131
1191
  return;
1132
1192
  }
1193
+ function bypassDuration(cmd, duration, metadata) {
1194
+ const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
1195
+ if (maxDuration && duration > maxDuration) {
1196
+ cmd.error(`The requested duration exceeds the allowed limit of ${maxDuration}`);
1197
+ }
1198
+ metadata.durationInMinutes = duration;
1199
+ }
@@ -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
- const roleKey = `${roleName} ${chalk_1.default.dim("[Role]")}`;
39
- assetsTree[assetKey][roleKey] = null;
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 {
@@ -621,6 +621,14 @@
621
621
  "hasDynamicHelp": false,
622
622
  "multiple": false,
623
623
  "type": "option"
624
+ },
625
+ "duration": {
626
+ "char": "d",
627
+ "description": "The duration of access for the request in minutes. If not provided, you will be prompted.",
628
+ "name": "duration",
629
+ "hasDynamicHelp": false,
630
+ "multiple": false,
631
+ "type": "option"
624
632
  }
625
633
  },
626
634
  "hasDynamicHelp": false,
@@ -995,5 +1003,5 @@
995
1003
  ]
996
1004
  }
997
1005
  },
998
- "version": "3.1.1-beta.68e6f48"
1006
+ "version": "3.1.1-beta.6b4868b"
999
1007
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opal-security",
3
3
  "description": "Opal allows you to centrally manage access to all of your sensitive systems.",
4
- "version": "3.1.1-beta.68e6f48",
4
+ "version": "3.1.1-beta.6b4868b",
5
5
  "author": "Stephen Cobbe",
6
6
  "bin": {
7
7
  "opal": "./bin/run"