opal-security 3.1.1-beta.e92fdf8 → 3.1.1-beta.f1a5f49

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.
@@ -1,24 +1,29 @@
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
- interface AppNode {
4
+ import { type AppType, type ConnectionType, EntityType } from "../graphql/graphql";
5
+ type AppNode = {
6
+ appId: string;
5
7
  appName: string;
8
+ appType?: AppType | ConnectionType;
6
9
  assets: Record<string, AssetNode>;
7
- }
8
- interface AssetNode {
10
+ };
11
+ type AssetNode = {
12
+ assetId: string;
9
13
  assetName: string;
10
- type: string;
14
+ type: EntityType;
11
15
  roles?: Record<string, RoleNode>;
12
- }
13
- interface RoleNode {
16
+ };
17
+ type RoleNode = {
18
+ roleId: string;
14
19
  roleName: string;
15
- }
20
+ };
16
21
  export type RequestMap = Record<string, AppNode>;
17
- interface DurationOption {
22
+ type DurationOption = {
18
23
  durationInMinutes: number;
19
24
  label: string;
20
- }
21
- interface RequestDefaults {
25
+ };
26
+ type RequestDefaults = {
22
27
  durationOptions?: DurationOption[];
23
28
  recommendedDurationInMinutes?: number | null;
24
29
  defaultDurationInMinutes?: number;
@@ -26,21 +31,22 @@ interface RequestDefaults {
26
31
  requireSupportTicket?: boolean;
27
32
  reasonOptional?: boolean;
28
33
  requesterIsAdmin?: boolean;
29
- }
30
- export interface RequestMetadata {
34
+ };
35
+ export type RequestMetadata = {
31
36
  requestMap: RequestMap;
32
37
  requestDefaults: RequestDefaults;
33
38
  durationLabel: string;
34
39
  durationInMinutes?: number;
35
40
  reason: string;
36
- }
41
+ };
37
42
  export declare function initEmptyRequestMetadata(): RequestMetadata;
38
43
  export declare function selectRequestableItems(cmd: Command, client: ApolloClient<NormalizedCacheObject>, requestMap: RequestMap): Promise<void>;
39
- export declare function chooseAssets(cmd: Command, client: ApolloClient<NormalizedCacheObject>, appId: string, requestMap: RequestMap): Promise<void>;
40
- export declare function chooseRoles(cmd: Command, client: ApolloClient<NormalizedCacheObject>, appId: string, assetId: string, requestMap: RequestMap): Promise<void>;
41
44
  export declare function doneSelectingAssets(): Promise<boolean>;
42
45
  export declare function setRequestDefaults(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
43
46
  export declare function promptForReason(metadata: RequestMetadata): Promise<void>;
44
47
  export declare function promptForExpiration(metadata: RequestMetadata): Promise<void>;
48
+ export declare function promptRequestSubmission(cmd: Command, metadata: RequestMetadata): Promise<boolean>;
45
49
  export declare function submitFinalRequest(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
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;
46
52
  export {};
@@ -2,19 +2,31 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.initEmptyRequestMetadata = initEmptyRequestMetadata;
4
4
  exports.selectRequestableItems = selectRequestableItems;
5
- exports.chooseAssets = chooseAssets;
6
- exports.chooseRoles = chooseRoles;
7
5
  exports.doneSelectingAssets = doneSelectingAssets;
8
6
  exports.setRequestDefaults = setRequestDefaults;
9
7
  exports.promptForReason = promptForReason;
10
8
  exports.promptForExpiration = promptForExpiration;
9
+ exports.promptRequestSubmission = promptRequestSubmission;
11
10
  exports.submitFinalRequest = submitFinalRequest;
11
+ exports.bypassRequestSelection = bypassRequestSelection;
12
+ exports.bypassDuration = bypassDuration;
12
13
  const chalk_1 = require("chalk");
13
- const inquirer = require("inquirer");
14
14
  const graphql_1 = require("../graphql");
15
+ const graphql_2 = require("../graphql/graphql");
15
16
  const displays_1 = require("../utils/displays");
16
17
  const config_1 = require("./config");
17
18
  const { AutoComplete, Select, prompt, Form } = 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 = {
@@ -94,6 +106,7 @@ async function queryRequestableApps(cmd, client, input) {
94
106
  value: {
95
107
  id: edge.node.id,
96
108
  name: edge.node.displayName,
109
+ type: type,
97
110
  toString: () => edge.node.displayName,
98
111
  },
99
112
  };
@@ -166,7 +179,7 @@ async function queryRequestableAssets(cmd, client, appId, input) {
166
179
  value: {
167
180
  name: name || "",
168
181
  id: id || "",
169
- 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)),
170
183
  },
171
184
  };
172
185
  });
