opal-security 3.1.0 → 3.1.1-beta.16f5ed5

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,16 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initEmptyRequestMetadata = initEmptyRequestMetadata;
3
4
  exports.selectRequestableItems = selectRequestableItems;
4
5
  exports.chooseAssets = chooseAssets;
5
6
  exports.chooseRoles = chooseRoles;
6
7
  exports.doneSelectingAssets = doneSelectingAssets;
8
+ exports.setRequestDefaults = setRequestDefaults;
7
9
  exports.promptForReason = promptForReason;
8
10
  exports.promptForExpiration = promptForExpiration;
9
11
  exports.submitFinalRequest = submitFinalRequest;
12
+ const chalk_1 = require("chalk");
10
13
  const inquirer = require("inquirer");
11
14
  const graphql_1 = require("../graphql");
12
- inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
15
+ const displays_1 = require("../utils/displays");
16
+ const config_1 = require("./config");
17
+ const { AutoComplete, Select, prompt, Form } = require("enquirer");
18
+ function initEmptyRequestMetadata() {
19
+ // Initialize with empty defaults
20
+ const requestDefaults = {
21
+ durationOptions: [],
22
+ recommendedDurationInMinutes: undefined,
23
+ defaultDurationInMinutes: undefined,
24
+ maxDurationInMinutes: undefined,
25
+ requireSupportTicket: false,
26
+ reasonOptional: false,
27
+ requesterIsAdmin: false,
28
+ };
29
+ // Initialize with empty map
30
+ const requestMap = {};
31
+ return {
32
+ requestMap,
33
+ requestDefaults,
34
+ durationLabel: "",
35
+ durationInMinutes: 0,
36
+ reason: "",
37
+ };
38
+ }
13
39
  // Queries and Mutations
40
+ // TODO: add pagination ability from CLI. (Load more...) option
14
41
  const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
15
42
  query GetRequestableAppsQuery($searchQuery: String) {
16
43
  appsV2(
@@ -40,6 +67,44 @@ const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
40
67
  }
41
68
  }
42
69
  `);
70
+ async function queryRequestableApps(cmd, client, input) {
71
+ var _a, _b;
72
+ try {
73
+ const resp = await client.query({
74
+ query: GET_REQUESTABLE_APPS_QUERY,
75
+ variables: {
76
+ searchQuery: input || "",
77
+ },
78
+ fetchPolicy: "network-only", // to avoid caching
79
+ });
80
+ 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) => {
81
+ let type = undefined;
82
+ switch (edge.node.__typename) {
83
+ case "Resource":
84
+ type = edge.node.resourceType;
85
+ break;
86
+ case "Connection":
87
+ type = edge.node.connectionType;
88
+ break;
89
+ default:
90
+ type = edge.node.__typename;
91
+ }
92
+ return {
93
+ message: `${edge.node.displayName} [${type}]`,
94
+ value: {
95
+ id: edge.node.id,
96
+ name: edge.node.displayName,
97
+ toString: () => edge.node.displayName,
98
+ },
99
+ };
100
+ });
101
+ }
102
+ catch (error) {
103
+ if (error instanceof Error || typeof error === "string") {
104
+ cmd.error(error);
105
+ }
106
+ }
107
+ }
43
108
  const GET_ASSETS_QUERY = (0, graphql_1.graphql)(`
