opal-security 3.1.1-beta.9cc3edb → 3.1.1-beta.b652df3

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,20 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DISPLAY_LABELS = void 0;
3
4
  exports.initEmptyRequestMetadata = initEmptyRequestMetadata;
4
5
  exports.selectRequestableItems = selectRequestableItems;
5
- exports.chooseAssets = chooseAssets;
6
- exports.chooseRoles = chooseRoles;
7
6
  exports.doneSelectingAssets = doneSelectingAssets;
8
7
  exports.setRequestDefaults = setRequestDefaults;
9
8
  exports.promptForReason = promptForReason;
10
9
  exports.promptForExpiration = promptForExpiration;
10
+ exports.promptRequestSubmission = promptRequestSubmission;
11
11
  exports.submitFinalRequest = submitFinalRequest;
12
+ exports.bypassRequestSelection = bypassRequestSelection;
13
+ exports.bypassDuration = bypassDuration;
12
14
  const chalk_1 = require("chalk");
13
- const inquirer = require("inquirer");
14
15
  const graphql_1 = require("../graphql");
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
+ const capStr = str === null || str === void 0 ? void 0 : str.toLocaleUpperCase();
22
+ if (capStr === "RESOURCE") {
23
+ return graphql_2.EntityType.Resource;
24
+ }
25
+ if (capStr === "GROUP") {
26
+ return graphql_2.EntityType.Group;
27
+ }
28
+ // if type unknown, default to resource
29
+ return graphql_2.EntityType.Resource;
30
+ }
31
+ exports.DISPLAY_LABELS = {
32
+ [graphql_2.EntityType.Resource]: "Resource",
33
+ [graphql_2.EntityType.Group]: "Group",
34
+ };
18
35
  function initEmptyRequestMetadata() {
19
36
  // Initialize with empty defaults
20
37
  const requestDefaults = {
@@ -94,6 +111,7 @@ async function queryRequestableApps(cmd, client, input) {
94
111
  value: {
95
112
  id: edge.node.id,
96
113
  name: edge.node.displayName,
114
+ type: type,
97
115
  toString: () => edge.node.displayName,
98
116
  },
99
117
  };
@@ -162,11 +180,11 @@ async function queryRequestableAssets(cmd, client, appId, input) {
162
180
  const id = ((_c = item.resource) === null || _c === void 0 ? void 0 : _c.id) || ((_d = item.group) === null || _d === void 0 ? void 0 : _d.id);
163
181
  const type = ((_e = item.resource) === null || _e === void 0 ? void 0 : _e.__typename) || ((_f = item.group) === null || _f === void 0 ? void 0 : _f.__typename);
164
182
  return {
165
- message: `${name} (${type})`,
183
+ message: `${name} [${type}]`,
166
184
  value: {
167
185
  name: name || "",
168
186
  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) || "",
187
+ 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
188
  },
171
189
  };
172
190
  });