@@ -485,39 +498,250 @@ async function createRequest(cmd, client, requestedResources, requestedGroups, r
485
498
  }
486
499
  }
487
500
  }
501
+ const CATALOG_ITEM = (0, graphql_1.graphql)(`
502
+ query GetCatalogItem($uuid: UUID!) {
503
+ catalogItem(id: $uuid) {
504
+ __typename
505
+ ... on Connection {
506
+ id
507
+ displayName
508
+ }
509
+ ... on Resource {
510
+ id
511
+ displayName
512
+ connection {
513
+ id
514
+ displayName
515
+ }
516
+ accessLevels{
517
+ accessLevelName
518
+ accessLevelRemoteId
519
+ }
520
+ }
521
+ ...on Group {
522
+ id
523
+ name
524
+ connection {
525
+ id
526
+ displayName
527
+ }
528
+ accessLevels{
529
+ accessLevelName
530
+ accessLevelRemoteId
531
+ }
532
+ }
533
+ ... on UserFacingError {
534
+ message
535
+ }
536
+ }
537
+ }
538
+ `);
539
+ const ASSOCIATED_ITEMS_QUERY = (0, graphql_1.graphql)(`
540
+ query GetAssociatedItems($resourceId: ResourceId!, $searchQuery: String) {
541
+ resource(input: {
542
+ id: $resourceId
543
+ }) {
544
+ __typename
545
+ ... on ResourceResult {
546
+ __typename
547
+ resource {
548
+ associatedItems(
549
+ first: 200
550
+ filters: {
551
+ searchQuery: {
552
+ contains: $searchQuery
553
+ }
554
+ access: REQUESTABLE
555
+ endUserVisible: true
556
+ entityType: {
557
+ in: [GROUP, RESOURCE]
558
+ }
559
+ }
560
+ ) {
561
+ edges {
562
+ __typename
563
+ ... on ResourceAssociatedItemEdge {
564
+ alias
565
+ node {
566
+ __typename
567
+ id
568
+ name
569
+ ... on Resource {
570
+ accessLevels(
571
+ filters: {
572
+ skipRemoteAccessLevels: false # azure app roles are remote
573
+ }
574
+ ) {
575
+ __typename
576
+ accessLevelName
577
+ accessLevelRemoteId
578
+ }
579
+ }
580
+ }
581
+ }
582
+ }
583
+ }
584
+ }
585
+ }
586
+ ... on ResourceNotFoundError {
587
+ message
588
+ }
589
+ }
590
+ }
591
+ `);
592
+ async function queryAssociatedItems(cmd, client, id, input) {
593
+ var _a, _b;
594
+ try {
595
+ const resp = await client.query({
596
+ query: ASSOCIATED_ITEMS_QUERY,
597
+ variables: {
598
+ resourceId: id || "",
599
+ searchQuery: input || "",
600
+ },
601
+ fetchPolicy: "network-only", // to avoid caching
602
+ });
603
+ switch (resp.data.resource.__typename) {
604
+ case "ResourceResult": {
605
+ const associatedItems = resp.data.resource.resource.associatedItems.edges.filter((edge) => edge.__typename === "ResourceAssociatedItemEdge");
606
+ const initial = [];
607
+ for (const edge of associatedItems) {
608
+ initial.push(...appRolesFromEdge(edge));
609
+ }
610
+ return initial;
611
+ }
612
+ case "ResourceNotFoundError":
613
+ cmd.log((_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.resource) === null || _b === void 0 ? void 0 : _b.message);
614
+ break;
615
+ default:
616
+ cmd.error(resp.error || "Unknown error occurred.");
617
+ }
618
+ }
619
+ catch (error) {
620
+ if (error instanceof Error || typeof error === "string") {
621
+ cmd.error(error);
622
+ }
623
+ }
624
+ }
488
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");
489
628
  async function selectRequestableItems(cmd, client, requestMap) {
490
- const initialChoices = (await queryRequestableApps(cmd, client, "")) || [];
629
+ const initial = (await queryRequestableApps(cmd, client, "")) || [];
491
630
  const appPrompt = new AutoComplete({
492
631
  name: "App",
493
- message: "Select an app:",
632
+ message: "Select an app",
633
+ hint: selectInstructions,
494
634
  limit: 15,
495
- choices: initialChoices,
635
+ choices: initial,
496
636
  async suggest(input) {
497
637
  const filteredChoices = await queryRequestableApps(cmd, client, input || "");
498
- return filteredChoices || initialChoices;
638
+ return filteredChoices || initial;
499
639
  },
500
640
  });
501
641
  const App = await appPrompt.run();
502
642
  // Set the app in the requestMap and call choose assets step
503
643
  if (!(App.id in requestMap)) {
504
644
  requestMap[App.id] = {
645
+ appId: App.id,
505
646
  appName: App.name,
506
647
  assets: {},
507
648
  };
508
649
  }
650
+ if (App.type === "OKTA_APP" || App.type === "AZURE_ENTERPRISE_APP") {
651
+ await chooseOktaAzureRoles(cmd, client, App, requestMap);
652
+ return;
653
+ }
509
654
  await chooseAssets(cmd, client, App.id, requestMap);
510
655
  }