44
109
  query PaginatedEntityDropdown(
45
110
  $id: UUID!
@@ -70,45 +135,14 @@ const GET_ASSETS_QUERY = (0, graphql_1.graphql)(`
70
135
  cursor
71
136
  }
72
137
  }
138
+ ... on AppNotFoundError {
139
+ message
140
+ }
73
141
  }
74
142
  }
75
143
  `);
76
- async function queryRequestableApps(cmd, client, input) {
77
- var _a, _b;
78
- try {
79
- const resp = await client.query({
80
- query: GET_REQUESTABLE_APPS_QUERY,
81
- variables: {
82
- searchQuery: input || "",
83
- },
84
- fetchPolicy: "network-only", // to avoid caching
85
- });
86
- 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) => {
87
- let type = undefined;
88
- if (edge.node.__typename === "Resource") {
89
- type = edge.node.resourceType;
90
- }
91
- if (edge.node.__typename === "Connection") {
92
- type = edge.node.connectionType;
93
- }
94
- const label = `${edge.node.displayName} (${type})`;
95
- return {
96
- name: label,
97
- value: {
98
- id: edge.node.id,
99
- name: label,
100
- },
101
- };
102
- });
103
- }
104
- catch (error) {
105
- if (error instanceof Error || typeof error === "string") {
106
- cmd.error(error);
107
- }
108
- }
109
- }
110
144
  async function queryRequestableAssets(cmd, client, appId, input) {
111
- var _a, _b, _c, _d;
145
+ var _a, _b, _c, _d, _e, _f;
112
146
  try {
113
147
  const resp = await client.query({
114
148
  query: GET_ASSETS_QUERY,
@@ -123,24 +157,327 @@ async function queryRequestableAssets(cmd, client, appId, input) {
123
157
  switch (resp.data.app.__typename) {
124
158
  case "App":
125
159
  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) => {
126
- var _a, _b, _c, _d, _e, _f;
160
+ var _a, _b, _c, _d, _e, _f, _g, _h;
127
161
  const name = ((_a = item.resource) === null || _a === void 0 ? void 0 : _a.name) || ((_b = item.group) === null || _b === void 0 ? void 0 : _b.name);
128
162
  const id = ((_c = item.resource) === null || _c === void 0 ? void 0 : _c.id) || ((_d = item.group) === null || _d === void 0 ? void 0 : _d.id);
129
163
  const type = ((_e = item.resource) === null || _e === void 0 ? void 0 : _e.__typename) || ((_f = item.group) === null || _f === void 0 ? void 0 : _f.__typename);
130
- const label = `${name} (${type})`;
131
164
  return {
132
- name: label,
165
+ message: `${name} (${type})`,
133
166
  value: {
134
- name: label,
135
- id: id,
167
+ name: name || "",
168
+ 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) || "",
136
170
  },
137
171
  };
138
172
  });
139
173
  case "AppNotFoundError":
140
- x = cmd.error("App not found");
174
+ x = cmd.error((_f = (_e = resp.data) === null || _e === void 0 ? void 0 : _e.app) === null || _f === void 0 ? void 0 : _f.message);
141
175
  break;
142
176
  default:
143
- cmd.error("Unknown error occurred.");
177
+ cmd.error(resp.error || "Unknown error occurred.");
178
+ }
179
+ }
180
+ catch (error) {
181
+ if (error instanceof Error || typeof error === "string") {
182
+ cmd.error(error);
183
+ }
184
+ }
185
+ }
186
+ const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
187
+ query ResourceAccessLevels($resourceId: ResourceId!) {
188
+ accessLevels(input: {
189
+ resourceId: $resourceId,
190
+ onlyMine: false,
191
+ }) {
192
+ __typename
193
+ ... on ResourceAccessLevelsResult {
194
+ accessLevels {
195
+ ... on ResourceAccessLevel {
196
+ accessLevelName
197
+ accessLevelRemoteId
198
+ }
199
+ }
200
+ }
201
+ ... on ResourceNotFoundError {
202
+ message
203
+ }
204
+ }
205
+ }
206
+ `);
207
+ const GROUP_ROLES_QUERY = (0, graphql_1.graphql)(`
208
+ query GroupAccessLevels($groupId: GroupId!) {
209
+ groupAccessLevels(
210
+ input: { groupId: $groupId }
211
+ ) {
212
+ ... on GroupAccessLevelsResult {
213
+ groupId
214
+ accessLevels {
215
+ ... on GroupAccessLevel {
216
+ accessLevelName
217
+ accessLevelRemoteId
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+ `);
224
+ async function queryAssetRoles(cmd, client, assetType, assetId) {
225
+ var _a, _b, _c, _d, _e, _f, _g, _h;
226
+ try {
227
+ switch (assetType) {
228
+ case "Resource": {
229
+ const resp = await client.query({
230
+ query: RESOURCE_ROLES_QUERY,
231
+ variables: {
232
+ resourceId: assetId,
233
+ },
234
+ fetchPolicy: "network-only", // to avoid caching
235
+ });
236
+ // no fall through doesn't consider process.exit();
237
+ let x;
238
+ switch (resp.data.accessLevels.__typename) {
239
+ case "ResourceAccessLevelsResult":
240
+ 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) => {
241
+ return {
242
+ message: role.accessLevelName || "",
243
+ value: {
244
+ name: role.accessLevelName || "",
245
+ id: role.accessLevelRemoteId || "",
246
+ },
247
+ };
248
+ });
249
+ case "ResourceNotFoundError":
250
+ x = cmd.error((_e = (_d = resp.data) === null || _d === void 0 ? void 0 : _d.accessLevels) === null || _e === void 0 ? void 0 : _e.message);
251
+ break;
252
+ default:
253
+ cmd.error(resp.error || "Unknown error occurred.");
254
+ }
255
+ return;
256
+ }
257
+ case "Group": {
258
+ const resp = await client.query({
259
+ query: GROUP_ROLES_QUERY,
260
+ variables: {
261
+ groupId: assetId,
262
+ },
263
+ fetchPolicy: "network-only", // to avoid caching
264
+ });
265
+ // no fall through doesn't consider process.exit();
266
+ let x;
267
+ switch (resp.data.groupAccessLevels.__typename) {
268
+ case "GroupAccessLevelsResult":
269
+ 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) => {
270
+ return {
271
+ message: role.accessLevelName,
272
+ value: {
273
+ name: role.accessLevelName,
274
+ id: role.accessLevelRemoteId,
275
+ },
276
+ };
277
+ });
278
+ default:
279
+ x = cmd.error(resp.error || "Unknown error occurred.");
280
+ }
281
+ return;
282
+ }
283
+ }
284
+ }
285
+ catch (error) {
286
+ if (error instanceof Error || typeof error === "string") {
287
+ cmd.error(error);
288
+ }
289
+ }
290
+ }
291
+ const REQUEST_DEFAULTS_QUERY = (0, graphql_1.graphql)(`
292
+ query RequestDefaults(
293
+ $requestedResources: [RequestConfigurationResourceInput!]!
294
+ $requestedGroups: [RequestConfigurationGroupInput!]!
295
+ ) {
296
+ requestDefaults(input: {
297
+ requestedResources: $requestedResources,
298
+ requestedGroups: $requestedGroups,
299
+ }
300
+ ) {
301
+ ... on RequestDefaults {
302
+ durationOptions {
303
+ durationInMinutes
304
+ label
305
+ }
306
+ recommendedDurationInMinutes
307
+ defaultDurationInMinutes
308
+ maxDurationInMinutes
309
+ requireSupportTicket
310
+ reasonOptional
311
+ requesterIsAdmin
312
+ }
313
+ }
314
+ }`);
315
+ async function queryRequestDefaults(cmd, client, requestedResources, requestedGroups) {
316
+ try {
317
+ const resp = await client.query({
318
+ query: REQUEST_DEFAULTS_QUERY,
319
+ variables: {
320
+ requestedResources: requestedResources,
321
+ requestedGroups: requestedGroups,
322
+ },
323
+ fetchPolicy: "network-only", // to avoid caching
324
+ });
325
+ return resp.data.requestDefaults;
326
+ }
327
+ catch (error) {
328
+ if (error instanceof Error || typeof error === "string") {
329
+ cmd.error(error);
330
+ }
331
+ }
332
+ }
333
+ const CREATE_REQUEST_MUTATION = (0, graphql_1.graphql)(`
334
+ mutation CreateRequest(
335
+ $requestedResources: [RequestedResourceInput!]!
336
+ $requestedGroups: [RequestedGroupInput!]!
337
+ $reason: String!
338
+ $durationInMinutes: Int
339
+ ) {
340
+ createRequest(
341
+ input: {
342
+ requestedResources: $requestedResources
343
+ requestedGroups: $requestedGroups
344
+ reason: $reason
345
+ durationInMinutes: $durationInMinutes
346
+ }
347
+ ) {
348
+ ... on CreateRequestResult {
349
+ request {
350
+ id
351
+ status
352
+ }
353
+ }
354
+ ... on RequestDurationTooLargeError {
355
+ message
356
+ }
357
+ ... on RequestRequiresUserAuthTokenForConnectionError {
358
+ message
359
+ }
360
+ ... on NoReviewersSetForOwnerError {
361
+ message
362
+ ownerId
363
+ }
364
+ ... on NoReviewersSetForResourceError {
365
+ message
366
+ resourceId
367
+ }
368
+ ... on NoReviewersSetForGroupError {
369
+ message
370
+ groupId
371
+ }
372
+ ... on NoManagerSetForRequestingUserError {
373
+ message
374
+ }
375
+ ... on MfaInvalidError {
376
+ message
377
+ }
378
+ ... on BulkRequestTooLargeError {
379
+ message
380
+ }
381
+ ... on ItemCannotBeRequestedError {
382
+ message
383
+ }
384
+ ... on UserCannotRequestAccessForTargetGroupError {
385
+ message
386
+ groupId
387
+ userId
388
+ }
389
+ ... on GroupNestingNotAllowedError {
390
+ message
391
+ fromGroupId
392
+ toGroupId
393
+ }
394
+ ... on TargetUserHasNestedAccessError {
395
+ message
396
+ groupIds
397
+ }
398
+ ... on RequestReasonMissingError {
399
+ message
400
+ }
401
+ ... on RequestFieldValueMissingError {
402
+ message
403
+ fieldName
404
+ }
405
+ ... on LinkedGroupNotRequestableError {
406
+ message
407
+ sourceGroupId
408
+ groupBindingId
409
+ }
410
+ ... on RequestReasonBelowMinLengthError {
411
+ message
412
+ }
413
+
414
+ }
415
+ }
416
+ `);
417
+ async function createRequest(cmd, client, requestedResources, requestedGroups, reason, durationInMinutes) {
418
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
419
+ try {
420
+ const resp = await client.mutate({
421
+ mutation: CREATE_REQUEST_MUTATION,
422
+ variables: {
423
+ requestedResources: requestedResources,
424
+ requestedGroups: requestedGroups,
425
+ reason: reason,
426
+ durationInMinutes: durationInMinutes,
427
+ },
428
+ });
429
+ let x;
430
+ switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.createRequest.__typename) {
431
+ case "CreateRequestResult":
432
+ return (_b = resp.data) === null || _b === void 0 ? void 0 : _b.createRequest.request;
433
+ case "RequestDurationTooLargeError":
434
+ x = cmd.error((_c = resp.data) === null || _c === void 0 ? void 0 : _c.createRequest.message);
435
+ break;
436
+ case "RequestRequiresUserAuthTokenForConnectionError":
437
+ x = cmd.error((_d = resp.data) === null || _d === void 0 ? void 0 : _d.createRequest.message);
438
+ break;
439
+ case "NoReviewersSetForOwnerError":
440
+ x = cmd.error((_e = resp.data) === null || _e === void 0 ? void 0 : _e.createRequest.message);
441
+ break;
442
+ case "NoReviewersSetForResourceError":
443
+ x = cmd.error((_f = resp.data) === null || _f === void 0 ? void 0 : _f.createRequest.message);
444
+ break;
445
+ case "NoReviewersSetForGroupError":
446
+ x = cmd.error((_g = resp.data) === null || _g === void 0 ? void 0 : _g.createRequest.message);
447
+ break;
448
+ case "NoManagerSetForRequestingUserError":
449
+ x = cmd.error((_h = resp.data) === null || _h === void 0 ? void 0 : _h.createRequest.message);
450
+ break;
451
+ case "MfaInvalidError":
452
+ x = cmd.error((_j = resp.data) === null || _j === void 0 ? void 0 : _j.createRequest.message);
453
+ break;
454
+ case "BulkRequestTooLargeError":
455
+ x = cmd.error((_k = resp.data) === null || _k === void 0 ? void 0 : _k.createRequest.message);
456
+ break;
457
+ case "ItemCannotBeRequestedError":
458
+ x = cmd.error((_l = resp.data) === null || _l === void 0 ? void 0 : _l.createRequest.message);
459
+ break;
460
+ case "UserCannotRequestAccessForTargetGroupError":
461
+ x = cmd.error((_m = resp.data) === null || _m === void 0 ? void 0 : _m.createRequest.message);
462
+ break;
463
+ case "GroupNestingNotAllowedError":
464
+ x = cmd.error((_o = resp.data) === null || _o === void 0 ? void 0 : _o.createRequest.message);
465
+ break;
466
+ case "TargetUserHasNestedAccessError":
467
+ x = cmd.error((_p = resp.data) === null || _p === void 0 ? void 0 : _p.createRequest.message);
468
+ break;
469
+ case "RequestReasonMissingError":
470
+ x = cmd.error((_q = resp.data) === null || _q === void 0 ? void 0 : _q.createRequest.message);
471
+ break;
472
+ case "RequestFieldValueMissingError":
473
+ x = cmd.error((_r = resp.data) === null || _r === void 0 ? void 0 : _r.createRequest.message);
474
+ break;
475
+ case "LinkedGroupNotRequestableError":
476
+ x = cmd.error((_s = resp.data) === null || _s === void 0 ? void 0 : _s.createRequest.message);
477
+ break;
478
+ case "RequestReasonBelowMinLengthError":
479
+ x = cmd.error((_t = resp.data) === null || _t === void 0 ? void 0 : _t.createRequest.message);
480
+ break;
144
481
  }
145
482
  }
146
483
  catch (error) {
@@ -151,35 +488,38 @@ async function queryRequestableAssets(cmd, client, appId, input) {
151
488
  }
152
489
  // Helper functions
153
490
  async function selectRequestableItems(cmd, client, requestMap) {
154
- const { App } = await inquirer.prompt([
155
- {
156
- name: "App",
157
- message: "Select an app:",
158
- type: "autocomplete",
159
- source: async (answers, input) => {
160
- var _a;
161
- return (_a = (await queryRequestableApps(cmd, client, input))) !== null && _a !== void 0 ? _a : [];
162
- },
163
- pageSize: 15,
491
+ const initialChoices = (await queryRequestableApps(cmd, client, "")) || [];
492
+ const appPrompt = new AutoComplete({
493
+ name: "App",
494
+ message: "Select an app:",
495
+ limit: 15,
496
+ choices: initialChoices,
497
+ async suggest(input) {
498
+ const filteredChoices = await queryRequestableApps(cmd, client, input || "");
499
+ return filteredChoices || initialChoices;
164
500
  },
165
- ]);
501
+ });
502
+ const App = await appPrompt.run();
166
503
  // Set the app in the requestMap and call choose assets step
167
- if (!requestMap.has(App.id)) {
168
- requestMap.set(App.id, {
504
+ if (!(App.id in requestMap)) {
505
+ requestMap[App.id] = {
169
506
  appName: App.name,
170
- assets: new Map(),
171
- });
507
+ assets: {},
508
+ };
172
509
  }
173
510
  await chooseAssets(cmd, client, App.id, requestMap);
174
511
  }
175
512
  async function chooseAssets(cmd, client, appId, requestMap) {
176
- var _a;
177
- const { Assets } = await inquirer.prompt({
513
+ const initialChoices = (await queryRequestableAssets(cmd, client, appId, "")) || [];
514
+ const assetPrompt = new AutoComplete({
178
515
  name: "Assets",
179
- type: "checkbox",
180
- pageSize: 15,
181
- message: "Select one or more items:",
182
- choices: (_a = (await queryRequestableAssets(cmd, client, appId, undefined))) !== null && _a !== void 0 ? _a : [],
516
+ message: "Select one or more assets:",
517
+ limit: 15,
518
+ multiple: true,
519
+ async choices(input) {
520
+ const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
521
+ return filteredChoices || initialChoices;
522
+ },
183
523
  validate: (answer) => {
184
524
  if (answer.length < 1) {
185
525
  return "You must select at least one item.";
@@ -187,72 +527,220 @@ async function chooseAssets(cmd, client, appId, requestMap) {
187
527
  return true;
188
528
  },
189
529
  });
190
- const entry = requestMap.get(appId);
530
+ const Assets = await assetPrompt.run();
531
+ const entry = requestMap[appId];
191
532
  for (const asset of Assets) {
192
533
  if (entry === undefined) {
193
- throw new Error(`App ${appId} not found in requestMap`);
534
+ throw new Error(`Error formatting app ${appId} in request`);
194
535
  }
195
- if (!entry.assets.has(asset.id)) {
196
- entry.assets.set(asset.id, {
536
+ if (!(asset.id in entry.assets)) {
537
+ entry.assets[asset.id] = {
197
538
  assetName: asset.name,
198
- roles: new Map(),
199
- });
539
+ type: asset.type,
540
+ roles: {},
541
+ };
200
542
  }
201
- await chooseRoles(appId, asset.id, requestMap);
543
+ await chooseRoles(cmd, client, appId, asset.id, requestMap);
202
544
  }
203
545
  }
204
- async function chooseRoles(appId, assetId, requestMap) {
546
+ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
205
547
  var _a;
206
- const { roles } = await inquirer.prompt({
207
- name: "roles",
208
- type: "checkbox",
209
- message: `Select one or more roles for ${assetId}:`,
210
- choices: ["push", "pull", "triage", "admin"],
211
- });
212
- const entry = requestMap.get(appId);
213
- const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets.get(assetId);
548
+ const entry = requestMap[appId];
549
+ const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets[assetId];
214
550
  if (entry === undefined || assetEntry === undefined) {
215
551
  throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
216
552
  }
553
+ const assetRoles = (_a = (await queryAssetRoles(cmd, client, assetEntry.type, assetId))) !== null && _a !== void 0 ? _a : [];
554
+ if (assetRoles !== undefined &&
555
+ (assetRoles.length === 0 ||
556
+ (assetRoles.length === 1 && assetRoles[0].value.name === ""))) {
557
+ return;
558
+ }
559
+ const rolePrompt = new AutoComplete({
560
+ name: "Roles",
561
+ message: `Select one or more roles for ${assetEntry.assetName}:`,
562
+ limit: 15,
563
+ multiple: true,
564
+ choices: assetRoles,
565
+ validate: (answer) => {
566
+ if (answer.length < 1) {
567
+ return "You must select at least one item.";
568
+ }
569
+ return true;
570
+ },
571
+ });
572
+ const roles = await rolePrompt.run();
573
+ if (!assetEntry.roles) {
574
+ assetEntry.roles = {};
575
+ }
217
576
  for (const role of roles) {
218
- (_a = assetEntry.roles) === null || _a === void 0 ? void 0 : _a.set(role, {
219
- roleName: role,
220
- });
577
+ assetEntry.roles[role.id] = {
578
+ roleName: role.name,
579
+ };
221
580
  }
222
581
  }
223
582
  async function doneSelectingAssets() {
224
583
  const submitMessage = "✅ Yes, proceed with request";
225
584
  const addMoreMessage = "❌ No, add more items";
226
- const { submitOrAdd } = await inquirer.prompt([
227
- {
228
- name: "submitOrAdd",
229
- message: "Is this all you want to request?",
230
- type: "list",
231
- choices: [submitMessage, addMoreMessage],
232
- },
233
- ]);
585
+ const prompt = new Select({
586
+ name: "submitOrAdd",
587
+ message: "Is this all you want to request?",
588
+ choices: [submitMessage, addMoreMessage],
589
+ });
590
+ const submitOrAdd = await prompt.run();
234
591
  return submitOrAdd === submitMessage;
235
592
  }
236
- async function promptForReason() {
237
- return await inquirer.prompt([
593
+ async function setRequestDefaults(cmd, client, metadata) {
594
+ const requestMap = metadata.requestMap;
595
+ const requestedResources = [];
596
+ const requestedGroups = [];
597
+ for (const appNode of Object.values(requestMap)) {
598
+ for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
599
+ if (assetNode.roles !== undefined) {
600
+ const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
601
+ return roleId;
602
+ });
603
+ const roleIds = mappedRoles.length ? mappedRoles : [""];
604
+ for (const roleId of roleIds) {
605
+ switch (assetNode.type) {
606
+ case "Resource": {
607
+ requestedResources.push({
608
+ resourceId: assetId,
609
+ accessLevelRemoteId: roleId,
610
+ });
611
+ break;
612
+ }
613
+ case "Group": {
614
+ requestedGroups.push({
615
+ groupId: assetId,
616
+ accessLevelRemoteId: roleId,
617
+ });
618
+ break;
619
+ }
620
+ }
621
+ }
622
+ }
623
+ }
624
+ }
625
+ try {
626
+ const requestDefaults = await queryRequestDefaults(cmd, client, requestedResources, requestedGroups);
627
+ if (requestDefaults !== undefined) {
628
+ metadata.requestDefaults.durationOptions =
629
+ requestDefaults.durationOptions;
630
+ metadata.requestDefaults.recommendedDurationInMinutes =
631
+ requestDefaults.recommendedDurationInMinutes;
632
+ metadata.requestDefaults.defaultDurationInMinutes =
633
+ requestDefaults.defaultDurationInMinutes;
634
+ metadata.requestDefaults.maxDurationInMinutes =
635
+ requestDefaults.maxDurationInMinutes;
636
+ metadata.requestDefaults.requireSupportTicket =
637
+ requestDefaults.requireSupportTicket;
638
+ metadata.requestDefaults.reasonOptional = requestDefaults.reasonOptional;
639
+ metadata.requestDefaults.requesterIsAdmin =
640
+ requestDefaults.requesterIsAdmin;
641
+ }
642
+ }
643
+ catch (_a) {
644
+ cmd.error("Error fetching request defaults.");
645
+ }
646
+ }
647
+ async function promptForReason(metadata) {
648
+ const { reason } = await prompt([
238
649
  {
239
650
  name: "reason",
240
651
  message: "I need access to this because...",
241
652
  type: "input",
653
+ validate: (answer) => {
654
+ if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
655
+ return "A reason for requesting these assets is required.";
656
+ }
657
+ return true;
658
+ },
242
659
  },
243
660
  ]);
661
+ metadata.reason = reason;
244
662
  }
245
- async function promptForExpiration() {
246
- return await inquirer.prompt([
247
- {
248
- name: "expiration",
249
- message: "When should access expire?",
250
- type: "list",
251
- choices: ["1 hour", "1 day", "7 days", "30 days", "1 year", "Indefinite"],
663
+ async function promptForExpiration(metadata) {
664
+ var _a, _b;
665
+ 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;
670
+ return {
671
+ message: label,
672
+ value: {
673
+ label: label,
674
+ durationInMinutes: option.durationInMinutes,
675
+ toString: () => label,
676
+ },
677
+ };
678
+ })) || [];
679
+ // Sort durations by minutes
680
+ durations.sort((a, b) => a.value.durationInMinutes - b.value.durationInMinutes);
681
+ const expirationSelect = new AutoComplete({
682
+ name: "expiration",
683
+ message: "When should access expire?",
684
+ type: "list",
685
+ choices: durations,
686
+ pageSize: 15,
687
+ });
688
+ let selected = await expirationSelect.run();
689
+ switch (selected.label) {
690
+ case "Custom": {
691
+ selected = await setCustomDuration();
692
+ break;
693
+ }
694
+ case "Permanent": {
695
+ selected.durationInMinutes = undefined;
696
+ break;
697
+ }
698
+ }
699
+ metadata.durationInMinutes = selected.durationInMinutes;
700
+ metadata.durationLabel = selected.label;
701
+ }
702
+ async function setCustomDuration() {
703
+ const durationForm = new Form({
704
+ name: "user",
705
+ message: "Please set a custom access duration:",
706
+ choices: [
707
+ { name: "days", message: "Days", initial: "0" },
708
+ { name: "hours", message: "Hours", initial: "0" },
709
+ { name: "minutes", message: "Minutes", initial: "0" },
710
+ ],
711
+ 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)) {
719
+ return "Please enter a valid duration.";
720
+ }
721
+ return true;
252
722
  },
253
- ]);
723
+ });
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
+ }
737
+ return {
738
+ durationInMinutes: durationInMinutes,
739
+ label: durationLabel,
740
+ };
254
741
  }
255
- async function submitFinalRequest(cmd) {
742
+ async function submitFinalRequest(cmd, client, metadata) {
743
+ var _a, _b, _c, _d;
256
744
  const submitMessage = "✅ Yes, submit request";
257
745
  const cancelMessage = "❌ No, cancel request";
258
746
  const { submit } = await inquirer.prompt([
@@ -263,12 +751,65 @@ async function submitFinalRequest(cmd) {
263
751
  choices: [submitMessage, cancelMessage],
264
752
  },
265
753
  ]);
266
- if (submit === submitMessage) {
267
- const requestLink = "https://dev.opal.dev/requests/sent/05ca5d5f-ea60-4cdb-84e1-7e3c575b2b72"; //TODO: Replace with actual request link
268
- cmd.log("\n🎉 Your Access Request has been submitted! Request ID: 1234");
269
- cmd.log(`🔍 View request status here: ${requestLink}`);
270
- }
271
- else {
272
- cmd.log("🚫 Access Request has been cancelled.");
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
+ }
789
+ }
790
+ }
791
+ }
792
+ }
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));
801
+ }
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`);
804
+ }
805
+ return;
806
+ }
807
+ case cancelMessage: {
808
+ cmd.log("🚫 Access Request has been cancelled.");
809
+ return;
810
+ }
811
+ default: {
812
+ cmd.error("Unknown error occurred.");
813
+ }
273
814
  }
274
815
  }