opal-security 3.1.1-beta.e5e99da → 3.1.1-beta.f2bce00

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.
@@ -7,6 +7,7 @@ interface AppNode {
7
7
  }
8
8
  interface AssetNode {
9
9
  assetName: string;
10
+ type: string;
10
11
  roles?: Record<string, RoleNode>;
11
12
  }
12
13
  interface RoleNode {
@@ -11,7 +11,7 @@ exports.promptForExpiration = promptForExpiration;
11
11
  exports.submitFinalRequest = submitFinalRequest;
12
12
  const inquirer = require("inquirer");
13
13
  const graphql_1 = require("../graphql");
14
- inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
14
+ const { AutoComplete, Select, prompt, Form } = require("enquirer");
15
15
  function createEmptyRequestMetadata() {
16
16
  // Initialize with empty defaults
17
17
  const requestDefaults = {
@@ -73,18 +73,23 @@ async function queryRequestableApps(cmd, client, input) {
73
73
  });
74
74
  return (_b = (_a = resp === null || resp === void 0 ? void 0 : resp.data) === null || _a === void 0 ? void 0 : _a.appsV2) === null || _b === void 0 ? void 0 : _b.edges.map((edge) => {
75
75
  let type = undefined;
76
- if (edge.node.__typename === "Resource") {
77
- type = edge.node.resourceType;
78
- }
79
- if (edge.node.__typename === "Connection") {
80
- type = edge.node.connectionType;
76
+ switch (edge.node.__typename) {
77
+ case "Resource":
78
+ type = edge.node.resourceType;
79
+ break;
80
+ case "Connection":
81
+ type = edge.node.connectionType;
82
+ break;
83
+ default:
84
+ type = edge.node.__typename;
81
85
  }
82
86
  const label = `${edge.node.displayName} (${type})`;
83
87
  return {
84
- name: label,
88
+ message: label,
85
89
  value: {
86
90
  id: edge.node.id,
87
91
  name: label,
92
+ toString: () => label,
88
93
  },
89
94
  };
90
95
  });
@@ -147,16 +152,17 @@ async function queryRequestableAssets(cmd, client, appId, input) {
147
152
  switch (resp.data.app.__typename) {
148
153
  case "App":
149
154
  return (_d = (_c = (_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.app) === null || _b === void 0 ? void 0 : _b.items) === null || _c === void 0 ? void 0 : _c.items) === null || _d === void 0 ? void 0 : _d.map((item) => {
150
- var _a, _b, _c, _d, _e, _f;
155
+ var _a, _b, _c, _d, _e, _f, _g, _h;
151
156
  const name = ((_a = item.resource) === null || _a === void 0 ? void 0 : _a.name) || ((_b = item.group) === null || _b === void 0 ? void 0 : _b.name);
152
157
  const id = ((_c = item.resource) === null || _c === void 0 ? void 0 : _c.id) || ((_d = item.group) === null || _d === void 0 ? void 0 : _d.id);
153
158
  const type = ((_e = item.resource) === null || _e === void 0 ? void 0 : _e.__typename) || ((_f = item.group) === null || _f === void 0 ? void 0 : _f.__typename);
154
159
  const label = `${name} (${type})`;
155
160
  return {
156
- name: label,
161
+ message: label,
157
162
  value: {
158
- name: label,
159
- id: id,
163
+ name: label || "",
164
+ id: id || "",
165
+ type: ((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename) || "",
160
166
  },
161
167
  };
162
168
  });
@@ -182,7 +188,6 @@ const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
182
188
  __typename
183
189
  ... on ResourceAccessLevelsResult {
184
190
  accessLevels {
185
- __typename
186
191
  ... on ResourceAccessLevel {
187
192
  accessLevelName
188
193
  accessLevelRemoteId
@@ -195,34 +200,82 @@ const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
195
200
  }
196
201
  }
197
202
  `);
198
- async function queryResourceRoles(cmd, client, resourceId) {
199
- var _a, _b, _c, _d, _e;
203
+ const GROUP_ROLES_QUERY = (0, graphql_1.graphql)(`
204
+ query GroupAccessLevels($groupId: GroupId!) {
205
+ groupAccessLevels(
206
+ input: { groupId: $groupId }
207
+ ) {
208
+ ... on GroupAccessLevelsResult {
209
+ groupId
210
+ accessLevels {
211
+ ... on GroupAccessLevel {
212
+ accessLevelName
213
+ accessLevelRemoteId
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ `);
220
+ async function queryAssetRoles(cmd, client, assetType, assetId) {
221
+ var _a, _b, _c, _d, _e, _f, _g, _h;
200
222
  try {
201
- const resp = await client.query({
202
- query: RESOURCE_ROLES_QUERY,
203
- variables: {
204
- resourceId: resourceId,
205
- },
206
- fetchPolicy: "network-only", // to avoid caching
207
- });
208
- // no fall through doesn't consider process.exit();
209
- let x;
210
- switch (resp.data.accessLevels.__typename) {
211
- case "ResourceAccessLevelsResult":
212
- return (_c = (_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.accessLevels) === null || _b === void 0 ? void 0 : _b.accessLevels) === null || _c === void 0 ? void 0 : _c.map((role) => {
213
- return {
214
- name: role.accessLevelName,
215
- value: {
216
- name: role.accessLevelName,
217
- id: role.accessLevelRemoteId,
218
- },
219
- };
223
+ switch (assetType) {
224
+ case "Resource": {
225
+ const resp = await client.query({
226
+ query: RESOURCE_ROLES_QUERY,
227
+ variables: {
228
+ resourceId: assetId,
229
+ },
230
+ fetchPolicy: "network-only", // to avoid caching
220
231
  });
221
- case "ResourceNotFoundError":
222
- x = cmd.error((_e = (_d = resp.data) === null || _d === void 0 ? void 0 : _d.accessLevels) === null || _e === void 0 ? void 0 : _e.message);
223
- break;
224
- default:
225
- cmd.error(resp.error || "Unknown error occurred.");
232
+ // no fall through doesn't consider process.exit();
233
+ let x;
234
+ switch (resp.data.accessLevels.__typename) {
235
+ case "ResourceAccessLevelsResult":
236
+ return (_c = (_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.accessLevels) === null || _b === void 0 ? void 0 : _b.accessLevels) === null || _c === void 0 ? void 0 : _c.map((role) => {
237
+ return {
238
+ message: role.accessLevelName || "",
239
+ value: {
240
+ name: role.accessLevelName || "",
241
+ id: role.accessLevelRemoteId || "",
242
+ },
243
+ };
244
+ });
245
+ case "ResourceNotFoundError":
246
+ x = cmd.error((_e = (_d = resp.data) === null || _d === void 0 ? void 0 : _d.accessLevels) === null || _e === void 0 ? void 0 : _e.message);
247
+ break;
248
+ default:
249
+ cmd.error(resp.error || "Unknown error occurred.");
250
+ }
251
+ return;
252
+ }
253
+ case "Group": {
254
+ const resp = await client.query({
255
+ query: GROUP_ROLES_QUERY,
256
+ variables: {
257
+ groupId: assetId,
258
+ },
259
+ fetchPolicy: "network-only", // to avoid caching
260
+ });
261
+ // no fall through doesn't consider process.exit();
262
+ let x;
263
+ switch (resp.data.groupAccessLevels.__typename) {
264
+ case "GroupAccessLevelsResult":
265
+ return (_h = (_g = (_f = resp.data) === null || _f === void 0 ? void 0 : _f.groupAccessLevels) === null || _g === void 0 ? void 0 : _g.accessLevels) === null || _h === void 0 ? void 0 : _h.map((role) => {
266
+ return {
267
+ message: role.accessLevelName,
268
+ value: {
269
+ name: role.accessLevelName,
270
+ id: role.accessLevelRemoteId,
271
+ },
272
+ };
273
+ });
274
+ default:
275
+ x = cmd.error(resp.error || "Unknown error occurred.");
276
+ }
277
+ return;
278
+ }
226
279
  }
227
280
  }
228
281
  catch (error) {
@@ -241,16 +294,18 @@ const REQUEST_DEFAULTS_QUERY = (0, graphql_1.graphql)(`
241
294
  requestedGroups: $requestedGroups,
242
295
  }
243
296
  ) {
244
- durationOptions {
245
- durationInMinutes
246
- label
297
+ ... on RequestDefaults {
298
+ durationOptions {
299
+ durationInMinutes
300
+ label
301
+ }
302
+ recommendedDurationInMinutes
303
+ defaultDurationInMinutes
304
+ maxDurationInMinutes
305
+ requireSupportTicket
306
+ reasonOptional
307
+ requesterIsAdmin
247
308
  }
248
- recommendedDurationInMinutes
249
- defaultDurationInMinutes
250
- maxDurationInMinutes
251
- requireSupportTicket
252
- reasonOptional
253
- requesterIsAdmin
254
309
  }
255
310
  }`);
256
311
  async function queryRequestDefaults(cmd, client, requestedResources, requestedGroups) {
@@ -271,20 +326,176 @@ async function queryRequestDefaults(cmd, client, requestedResources, requestedGr
271
326
  }
272
327
  }
273
328
  }
329
+ const CREATE_REQUEST_MUTATION = (0, graphql_1.graphql)(`
330
+ mutation CreateRequest(
331
+ $requestedResources: [RequestedResourceInput!]!
332
+ $requestedGroups: [RequestedGroupInput!]!
333
+ $reason: String!
334
+ $durationInMinutes: Int
335
+ ) {
336
+ createRequest(
337
+ input: {
338
+ requestedResources: $requestedResources
339
+ requestedGroups: $requestedGroups
340
+ reason: $reason
341
+ durationInMinutes: $durationInMinutes
342
+ }
343
+ ) {
344
+ ... on CreateRequestResult {
345
+ request {
346
+ id
347
+ status
348
+ }
349
+ }
350
+ ... on RequestDurationTooLargeError {
351
+ message
352
+ }
353
+ ... on RequestRequiresUserAuthTokenForConnectionError {
354
+ message
355
+ }
356
+ ... on NoReviewersSetForOwnerError {
357
+ message
358
+ ownerId
359
+ }
360
+ ... on NoReviewersSetForResourceError {
361
+ message
362
+ resourceId
363
+ }
364
+ ... on NoReviewersSetForGroupError {
365
+ message
366
+ groupId
367
+ }
368
+ ... on NoManagerSetForRequestingUserError {
369
+ message
370
+ }
371
+ ... on MfaInvalidError {
372
+ message
373
+ }
374
+ ... on BulkRequestTooLargeError {
375
+ message
376
+ }
377
+ ... on ItemCannotBeRequestedError {
378
+ message
379
+ }
380
+ ... on UserCannotRequestAccessForTargetGroupError {
381
+ message
382
+ groupId
383
+ userId
384
+ }
385
+ ... on GroupNestingNotAllowedError {
386
+ message
387
+ fromGroupId
388
+ toGroupId
389
+ }
390
+ ... on TargetUserHasNestedAccessError {
391
+ message
392
+ groupIds
393
+ }
394
+ ... on RequestReasonMissingError {
395
+ message
396
+ }
397
+ ... on RequestFieldValueMissingError {
398
+ message
399
+ fieldName
400
+ }
401
+ ... on LinkedGroupNotRequestableError {
402
+ message
403
+ sourceGroupId
404
+ groupBindingId
405
+ }
406
+ ... on RequestReasonBelowMinLengthError {
407
+ message
408
+ }
409
+
410
+ }
411
+ }
412
+ `);
413
+ async function createRequest(cmd, client, requestedResources, requestedGroups, reason, durationInMinutes) {
414
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
415
+ try {
416
+ const resp = await client.mutate({
417
+ mutation: CREATE_REQUEST_MUTATION,
418
+ variables: {
419
+ requestedResources: requestedResources,
420
+ requestedGroups: requestedGroups,
421
+ reason: reason,
422
+ durationInMinutes: durationInMinutes,
423
+ },
424
+ });
425
+ let x;
426
+ switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.createRequest.__typename) {
427
+ case "CreateRequestResult":
428
+ return (_b = resp.data) === null || _b === void 0 ? void 0 : _b.createRequest.request;
429
+ case "RequestDurationTooLargeError":
430
+ x = cmd.error((_c = resp.data) === null || _c === void 0 ? void 0 : _c.createRequest.message);
431
+ break;
432
+ case "RequestRequiresUserAuthTokenForConnectionError":
433
+ x = cmd.error((_d = resp.data) === null || _d === void 0 ? void 0 : _d.createRequest.message);
434
+ break;
435
+ case "NoReviewersSetForOwnerError":
436
+ x = cmd.error((_e = resp.data) === null || _e === void 0 ? void 0 : _e.createRequest.message);
437
+ break;
438
+ case "NoReviewersSetForResourceError":
439
+ x = cmd.error((_f = resp.data) === null || _f === void 0 ? void 0 : _f.createRequest.message);
440
+ break;
441
+ case "NoReviewersSetForGroupError":
442
+ x = cmd.error((_g = resp.data) === null || _g === void 0 ? void 0 : _g.createRequest.message);
443
+ break;
444
+ case "NoManagerSetForRequestingUserError":
445
+ x = cmd.error((_h = resp.data) === null || _h === void 0 ? void 0 : _h.createRequest.message);
446
+ break;
447
+ case "MfaInvalidError":
448
+ x = cmd.error((_j = resp.data) === null || _j === void 0 ? void 0 : _j.createRequest.message);
449
+ break;
450
+ case "BulkRequestTooLargeError":
451
+ x = cmd.error((_k = resp.data) === null || _k === void 0 ? void 0 : _k.createRequest.message);
452
+ break;
453
+ case "ItemCannotBeRequestedError":
454
+ x = cmd.error((_l = resp.data) === null || _l === void 0 ? void 0 : _l.createRequest.message);
455
+ break;
456
+ case "UserCannotRequestAccessForTargetGroupError":
457
+ x = cmd.error((_m = resp.data) === null || _m === void 0 ? void 0 : _m.createRequest.message);
458
+ break;
459
+ case "GroupNestingNotAllowedError":
460
+ x = cmd.error((_o = resp.data) === null || _o === void 0 ? void 0 : _o.createRequest.message);
461
+ break;
462
+ case "TargetUserHasNestedAccessError":
463
+ x = cmd.error((_p = resp.data) === null || _p === void 0 ? void 0 : _p.createRequest.message);
464
+ break;
465
+ case "RequestReasonMissingError":
466
+ x = cmd.error((_q = resp.data) === null || _q === void 0 ? void 0 : _q.createRequest.message);
467
+ break;
468
+ case "RequestFieldValueMissingError":
469
+ x = cmd.error((_r = resp.data) === null || _r === void 0 ? void 0 : _r.createRequest.message);
470
+ break;
471
+ case "LinkedGroupNotRequestableError":
472
+ x = cmd.error((_s = resp.data) === null || _s === void 0 ? void 0 : _s.createRequest.message);
473
+ break;
474
+ case "RequestReasonBelowMinLengthError":
475
+ x = cmd.error((_t = resp.data) === null || _t === void 0 ? void 0 : _t.createRequest.message);
476
+ break;
477
+ }
478
+ }
479
+ catch (error) {
480
+ if (error instanceof Error || typeof error === "string") {
481
+ cmd.error(error);
482
+ }
483
+ }
484
+ }
274
485
  // Helper functions
275
486
  async function selectRequestableItems(cmd, client, requestMap) {
276
- const { App } = await inquirer.prompt([
277
- {
278
- name: "App",
279
- message: "Select an app:",
280
- type: "autocomplete",
281
- source: async (answers, input) => {
282
- var _a;
283
- return (_a = (await queryRequestableApps(cmd, client, input))) !== null && _a !== void 0 ? _a : [];
284
- },
285
- pageSize: 15,
487
+ const initialChoices = (await queryRequestableApps(cmd, client, "")) || [];
488
+ const appPrompt = new AutoComplete({
489
+ name: "App",
490
+ message: "Select an app:",
491
+ limit: 15,
492
+ choices: initialChoices,
493
+ async suggest(input) {
494
+ const filteredChoices = await queryRequestableApps(cmd, client, input || "");
495
+ return filteredChoices || initialChoices;
286
496
  },
287
- ]);
497
+ });
498
+ const App = await appPrompt.run();
288
499
  // Set the app in the requestMap and call choose assets step
289
500
  if (!(App.id in requestMap)) {
290
501
  requestMap[App.id] = {
@@ -295,13 +506,16 @@ async function selectRequestableItems(cmd, client, requestMap) {
295
506
  await chooseAssets(cmd, client, App.id, requestMap);
296
507
  }
297
508
  async function chooseAssets(cmd, client, appId, requestMap) {
298
- var _a;
299
- const { Assets } = await inquirer.prompt({
509
+ const initialChoices = (await queryRequestableAssets(cmd, client, appId, "")) || [];
510
+ const assetPrompt = new AutoComplete({
300
511
  name: "Assets",
301
- type: "checkbox",
302
- pageSize: 15,
303
- message: "Select one or more items:",
304
- choices: (_a = (await queryRequestableAssets(cmd, client, appId, undefined))) !== null && _a !== void 0 ? _a : [],
512
+ message: "Select one or more assets:",
513
+ limit: 15,
514
+ multiple: true,
515
+ async choices(input) {
516
+ const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
517
+ return filteredChoices || initialChoices;
518
+ },
305
519
  validate: (answer) => {
306
520
  if (answer.length < 1) {
307
521
  return "You must select at least one item.";
@@ -309,14 +523,16 @@ async function chooseAssets(cmd, client, appId, requestMap) {
309
523
  return true;
310
524
  },
311
525
  });
526
+ const Assets = await assetPrompt.run();
312
527
  const entry = requestMap[appId];
313
528
  for (const asset of Assets) {
314
529
  if (entry === undefined) {
315
- throw new Error(`App ${appId} not found in requestMap`);
530
+ throw new Error(`Error formatting app ${appId} in request`);
316
531
  }
317
532
  if (!(asset.id in entry.assets)) {
318
533
  entry.assets[asset.id] = {
319
534
  assetName: asset.name,
535
+ type: asset.type,
320
536
  roles: {},
321
537
  };
322
538
  }
@@ -325,29 +541,31 @@ async function chooseAssets(cmd, client, appId, requestMap) {
325
541
  }
326
542
  async function chooseRoles(cmd, client, appId, assetId, requestMap) {
327
543
  var _a;
328
- const resourceRoles = (_a = (await queryResourceRoles(cmd, client, assetId))) !== null && _a !== void 0 ? _a : [];
329
- if (resourceRoles !== undefined &&
330
- (resourceRoles.length === 0 ||
331
- (resourceRoles.length === 1 && resourceRoles[0].name === ""))) {
544
+ const entry = requestMap[appId];
545
+ const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets[assetId];
546
+ if (entry === undefined || assetEntry === undefined) {
547
+ throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
548
+ }
549
+ const assetRoles = (_a = (await queryAssetRoles(cmd, client, assetEntry.type, assetId))) !== null && _a !== void 0 ? _a : [];
550
+ if (assetRoles !== undefined &&
551
+ (assetRoles.length === 0 ||
552
+ (assetRoles.length === 1 && assetRoles[0].value.name === ""))) {
332
553
  return;
333
554
  }
334
- const { roles } = await inquirer.prompt({
335
- name: "roles",
336
- type: "checkbox",
555
+ const rolePrompt = new AutoComplete({
556
+ name: "Roles",
337
557
  message: `Select one or more roles for ${assetId}:`,
338
- choices: resourceRoles,
558
+ limit: 15,
559
+ multiple: true,
560
+ choices: assetRoles,
339
561
  validate: (answer) => {
340
- if ((resourceRoles === null || resourceRoles === void 0 ? void 0 : resourceRoles.length) > 1 && answer.length < 1) {
341
- return "You must select at least one role.";
562
+ if (answer.length < 1) {
563
+ return "You must select at least one item.";
342
564
  }
343
565
  return true;
344
566
  },
345
567
  });
346
- const entry = requestMap[appId];
347
- const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets[assetId];
348
- if (entry === undefined || assetEntry === undefined) {
349
- throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
350
- }
568
+ const roles = await rolePrompt.run();
351
569
  if (!assetEntry.roles) {
352
570
  assetEntry.roles = {};
353
571
  }
@@ -360,14 +578,12 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
360
578
  async function doneSelectingAssets() {
361
579
  const submitMessage = "✅ Yes, proceed with request";
362
580
  const addMoreMessage = "❌ No, add more items";
363
- const { submitOrAdd } = await inquirer.prompt([
364
- {
365
- name: "submitOrAdd",
366
- message: "Is this all you want to request?",
367
- type: "list",
368
- choices: [submitMessage, addMoreMessage],
369
- },
370
- ]);
581
+ const prompt = new Select({
582
+ name: "submitOrAdd",
583
+ message: "Is this all you want to request?",
584
+ choices: [submitMessage, addMoreMessage],
585
+ });
586
+ const submitOrAdd = await prompt.run();
371
587
  return submitOrAdd === submitMessage;
372
588
  }
373
589
  async function setRequestDefaults(cmd, client, metadata) {
@@ -388,7 +604,7 @@ async function setRequestDefaults(cmd, client, metadata) {
388
604
  }
389
605
  try {
390
606
  const requestDefaults = await queryRequestDefaults(cmd, client, requestedResources, requestedGroups);
391
- if ((requestDefaults === null || requestDefaults === void 0 ? void 0 : requestDefaults.__typename) === "RequestDefaults") {
607
+ if (requestDefaults !== undefined) {
392
608
  metadata.requestDefaults.durationOptions =
393
609
  requestDefaults.durationOptions;
394
610
  metadata.requestDefaults.recommendedDurationInMinutes =
@@ -409,47 +625,91 @@ async function setRequestDefaults(cmd, client, metadata) {
409
625
  }
410
626
  }
411
627
  async function promptForReason(metadata) {
412
- return await inquirer.prompt([
628
+ const { reason } = await prompt([
413
629
  {
414
630
  name: "reason",
415
631
  message: "I need access to this because...",
416
632
  type: "input",
417
633
  validate: (answer) => {
418
- if (metadata.requestDefaults.reasonOptional && answer.length < 1) {
634
+ if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
419
635
  return "A reason for requesting these assets is required.";
420
636
  }
421
637
  return true;
422
638
  },
423
639
  },
424
640
  ]);
641
+ return reason;
425
642
  }
426
643
  async function promptForExpiration(metadata) {
427
644
  var _a, _b;
428
645
  const durations = ((_b = (_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.durationOptions) === null || _b === void 0 ? void 0 : _b.map((option) => {
646
+ const label = option.durationInMinutes ===
647
+ metadata.requestDefaults.recommendedDurationInMinutes
648
+ ? `${option.label} (Recommended)`
649
+ : option.label;
429
650
  return {
430
- name: option.durationInMinutes ===
431
- metadata.requestDefaults.recommendedDurationInMinutes
432
- ? `${option.label} (Recommended)`
433
- : option.label,
651
+ message: label,
434
652
  value: {
435
- label: option.label,
653
+ label: label,
436
654
  durationInMinutes: option.durationInMinutes,
655
+ toString: () => label,
437
656
  },
438
657
  };
439
658
  })) || [];
440
- // TODO: Sort durations by minutes
441
- // durations = durations.sort(
442
- // durations.filter((option) => option.value.durationInMinutes),
443
- // );
444
- return await inquirer.prompt([
445
- {
446
- name: "expiration",
447
- message: "When should access expire?",
448
- type: "list",
449
- choices: durations,
450
- pageSize: 15,
659
+ // Sort durations by minutes
660
+ durations.sort((a, b) => a.value.durationInMinutes - b.value.durationInMinutes);
661
+ const expirationSelect = new AutoComplete({
662
+ name: "expiration",
663
+ message: "When should access expire?",
664
+ type: "list",
665
+ choices: durations,
666
+ pageSize: 15,
667
+ });
668
+ let selected = await expirationSelect.run();
669
+ if (selected.label === "Custom") {
670
+ selected = await setCustomDuration();
671
+ }
672
+ return selected;
673
+ }
674
+ async function setCustomDuration() {
675
+ const durationForm = new Form({
676
+ name: "user",
677
+ message: "Please set a custom access duration:",
678
+ choices: [
679
+ { name: "days", message: "Days", initial: "0" },
680
+ { name: "hours", message: "Hours", initial: "0" },
681
+ { name: "minutes", message: "Minutes", initial: "0" },
682
+ ],
683
+ validate: (answer) => {
684
+ const days = Number.parseInt(answer.days);
685
+ const hours = Number.parseInt(answer.hours);
686
+ const minutes = Number.parseInt(answer.minutes);
687
+ if (days < 0 ||
688
+ hours < 0 ||
689
+ minutes < 0 ||
690
+ (days === 0 && hours === 0 && minutes === 0)) {
691
+ return "Please enter a valid duration.";
692
+ }
693
+ return true;
451
694
  },
452
- ]);
695
+ });
696
+ const duration = await durationForm.run();
697
+ // Convert to minutes, define the new custom label
698
+ const durationInMinutes = duration.days * 1440 + duration.hours * 60 + duration.minutes;
699
+ let durationLabel = "";
700
+ if (duration.days > 0) {
701
+ durationLabel += `${duration.days}d `;
702
+ }
703
+ if (duration.hours > 0) {
704
+ durationLabel += `${duration.hours}h `;
705
+ }
706
+ if (duration.minutes > 0) {
707
+ durationLabel += `${duration.minutes}m`;
708
+ }
709
+ return {
710
+ durationInMinutes: durationInMinutes,
711
+ label: durationLabel,
712
+ };
453
713
  }
454
714
  async function submitFinalRequest(cmd) {
455
715
  const submitMessage = "✅ Yes, submit request";
@@ -1,5 +1,9 @@
1
+ import type { ApolloQueryResult } from "@apollo/client";
1
2
  import type { Command } from "@oclif/core/lib/command";
3
+ import type { GetRequestQuery, GetRequestsQuery } from "../graphql/graphql";
2
4
  import type { RequestMap } from "../lib/requests";
3
5
  export declare function headerMessage(cmd: Command): void;
4
6
  export declare function treeifyRequestMap(requestMap: RequestMap): string;
5
7
  export declare function displayFinalRequestSummary(cmd: Command, requestMap: RequestMap, reason: string, expiration: string): void;
8
+ export declare function displayRequestDetails(cmd: Command, requestResp: ApolloQueryResult<GetRequestQuery>): void;
9
+ export declare function displayRequestList(cmd: Command, requestResp: ApolloQueryResult<GetRequestsQuery>): void;