656
+ async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
657
+ const associatedItems = (await queryAssociatedItems(cmd, client, app.id, "")) || [];
658
+ const rolePrompt = new AutoComplete({
659
+ name: "Roles",
660
+ message: `Select a role for ${app.name}:`,
661
+ hint: multiSelectInstructions,
662
+ limit: 15,
663
+ multiple: true,
664
+ async choices(input) {
665
+ if (!input)
666
+ return associatedItems;
667
+ const filteredChoices = await queryAssociatedItems(cmd, client, app.id, input);
668
+ return filteredChoices || associatedItems;
669
+ },
670
+ validate: (answer) => {
671
+ if (answer.length !== 1) {
672
+ return "Only one role is allowed to be requested for on Okta or Azure apps.";
673
+ }
674
+ return true;
675
+ },
676
+ });
677
+ const Roles = await rolePrompt.run();
678
+ const entry = requestMap[app.id];
679
+ for (const role of Roles) {
680
+ if (!(role.id in entry.assets)) {
681
+ entry.assets[role.id] = {
682
+ assetId: role.id,
683
+ assetName: role.name,
684
+ type: entityTypeFromString(role.type),
685
+ roles: {},
686
+ };
687
+ }
688
+ }
689
+ }
690
+ function appRolesFromEdge(edge) {
691
+ var _a, _b, _c, _d;
692
+ switch (edge.node.__typename) {
693
+ case "Resource": {
694
+ if (edge.node.accessLevels && edge.node.accessLevels.length > 0) {
695
+ return edge.node.accessLevels.map((accessLevel) => ({
696
+ message: accessLevel.accessLevelName || "No Role (Direct access)",
697
+ value: {
698
+ id: edge.node.id + accessLevel.accessLevelRemoteId,
699
+ name: accessLevel.accessLevelName,
700
+ type: graphql_2.EntityType.Resource,
701
+ toString: () => accessLevel.accessLevelName,
702
+ },
703
+ }));
704
+ }
705
+ return [
706
+ {
707
+ message: (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name,
708
+ value: {
709
+ id: edge.node.id,
710
+ name: (_b = edge.alias) !== null && _b !== void 0 ? _b : edge.node.name,
711
+ type: graphql_2.EntityType.Resource,
712
+ toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
713
+ },
714
+ },
715
+ ];
716
+ }
717
+ case "Group":
718
+ return [
719
+ {
720
+ message: (_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name,
721
+ value: {
722
+ id: edge.node.id,
723
+ name: (_d = edge.alias) !== null && _d !== void 0 ? _d : edge.node.name,
724
+ type: graphql_2.EntityType.Group,
725
+ toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
726
+ },
727
+ },
728
+ ];
729
+ }
730
+ }
511
731
  async function chooseAssets(cmd, client, appId, requestMap) {
512
- const initialChoices = (await queryRequestableAssets(cmd, client, appId, "")) || [];
732
+ const initial = (await queryRequestableAssets(cmd, client, appId, "")) || [];
513
733
  const assetPrompt = new AutoComplete({
514
734
  name: "Assets",
515
735
  message: "Select one or more assets:",
736
+ hint: multiSelectInstructions,
516
737
  limit: 15,
517
738
  multiple: true,
518
739
  async choices(input) {
740
+ if (!input) {
741
+ return initial;
742
+ }
519
743
  const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
520
- return filteredChoices || initialChoices;
744
+ return filteredChoices || initial;
521
745
  },
522
746
  validate: (answer) => {
523
747
  if (answer.length < 1) {
@@ -534,6 +758,7 @@ async function chooseAssets(cmd, client, appId, requestMap) {
534
758
  }
535
759
  if (!(asset.id in entry.assets)) {
536
760
  entry.assets[asset.id] = {
761
+ assetId: asset.id,
537
762
  assetName: asset.name,
538
763
  type: asset.type,
539
764
  roles: {},
@@ -558,6 +783,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
558
783
  const rolePrompt = new AutoComplete({
559
784
  name: "Roles",
560
785
  message: `Select one or more roles for ${assetEntry.assetName}:`,
786
+ hint: multiSelectInstructions,
561
787
  limit: 15,
562
788
  multiple: true,
563
789
  choices: assetRoles,
@@ -574,6 +800,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
574
800
  }
575
801
  for (const role of roles) {
576
802
  assetEntry.roles[role.id] = {
803
+ roleId: role.id,
577
804
  roleName: role.name,
578
805
  };
579
806
  }
@@ -602,14 +829,14 @@ async function setRequestDefaults(cmd, client, metadata) {
602
829
  const roleIds = mappedRoles.length ? mappedRoles : [""];
603
830
  for (const roleId of roleIds) {
604
831
  switch (assetNode.type) {
605
- case "Resource": {
832
+ case graphql_2.EntityType.Resource: {
606
833
  requestedResources.push({
607
834
  resourceId: assetId,
608
835
  accessLevelRemoteId: roleId,
609
836
  });
610
837
  break;
611
838
  }
612
- case "Group": {
839
+ case graphql_2.EntityType.Group: {
613
840
  requestedGroups.push({
614
841
  groupId: assetId,
615
842
  accessLevelRemoteId: roleId,
@@ -647,7 +874,7 @@ async function promptForReason(metadata) {
647
874
  const { reason } = await prompt([
648
875
  {
649
876
  name: "reason",
650
- message: "I need access to this because...",
877
+ message: "Why do you need access?",
651
878
  type: "input",
652
879
  validate: (answer) => {
653
880
  if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
@@ -769,77 +996,149 @@ async function setCustomDuration(metadata) {
769
996
  label: durationLabel,
770
997
  };
771
998
  }
772
- async function submitFinalRequest(cmd, client, metadata) {
773
- var _a, _b, _c, _d;
999
+ async function promptRequestSubmission(cmd, metadata) {
1000
+ (0, displays_1.displayFinalRequestSummary)(cmd, metadata);
774
1001
  const submitMessage = "āœ… Yes, submit request";
775
1002
  const cancelMessage = "āŒ No, cancel request";
776
- const { submit } = await inquirer.prompt([
777
- {
778
- name: "submit",
779
- message: "Submit request?",
780
- type: "list",
781
- choices: [submitMessage, cancelMessage],
782
- },
783
- ]);
784
- switch (submit) {
785
- case submitMessage: {
786
- // Build requested assets lists for the mutation
787
- const requestedResources = [];
788
- const requestedGroups = [];
789
- for (const appNode of Object.values(metadata.requestMap)) {
790
- for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
791
- if (assetNode.roles !== undefined) {
792
- const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
793
- return roleId;
794
- });
795
- const roleIds = mappedRoles.length ? mappedRoles : [""];
796
- for (const roleId of roleIds) {
797
- switch (assetNode.type) {
798
- case "Resource": {
799
- requestedResources.push({
800
- resourceId: assetId,
801
- accessLevel: {
802
- accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
803
- accessLevelRemoteId: roleId,
804
- },
805
- });
806
- break;
807
- }
808
- case "Group": {
809
- requestedGroups.push({
810
- groupId: assetId,
811
- accessLevel: {
812
- accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
813
- accessLevelRemoteId: roleId,
814
- },
815
- });
816
- break;
817
- }
818
- }
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;
819
1047
  }
820
1048
  }
821
1049
  }
822
1050
  }
823
- const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
824
- // Build link to request
825
- const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
826
- if (resp === null || resp === void 0 ? void 0 : resp.id) {
827
- cmd.log("\nšŸŽ‰ Your Access Request has been submitted!\n");
828
- cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
829
- if (resp === null || resp === void 0 ? void 0 : resp.status) {
830
- cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
1051
+ }
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));
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`);
1064
+ }
1065
+ return;
1066
+ }
1067
+ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
1068
+ var _a, _b;
1069
+ try {
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(":");
1073
+ const resp = await client.query({
1074
+ query: CATALOG_ITEM,
1075
+ variables: {
1076
+ uuid: assetId || "",
1077
+ },
1078
+ fetchPolicy: "network-only", // to avoid caching
1079
+ });
1080
+ switch (resp.data.catalogItem.__typename) {
1081
+ case "Group":
1082
+ case "Resource": {
1083
+ const item = resp.data.catalogItem;
1084
+ const assetName = item.__typename === "Resource" ? item.displayName : item.name;
1085
+ const requestableRoles = (item.accessLevels || [])
1086
+ // TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
1087
+ .map((role) => ({
1088
+ id: role.accessLevelRemoteId,
1089
+ name: role.accessLevelName,
1090
+ }));
1091
+ const appId = ((_a = item.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
1092
+ if (!(appId in metadata.requestMap)) {
1093
+ metadata.requestMap[appId] = {
1094
+ appName: ((_b = item.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
1095
+ appId: appId,
1096
+ assets: {},
1097
+ };
1098
+ }
1099
+ const assetEntry = metadata.requestMap[appId].assets[id];
1100
+ if (!assetEntry) {
1101
+ metadata.requestMap[appId].assets[assetId] = {
1102
+ assetId: assetId,
1103
+ assetName: assetName,
1104
+ type: entityTypeFromString(item.__typename),
1105
+ roles: {},
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,
1118
+ };
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
+ }
1123
+ }
1124
+ break;
831
1125
  }
832
- const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
833
- cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
1126
+ default:
1127
+ cmd.error("Invalid asset id was passed in using the --id flag.");
834
1128
  }
835
- return;
836
- }
837
- case cancelMessage: {
838
- cmd.log("🚫 Access Request has been cancelled.");
839
- return;
840
1129
  }
841
- default: {
842
- cmd.error("Unknown error occurred.");
1130
+ }
1131
+ catch (error) {
1132
+ if (error instanceof Error || typeof error === "string") {
1133
+ cmd.error(error);
843
1134
  }
844
1135
  }
1136
+ return;
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;
845
1144
  }
@@ -28,12 +28,17 @@ function treeifyRequestMap(cmd, requestMap) {
28
28
  for (const [_appId, appNode] of Object.entries(requestMap)) {
29
29
  const assetsTree = {};
30
30
  for (const [_assetId, assetNode] of Object.entries(appNode.assets)) {
31
- const assetKey = `${assetNode.assetName} ${chalk_1.default.dim(`[${assetNode.type}]`)}`;
31
+ // If okta/azure asset with no role, change asset name
32
+ const assetName = assetNode.assetName || "No Role (Direct access)";
33
+ const assetKey = `${assetName} ${chalk_1.default.dim(`[${assetNode.type}]`)}`;
32
34
  if (assetNode.roles !== undefined) {
33
35
  assetsTree[assetKey] = {};
34
36
  for (const [_roleId, roleNode] of Object.entries(assetNode.roles)) {
35
- const roleKey = `${roleNode.roleName} ${chalk_1.default.dim("[Role]")}`;
36
- assetsTree[assetKey][roleKey] = null;
37
+ const roleName = roleNode.roleName;
38
+ if (roleName !== "") {
39
+ const roleKey = `${roleName} ${chalk_1.default.dim("[Role]")}`;
40
+ assetsTree[assetKey][roleKey] = null;
41
+ }
37
42
  }
38
43
  }
39
44
  else {