@@ -225,7 +243,7 @@ async function queryAssetRoles(cmd, client, assetType, assetId) {
225
243
  var _a, _b, _c, _d, _e, _f, _g, _h;
226
244
  try {
227
245
  switch (assetType) {
228
- case "Resource": {
246
+ case graphql_2.EntityType.Resource: {
229
247
  const resp = await client.query({
230
248
  query: RESOURCE_ROLES_QUERY,
231
249
  variables: {
@@ -254,7 +272,7 @@ async function queryAssetRoles(cmd, client, assetType, assetId) {
254
272
  }
255
273
  return;
256
274
  }
257
- case "Group": {
275
+ case graphql_2.EntityType.Group: {
258
276
  const resp = await client.query({
259
277
  query: GROUP_ROLES_QUERY,
260
278
  variables: {
@@ -426,58 +444,181 @@ async function createRequest(cmd, client, requestedResources, requestedGroups, r
426
444
  durationInMinutes: durationInMinutes,
427
445
  },
428
446
  });
429
- let x;
430
447
  switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.createRequest.__typename) {
431
448
  case "CreateRequestResult":
432
449
  return (_b = resp.data) === null || _b === void 0 ? void 0 : _b.createRequest.request;
433
450
  case "RequestDurationTooLargeError":
434
- x = cmd.error((_c = resp.data) === null || _c === void 0 ? void 0 : _c.createRequest.message);
451
+ cmd.log((_c = resp.data) === null || _c === void 0 ? void 0 : _c.createRequest.message);
435
452
  break;
436
453
  case "RequestRequiresUserAuthTokenForConnectionError":
437
- x = cmd.error((_d = resp.data) === null || _d === void 0 ? void 0 : _d.createRequest.message);
454
+ cmd.log((_d = resp.data) === null || _d === void 0 ? void 0 : _d.createRequest.message);
438
455
  break;
439
456
  case "NoReviewersSetForOwnerError":
440
- x = cmd.error((_e = resp.data) === null || _e === void 0 ? void 0 : _e.createRequest.message);
457
+ cmd.log((_e = resp.data) === null || _e === void 0 ? void 0 : _e.createRequest.message);
441
458
  break;
442
459
  case "NoReviewersSetForResourceError":
443
- x = cmd.error((_f = resp.data) === null || _f === void 0 ? void 0 : _f.createRequest.message);
460
+ cmd.log((_f = resp.data) === null || _f === void 0 ? void 0 : _f.createRequest.message);
444
461
  break;
445
462
  case "NoReviewersSetForGroupError":
446
- x = cmd.error((_g = resp.data) === null || _g === void 0 ? void 0 : _g.createRequest.message);
463
+ cmd.log((_g = resp.data) === null || _g === void 0 ? void 0 : _g.createRequest.message);
447
464
  break;
448
465
  case "NoManagerSetForRequestingUserError":
449
- x = cmd.error((_h = resp.data) === null || _h === void 0 ? void 0 : _h.createRequest.message);
466
+ cmd.log((_h = resp.data) === null || _h === void 0 ? void 0 : _h.createRequest.message);
450
467
  break;
451
468
  case "MfaInvalidError":
452
- x = cmd.error((_j = resp.data) === null || _j === void 0 ? void 0 : _j.createRequest.message);
469
+ cmd.log((_j = resp.data) === null || _j === void 0 ? void 0 : _j.createRequest.message);
453
470
  break;
454
471
  case "BulkRequestTooLargeError":
455
- x = cmd.error((_k = resp.data) === null || _k === void 0 ? void 0 : _k.createRequest.message);
472
+ cmd.log((_k = resp.data) === null || _k === void 0 ? void 0 : _k.createRequest.message);
456
473
  break;
457
474
  case "ItemCannotBeRequestedError":
458
- x = cmd.error((_l = resp.data) === null || _l === void 0 ? void 0 : _l.createRequest.message);
475
+ cmd.log((_l = resp.data) === null || _l === void 0 ? void 0 : _l.createRequest.message);
459
476
  break;
460
477
  case "UserCannotRequestAccessForTargetGroupError":
461
- x = cmd.error((_m = resp.data) === null || _m === void 0 ? void 0 : _m.createRequest.message);
478
+ cmd.log((_m = resp.data) === null || _m === void 0 ? void 0 : _m.createRequest.message);
462
479
  break;
463
480
  case "GroupNestingNotAllowedError":
464
- x = cmd.error((_o = resp.data) === null || _o === void 0 ? void 0 : _o.createRequest.message);
481
+ cmd.log((_o = resp.data) === null || _o === void 0 ? void 0 : _o.createRequest.message);
465
482
  break;
466
483
  case "TargetUserHasNestedAccessError":
467
- x = cmd.error((_p = resp.data) === null || _p === void 0 ? void 0 : _p.createRequest.message);
484
+ cmd.log((_p = resp.data) === null || _p === void 0 ? void 0 : _p.createRequest.message);
468
485
  break;
469
486
  case "RequestReasonMissingError":
470
- x = cmd.error((_q = resp.data) === null || _q === void 0 ? void 0 : _q.createRequest.message);
487
+ cmd.log((_q = resp.data) === null || _q === void 0 ? void 0 : _q.createRequest.message);
471
488
  break;
472
489
  case "RequestFieldValueMissingError":
473
- x = cmd.error((_r = resp.data) === null || _r === void 0 ? void 0 : _r.createRequest.message);
490
+ cmd.log((_r = resp.data) === null || _r === void 0 ? void 0 : _r.createRequest.message);
474
491
  break;
475
492
  case "LinkedGroupNotRequestableError":
476
- x = cmd.error((_s = resp.data) === null || _s === void 0 ? void 0 : _s.createRequest.message);
493
+ cmd.log((_s = resp.data) === null || _s === void 0 ? void 0 : _s.createRequest.message);
477
494
  break;
478
495
  case "RequestReasonBelowMinLengthError":
479
- x = cmd.error((_t = resp.data) === null || _t === void 0 ? void 0 : _t.createRequest.message);
496
+ cmd.log((_t = resp.data) === null || _t === void 0 ? void 0 : _t.createRequest.message);
497
+ break;
498
+ }
499
+ }
500
+ catch (error) {
501
+ if (error instanceof Error || typeof error === "string") {
502
+ cmd.error(error);
503
+ }
504
+ }
505
+ }
506
+ const CATALOG_ITEM = (0, graphql_1.graphql)(`
507
+ query GetCatalogItem($uuid: UUID!) {
508
+ catalogItem(id: $uuid) {
509
+ __typename
510
+ ... on Connection {
511
+ id
512
+ displayName
513
+ }
514
+ ... on Resource {
515
+ id
516
+ displayName
517
+ connection {
518
+ id
519
+ displayName
520
+ }
521
+ accessLevels{
522
+ accessLevelName
523
+ accessLevelRemoteId
524
+ }
525
+ }
526
+ ...on Group {
527
+ id
528
+ name
529
+ connection {
530
+ id
531
+ displayName
532
+ }
533
+ accessLevels{
534
+ accessLevelName
535
+ accessLevelRemoteId
536
+ }
537
+ }
538
+ ... on UserFacingError {
539
+ message
540
+ }
541
+ }
542
+ }
543
+ `);
544
+ const ASSOCIATED_ITEMS_QUERY = (0, graphql_1.graphql)(`
545
+ query GetAssociatedItems($resourceId: ResourceId!, $searchQuery: String) {
546
+ resource(input: {
547
+ id: $resourceId
548
+ }) {
549
+ __typename
550
+ ... on ResourceResult {
551
+ __typename
552
+ resource {
553
+ associatedItems(
554
+ first: 200
555
+ filters: {
556
+ searchQuery: {
557
+ contains: $searchQuery
558
+ }
559
+ access: REQUESTABLE
560
+ endUserVisible: true
561
+ entityType: {
562
+ in: [GROUP, RESOURCE]
563
+ }
564
+ }
565
+ ) {
566
+ edges {
567
+ __typename
568
+ ... on ResourceAssociatedItemEdge {
569
+ alias
570
+ node {
571
+ __typename
572
+ id
573
+ name
574
+ ... on Resource {
575
+ accessLevels(
576
+ filters: {
577
+ skipRemoteAccessLevels: false # azure app roles are remote
578
+ }
579
+ ) {
580
+ __typename
581
+ accessLevelName
582
+ accessLevelRemoteId
583
+ }
584
+ }
585
+ }
586
+ }
587
+ }
588
+ }
589
+ }
590
+ }
591
+ ... on ResourceNotFoundError {
592
+ message
593
+ }
594
+ }
595
+ }
596
+ `);
597
+ async function queryAssociatedItems(cmd, client, id, input) {
598
+ var _a, _b;
599
+ try {
600
+ const resp = await client.query({
601
+ query: ASSOCIATED_ITEMS_QUERY,
602
+ variables: {
603
+ resourceId: id || "",
604
+ searchQuery: input || "",
605
+ },
606
+ fetchPolicy: "network-only", // to avoid caching
607
+ });
608
+ switch (resp.data.resource.__typename) {
609
+ case "ResourceResult": {
610
+ const associatedItems = resp.data.resource.resource.associatedItems.edges.filter((edge) => edge.__typename === "ResourceAssociatedItemEdge");
611
+ const initial = [];
612
+ for (const edge of associatedItems) {
613
+ initial.push(...appRolesFromEdge(edge));
614
+ }
615
+ return initial;
616
+ }
617
+ case "ResourceNotFoundError":
618
+ cmd.log((_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.resource) === null || _b === void 0 ? void 0 : _b.message);
480
619
  break;
620
+ default:
621
+ cmd.error(resp.error || "Unknown error occurred.");
481
622
  }
482
623
  }
483
624
  catch (error) {
@@ -487,38 +628,125 @@ async function createRequest(cmd, client, requestedResources, requestedGroups, r
487
628
  }
488
629
  }
489
630
  // Helper functions
631
+ const selectInstructions = chalk_1.default.dim("[↑↓] Navigate Ā· [Enter] Select Ā· Type to filter");
632
+ const multiSelectInstructions = chalk_1.default.dim("[↑↓] Navigate Ā· [Space] Select Ā· [Enter] Confirm Ā· Type to filter");
490
633
  async function selectRequestableItems(cmd, client, requestMap) {
491
- const initialChoices = (await queryRequestableApps(cmd, client, "")) || [];
634
+ const initial = (await queryRequestableApps(cmd, client, "")) || [];
492
635
  const appPrompt = new AutoComplete({
493
636
  name: "App",
494
- message: "Select an app:",
637
+ message: "Select an app",
638
+ hint: selectInstructions,
495
639
  limit: 15,
496
- choices: initialChoices,
640
+ choices: initial,
497
641
  async suggest(input) {
498
642
  const filteredChoices = await queryRequestableApps(cmd, client, input || "");
499
- return filteredChoices || initialChoices;
643
+ return filteredChoices || initial;
500
644
  },
501
645
  });
502
646
  const App = await appPrompt.run();
503
647
  // Set the app in the requestMap and call choose assets step
504
648
  if (!(App.id in requestMap)) {
505
649
  requestMap[App.id] = {
650
+ appId: App.id,
506
651
  appName: App.name,
507
652
  assets: {},
508
653
  };
509
654
  }
655
+ if (App.type === "OKTA_APP" || App.type === "AZURE_ENTERPRISE_APP") {
656
+ await chooseOktaAzureRoles(cmd, client, App, requestMap);
657
+ return;
658
+ }
510
659
  await chooseAssets(cmd, client, App.id, requestMap);
511
660
  }
661
+ async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
662
+ const associatedItems = (await queryAssociatedItems(cmd, client, app.id, "")) || [];
663
+ const rolePrompt = new AutoComplete({
664
+ name: "Roles",
665
+ message: `Select a role for ${app.name}:`,
666
+ hint: multiSelectInstructions,
667
+ limit: 15,
668
+ multiple: true,
669
+ async choices(input) {
670
+ if (!input)
671
+ return associatedItems;
672
+ const filteredChoices = await queryAssociatedItems(cmd, client, app.id, input);
673
+ return filteredChoices || associatedItems;
674
+ },
675
+ validate: (answer) => {
676
+ if (answer.length !== 1) {
677
+ return "Only one role is allowed to be requested for on Okta or Azure apps.";
678
+ }
679
+ return true;
680
+ },
681
+ });
682
+ const Roles = await rolePrompt.run();
683
+ const entry = requestMap[app.id];
684
+ for (const role of Roles) {
685
+ if (!(role.id in entry.assets)) {
686
+ entry.assets[role.id] = {
687
+ assetId: role.id,
688
+ assetName: role.name,
689
+ type: entityTypeFromString(role.type),
690
+ roles: {},
691
+ };
692
+ }
693
+ }
694
+ }
695
+ function appRolesFromEdge(edge) {
696
+ var _a, _b, _c, _d;
697
+ switch (edge.node.__typename) {
698
+ case "Resource": {
699
+ if (edge.node.accessLevels && edge.node.accessLevels.length > 0) {
700
+ return edge.node.accessLevels.map((accessLevel) => ({
701
+ message: accessLevel.accessLevelName || "No Role (Direct access)",
702
+ value: {
703
+ id: edge.node.id + accessLevel.accessLevelRemoteId,
704
+ name: accessLevel.accessLevelName,
705
+ type: exports.DISPLAY_LABELS[graphql_2.EntityType.Resource],
706
+ toString: () => accessLevel.accessLevelName,
707
+ },
708
+ }));
709
+ }
710
+ return [
711
+ {
712
+ message: (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name,
713
+ value: {
714
+ id: edge.node.id,
715
+ name: (_b = edge.alias) !== null && _b !== void 0 ? _b : edge.node.name,
716
+ type: exports.DISPLAY_LABELS[graphql_2.EntityType.Resource],
717
+ toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
718
+ },
719
+ },
720
+ ];
721
+ }
722
+ case "Group":
723
+ return [
724
+ {
725
+ message: `${(_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name} ${graphql_2.EntityType.Group}`,
726
+ value: {
727
+ id: edge.node.id,
728
+ name: (_d = edge.alias) !== null && _d !== void 0 ? _d : edge.node.name,
729
+ type: exports.DISPLAY_LABELS[graphql_2.EntityType.Group],
730
+ toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
731
+ },
732
+ },
733
+ ];
734
+ }
735
+ }
512
736
  async function chooseAssets(cmd, client, appId, requestMap) {
513
- const initialChoices = (await queryRequestableAssets(cmd, client, appId, "")) || [];
737
+ const initial = (await queryRequestableAssets(cmd, client, appId, "")) || [];
514
738
  const assetPrompt = new AutoComplete({
515
739
  name: "Assets",
516
740
  message: "Select one or more assets:",
741
+ hint: multiSelectInstructions,
517
742
  limit: 15,
518
743
  multiple: true,
519
744
  async choices(input) {
745
+ if (!input) {
746
+ return initial;
747
+ }
520
748
  const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
521
- return filteredChoices || initialChoices;
749
+ return filteredChoices || initial;
522
750
  },
523
751
  validate: (answer) => {
524
752
  if (answer.length < 1) {
@@ -535,6 +763,7 @@ async function chooseAssets(cmd, client, appId, requestMap) {
535
763
  }
536
764
  if (!(asset.id in entry.assets)) {
537
765
  entry.assets[asset.id] = {
766
+ assetId: asset.id,
538
767
  assetName: asset.name,
539
768
  type: asset.type,
540
769
  roles: {},
@@ -559,6 +788,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
559
788
  const rolePrompt = new AutoComplete({
560
789
  name: "Roles",
561
790
  message: `Select one or more roles for ${assetEntry.assetName}:`,
791
+ hint: multiSelectInstructions,
562
792
  limit: 15,
563
793
  multiple: true,
564
794
  choices: assetRoles,
@@ -575,6 +805,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
575
805
  }
576
806
  for (const role of roles) {
577
807
  assetEntry.roles[role.id] = {
808
+ roleId: role.id,
578
809
  roleName: role.name,
579
810
  };
580
811
  }
@@ -603,14 +834,14 @@ async function setRequestDefaults(cmd, client, metadata) {
603
834
  const roleIds = mappedRoles.length ? mappedRoles : [""];
604
835
  for (const roleId of roleIds) {
605
836
  switch (assetNode.type) {
606
- case "Resource": {
837
+ case graphql_2.EntityType.Resource: {
607
838
  requestedResources.push({
608
839
  resourceId: assetId,
609
840
  accessLevelRemoteId: roleId,
610
841
  });
611
842
  break;
612
843
  }
613
- case "Group": {
844
+ case graphql_2.EntityType.Group: {
614
845
  requestedGroups.push({
615
846
  groupId: assetId,
616
847
  accessLevelRemoteId: roleId,
@@ -648,7 +879,7 @@ async function promptForReason(metadata) {
648
879
  const { reason } = await prompt([
649
880
  {
650
881
  name: "reason",
651
- message: "I need access to this because...",
882
+ message: "Why do you need access?",
652
883
  type: "input",
653
884
  validate: (answer) => {
654
885
  if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
@@ -663,10 +894,16 @@ async function promptForReason(metadata) {
663
894
  async function promptForExpiration(metadata) {
664
895
  var _a, _b;
665
896
  const durations = ((_b = (_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.durationOptions) === null || _b === void 0 ? void 0 : _b.map((option) => {
666
- const label = option.durationInMinutes ===
667
- metadata.requestDefaults.recommendedDurationInMinutes
668
- ? `${option.label} (Recommended)`
669
- : option.label;
897
+ var _a;
898
+ let label = option.label;
899
+ if (option.durationInMinutes ===
900
+ ((_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.maxDurationInMinutes)) {
901
+ label = `${label} (MAX)`;
902
+ }
903
+ if (option.durationInMinutes ===
904
+ metadata.requestDefaults.recommendedDurationInMinutes) {
905
+ label = `${label} (RECOMMENDED)`;
906
+ }
670
907
  return {
671
908
  message: label,
672
909
  value: {
@@ -681,6 +918,7 @@ async function promptForExpiration(metadata) {
681
918
  const expirationSelect = new AutoComplete({
682
919
  name: "expiration",
683
920
  message: "When should access expire?",
921
+ hint: "Type to filter",
684
922
  type: "list",
685
923
  choices: durations,
686
924
  pageSize: 15,
@@ -688,7 +926,7 @@ async function promptForExpiration(metadata) {
688
926
  let selected = await expirationSelect.run();
689
927
  switch (selected.label) {
690
928
  case "Custom": {
691
- selected = await setCustomDuration();
929
+ selected = await setCustomDuration(metadata);
692
930
  break;
693
931
  }
694
932
  case "Permanent": {
@@ -699,7 +937,36 @@ async function promptForExpiration(metadata) {
699
937
  metadata.durationInMinutes = selected.durationInMinutes;
700
938
  metadata.durationLabel = selected.label;
701
939
  }
702
- async function setCustomDuration() {
940
+ function getDurationNumbers(duration) {
941
+ const d = +duration.days || 0;
942
+ const h = +duration.hours || 0;
943
+ const m = +duration.minutes || 0;
944
+ return { d, h, m };
945
+ }
946
+ function getDurationInMinutes(duration) {
947
+ const { d, h, m } = getDurationNumbers(duration);
948
+ const minutesInDay = 1440; // 24 hours * 60 minutes
949
+ const minutesInHour = 60;
950
+ return d * minutesInDay + h * minutesInHour + m;
951
+ }
952
+ function getDHMFromMinutes(minutes) {
953
+ const label = [];
954
+ const d = Math.floor(minutes / 1440);
955
+ if (d > 0) {
956
+ label.push(`${d}d`);
957
+ }
958
+ const remainingMinutes = minutes % 1440;
959
+ const h = Math.floor(remainingMinutes / 60);
960
+ if (h > 0) {
961
+ label.push(`${h}h`);
962
+ }
963
+ const m = remainingMinutes % 60;
964
+ if (m > 0) {
965
+ label.push(`${m}m`);
966
+ }
967
+ return label.join(" ");
968
+ }
969
+ async function setCustomDuration(metadata) {
703
970
  const durationForm = new Form({
704
971
  name: "user",
705
972
  message: "Please set a custom access duration:",
@@ -709,107 +976,177 @@ async function setCustomDuration() {
709
976
  { name: "minutes", message: "Minutes", initial: "0" },
710
977
  ],
711
978
  validate: (answer) => {
712
- const days = Number.parseInt(answer.days);
713
- const hours = Number.parseInt(answer.hours);
714
- const minutes = Number.parseInt(answer.minutes);
715
- if (days < 0 ||
716
- hours < 0 ||
717
- minutes < 0 ||
718
- (days === 0 && hours === 0 && minutes === 0)) {
979
+ var _a, _b, _c;
980
+ const { d, h, m } = getDurationNumbers(answer);
981
+ const durationInMinutes = getDurationInMinutes(answer);
982
+ if (d < 0 || h < 0 || m < 0 || d + h + m === 0 || (h > 23 && m > 59)) {
719
983
  return "Please enter a valid duration.";
720
984
  }
985
+ if (((_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.maxDurationInMinutes) &&
986
+ durationInMinutes > ((_b = metadata.requestDefaults) === null || _b === void 0 ? void 0 : _b.maxDurationInMinutes)) {
987
+ const maxDHM = getDHMFromMinutes((_c = metadata.requestDefaults) === null || _c === void 0 ? void 0 : _c.maxDurationInMinutes);
988
+ return `The max duration for the selected assets is ${maxDHM}.`;
989
+ }
721
990
  return true;
722
991
  },
992
+ return: (answer) => {
993
+ return getDurationInMinutes(answer);
994
+ },
723
995
  });
724
- const duration = await durationForm.run();
725
- // Convert to minutes, define the new custom label
726
- const durationInMinutes = duration.days * 1440 + duration.hours * 60 + duration.minutes;
727
- let durationLabel = "";
728
- if (duration.days > 0) {
729
- durationLabel += `${duration.days}d `;
730
- }
731
- if (duration.hours > 0) {
732
- durationLabel += `${duration.hours}h `;
733
- }
734
- if (duration.minutes > 0) {
735
- durationLabel += `${duration.minutes}m`;
736
- }
996
+ const durationResult = await durationForm.run();
997
+ const { d, h, m } = getDurationNumbers(durationResult);
998
+ const durationInMinutes = getDurationInMinutes(durationResult);
999
+ const durationLabel = getDHMFromMinutes(durationInMinutes);
737
1000
  return {
738
1001
  durationInMinutes: durationInMinutes,
739
1002
  label: durationLabel,
740
1003
  };
741
1004
  }
742
- async function submitFinalRequest(cmd, client, metadata) {
743
- var _a, _b, _c, _d;
1005
+ async function promptRequestSubmission(cmd, metadata) {
1006
+ (0, displays_1.displayFinalRequestSummary)(cmd, metadata);
744
1007
  const submitMessage = "āœ… Yes, submit request";
745
1008
  const cancelMessage = "āŒ No, cancel request";
746
- const { submit } = await inquirer.prompt([
747
- {
748
- name: "submit",
749
- message: "Submit request?",
750
- type: "list",
751
- choices: [submitMessage, cancelMessage],
752
- },
753
- ]);
754
- switch (submit) {
755
- case submitMessage: {
756
- // Build requested assets lists for the mutation
757
- const requestedResources = [];
758
- const requestedGroups = [];
759
- for (const appNode of Object.values(metadata.requestMap)) {
760
- for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
761
- if (assetNode.roles !== undefined) {
762
- const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
763
- return roleId;
764
- });
765
- const roleIds = mappedRoles.length ? mappedRoles : [""];
766
- for (const roleId of roleIds) {
767
- switch (assetNode.type) {
768
- case "Resource": {
769
- requestedResources.push({
770
- resourceId: assetId,
771
- accessLevel: {
772
- accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
773
- accessLevelRemoteId: roleId,
774
- },
775
- });
776
- break;
777
- }
778
- case "Group": {
779
- requestedGroups.push({
780
- groupId: assetId,
781
- accessLevel: {
782
- accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
783
- accessLevelRemoteId: roleId,
784
- },
785
- });
786
- break;
787
- }
788
- }
1009
+ const prompt = new Select({
1010
+ name: "submitOrCancel",
1011
+ message: "Is this all you want to request?",
1012
+ choices: [submitMessage, cancelMessage],
1013
+ });
1014
+ const submitOrCancel = await prompt.run();
1015
+ return submitOrCancel === submitMessage;
1016
+ }
1017
+ async function submitFinalRequest(cmd, client, metadata) {
1018
+ var _a, _b, _c, _d;
1019
+ // Build requested assets lists for the mutation
1020
+ const requestedResources = [];
1021
+ const requestedGroups = [];
1022
+ for (const appNode of Object.values(metadata.requestMap)) {
1023
+ // This extraction is different than the one in setRequestDefaults.
1024
+ // Both extract the requestedResources and requestedGroups,
1025
+ // use different formats.
1026
+ for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
1027
+ if (assetNode.roles) {
1028
+ const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
1029
+ return roleId;
1030
+ });
1031
+ const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
1032
+ for (const roleId of roleIds) {
1033
+ switch (assetNode.type) {
1034
+ case graphql_2.EntityType.Resource: {
1035
+ requestedResources.push({
1036
+ resourceId: assetId,
1037
+ accessLevel: {
1038
+ accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
1039
+ accessLevelRemoteId: roleId,
1040
+ },
1041
+ });
1042
+ break;
1043
+ }
1044
+ case graphql_2.EntityType.Group: {
1045
+ requestedGroups.push({
1046
+ groupId: assetId,
1047
+ accessLevel: {
1048
+ accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
1049
+ accessLevelRemoteId: roleId,
1050
+ },
1051
+ });
1052
+ break;
789
1053
  }
790
1054
  }
791
1055
  }
792
1056
  }
793
- const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
794
- // Build link to request
795
- const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
796
- if (resp === null || resp === void 0 ? void 0 : resp.id) {
797
- cmd.log("\nšŸŽ‰ Your Access Request has been submitted!\n");
798
- cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
799
- if (resp === null || resp === void 0 ? void 0 : resp.status) {
800
- cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
1057
+ }
1058
+ }
1059
+ const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
1060
+ // Build link to request
1061
+ const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
1062
+ if (resp === null || resp === void 0 ? void 0 : resp.id) {
1063
+ cmd.log("\nšŸŽ‰ Your Access Request has been submitted!\n");
1064
+ cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
1065
+ if (resp === null || resp === void 0 ? void 0 : resp.status) {
1066
+ cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
1067
+ }
1068
+ const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
1069
+ cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
1070
+ }
1071
+ return;
1072
+ }
1073
+ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
1074
+ var _a, _b;
1075
+ try {
1076
+ // Query Catalog Item endpoint to identify what the id belongs to (resource or group)
1077
+ for (const val of flagValue) {
1078
+ const delimiterIndex = val.indexOf(":");
1079
+ const assetId = val.substring(0, delimiterIndex);
1080
+ const roleName = val.substring(delimiterIndex + 1);
1081
+ const resp = await client.query({
1082
+ query: CATALOG_ITEM,
1083
+ variables: {
1084
+ uuid: assetId || "",
1085
+ },
1086
+ fetchPolicy: "network-only", // to avoid caching
1087
+ });
1088
+ switch (resp.data.catalogItem.__typename) {
1089
+ case "Group":
1090
+ case "Resource": {
1091
+ const item = resp.data.catalogItem;
1092
+ const assetName = item.__typename === "Resource" ? item.displayName : item.name;
1093
+ const requestableRoles = (item.accessLevels || [])
1094
+ // TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
1095
+ .map((role) => ({
1096
+ id: role.accessLevelRemoteId,
1097
+ name: role.accessLevelName,
1098
+ }));
1099
+ const appId = ((_a = item.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
1100
+ if (!(appId in metadata.requestMap)) {
1101
+ metadata.requestMap[appId] = {
1102
+ appName: ((_b = item.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
1103
+ appId: appId,
1104
+ assets: {},
1105
+ };
1106
+ }
1107
+ const assetEntry = metadata.requestMap[appId].assets[assetId];
1108
+ if (!assetEntry) {
1109
+ metadata.requestMap[appId].assets[assetId] = {
1110
+ assetId: assetId,
1111
+ assetName: assetName,
1112
+ type: entityTypeFromString(item.__typename),
1113
+ roles: {},
1114
+ };
1115
+ }
1116
+ if (requestableRoles.length > 0 &&
1117
+ !(requestableRoles.length === 1 && requestableRoles[0].name === "")) {
1118
+ const selectedRole = requestableRoles.find((role) => role.name === roleName);
1119
+ if (selectedRole !== undefined) {
1120
+ if (!metadata.requestMap[appId].assets[assetId].roles) {
1121
+ metadata.requestMap[appId].assets[assetId].roles = {};
1122
+ }
1123
+ metadata.requestMap[appId].assets[assetId].roles[selectedRole.id] = {
1124
+ roleId: selectedRole.id,
1125
+ roleName: selectedRole.name,
1126
+ };
1127
+ }
1128
+ else {
1129
+ cmd.error(`Access level specified does not match one of ${assetName}'s defined access levels: ${requestableRoles.map((role) => `"${role.name}"`)}`);
1130
+ }
1131
+ }
1132
+ break;
801
1133
  }
802
- const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
803
- cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
1134
+ default:
1135
+ cmd.error("Invalid asset id was passed in using the --id flag.");
804
1136
  }
805
- return;
806
- }
807
- case cancelMessage: {
808
- cmd.log("🚫 Access Request has been cancelled.");
809
- return;
810
1137
  }
811
- default: {
812
- cmd.error("Unknown error occurred.");
1138
+ }
1139
+ catch (error) {
1140
+ if (error instanceof Error || typeof error === "string") {
1141
+ cmd.error(error);
813
1142
  }
814
1143
  }
1144
+ return;
1145
+ }
1146
+ function bypassDuration(cmd, duration, metadata) {
1147
+ const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
1148
+ if (maxDuration && duration > maxDuration) {
1149
+ cmd.error(`The requested duration exceeds the allowed limit of ${maxDuration}`);
1150
+ }
1151
+ metadata.durationInMinutes = duration;
815
1152
  }