opal-security 3.1.1-beta.778ef29 ā 3.1.1-beta.7e1cc21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -26
- package/lib/commands/login.js +6 -2
- package/lib/commands/request/create.d.ts +6 -1
- package/lib/commands/request/create.js +63 -19
- package/lib/commands/request/get.d.ts +7 -2
- package/lib/commands/request/get.js +105 -7
- package/lib/commands/request/list.d.ts +8 -2
- package/lib/commands/request/list.js +112 -8
- package/lib/commands/whoami.d.ts +8 -0
- package/lib/commands/whoami.js +34 -0
- package/lib/graphql/gql.d.ts +37 -2
- package/lib/graphql/gql.js +8 -1
- package/lib/graphql/graphql.d.ts +426 -118
- package/lib/graphql/graphql.js +2057 -208
- package/lib/lib/apollo.js +3 -4
- package/lib/lib/credentials/index.d.ts +2 -1
- package/lib/lib/credentials/index.js +2 -1
- package/lib/lib/flags.js +1 -1
- package/lib/lib/requests.d.ts +45 -14
- package/lib/lib/requests.js +919 -115
- package/lib/utils/displays.d.ts +8 -3
- package/lib/utils/displays.js +199 -45
- package/oclif.manifest.json +167 -46
- package/package.json +4 -1
package/lib/lib/requests.js
CHANGED
|
@@ -1,15 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DISPLAY_LABELS = void 0;
|
|
4
|
+
exports.initEmptyRequestMetadata = initEmptyRequestMetadata;
|
|
3
5
|
exports.selectRequestableItems = selectRequestableItems;
|
|
4
|
-
exports.chooseAssets = chooseAssets;
|
|
5
|
-
exports.chooseRoles = chooseRoles;
|
|
6
6
|
exports.doneSelectingAssets = doneSelectingAssets;
|
|
7
|
+
exports.setRequestDefaults = setRequestDefaults;
|
|
7
8
|
exports.promptForReason = promptForReason;
|
|
8
9
|
exports.promptForExpiration = promptForExpiration;
|
|
10
|
+
exports.promptRequestSubmission = promptRequestSubmission;
|
|
9
11
|
exports.submitFinalRequest = submitFinalRequest;
|
|
10
|
-
|
|
12
|
+
exports.bypassRequestSelection = bypassRequestSelection;
|
|
13
|
+
exports.bypassDuration = bypassDuration;
|
|
14
|
+
const chalk_1 = require("chalk");
|
|
11
15
|
const graphql_1 = require("../graphql");
|
|
12
|
-
|
|
16
|
+
const graphql_2 = require("../graphql/graphql");
|
|
17
|
+
const displays_1 = require("../utils/displays");
|
|
18
|
+
const config_1 = require("./config");
|
|
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
|
+
};
|
|
35
|
+
function initEmptyRequestMetadata() {
|
|
36
|
+
// Initialize with empty defaults
|
|
37
|
+
const requestDefaults = {
|
|
38
|
+
durationOptions: [],
|
|
39
|
+
recommendedDurationInMinutes: undefined,
|
|
40
|
+
defaultDurationInMinutes: undefined,
|
|
41
|
+
maxDurationInMinutes: undefined,
|
|
42
|
+
requireSupportTicket: false,
|
|
43
|
+
reasonOptional: false,
|
|
44
|
+
requesterIsAdmin: false,
|
|
45
|
+
};
|
|
46
|
+
// Initialize with empty map
|
|
47
|
+
const requestMap = {};
|
|
48
|
+
return {
|
|
49
|
+
requestMap,
|
|
50
|
+
requestDefaults,
|
|
51
|
+
durationLabel: "",
|
|
52
|
+
durationInMinutes: 0,
|
|
53
|
+
reason: "",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
13
56
|
// Queries and Mutations
|
|
14
57
|
// TODO: add pagination ability from CLI. (Load more...) option
|
|
15
58
|
const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
|
|
@@ -53,18 +96,23 @@ async function queryRequestableApps(cmd, client, input) {
|
|
|
53
96
|
});
|
|
54
97
|
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) => {
|
|
55
98
|
let type = undefined;
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
switch (edge.node.__typename) {
|
|
100
|
+
case "Resource":
|
|
101
|
+
type = edge.node.resourceType;
|
|
102
|
+
break;
|
|
103
|
+
case "Connection":
|
|
104
|
+
type = edge.node.connectionType;
|
|
105
|
+
break;
|
|
106
|
+
default:
|
|
107
|
+
type = edge.node.__typename;
|
|
58
108
|
}
|
|
59
|
-
if (edge.node.__typename === "Connection") {
|
|
60
|
-
type = edge.node.connectionType;
|
|
61
|
-
}
|
|
62
|
-
const label = `${edge.node.displayName} (${type})`;
|
|
63
109
|
return {
|
|
64
|
-
|
|
110
|
+
message: `${edge.node.displayName} [${type}]`,
|
|
65
111
|
value: {
|
|
66
112
|
id: edge.node.id,
|
|
67
|
-
name:
|
|
113
|
+
name: edge.node.displayName,
|
|
114
|
+
type: type,
|
|
115
|
+
toString: () => edge.node.displayName,
|
|
68
116
|
},
|
|
69
117
|
};
|
|
70
118
|
});
|
|
@@ -127,16 +175,16 @@ async function queryRequestableAssets(cmd, client, appId, input) {
|
|
|
127
175
|
switch (resp.data.app.__typename) {
|
|
128
176
|
case "App":
|
|
129
177
|
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) => {
|
|
130
|
-
var _a, _b, _c, _d, _e, _f;
|
|
178
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
131
179
|
const name = ((_a = item.resource) === null || _a === void 0 ? void 0 : _a.name) || ((_b = item.group) === null || _b === void 0 ? void 0 : _b.name);
|
|
132
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);
|
|
133
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);
|
|
134
|
-
const label = `${name} (${type})`;
|
|
135
182
|
return {
|
|
136
|
-
|
|
183
|
+
message: `${name} [${type}]`,
|
|
137
184
|
value: {
|
|
138
|
-
name:
|
|
139
|
-
id: id,
|
|
185
|
+
name: name || "",
|
|
186
|
+
id: id || "",
|
|
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)),
|
|
140
188
|
},
|
|
141
189
|
};
|
|
142
190
|
});
|
|
@@ -162,7 +210,6 @@ const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
|
|
|
162
210
|
__typename
|
|
163
211
|
... on ResourceAccessLevelsResult {
|
|
164
212
|
accessLevels {
|
|
165
|
-
__typename
|
|
166
213
|
... on ResourceAccessLevel {
|
|
167
214
|
accessLevelName
|
|
168
215
|
accessLevelRemoteId
|
|
@@ -175,31 +222,400 @@ const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
|
|
|
175
222
|
}
|
|
176
223
|
}
|
|
177
224
|
`);
|
|
178
|
-
|
|
179
|
-
|
|
225
|
+
const GROUP_ROLES_QUERY = (0, graphql_1.graphql)(`
|
|
226
|
+
query GroupAccessLevels($groupId: GroupId!) {
|
|
227
|
+
groupAccessLevels(
|
|
228
|
+
input: { groupId: $groupId }
|
|
229
|
+
) {
|
|
230
|
+
... on GroupAccessLevelsResult {
|
|
231
|
+
groupId
|
|
232
|
+
accessLevels {
|
|
233
|
+
... on GroupAccessLevel {
|
|
234
|
+
accessLevelName
|
|
235
|
+
accessLevelRemoteId
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
`);
|
|
242
|
+
async function queryAssetRoles(cmd, client, assetType, assetId) {
|
|
243
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
244
|
+
try {
|
|
245
|
+
switch (assetType) {
|
|
246
|
+
case graphql_2.EntityType.Resource: {
|
|
247
|
+
const resp = await client.query({
|
|
248
|
+
query: RESOURCE_ROLES_QUERY,
|
|
249
|
+
variables: {
|
|
250
|
+
resourceId: assetId,
|
|
251
|
+
},
|
|
252
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
253
|
+
});
|
|
254
|
+
// no fall through doesn't consider process.exit();
|
|
255
|
+
let x;
|
|
256
|
+
switch (resp.data.accessLevels.__typename) {
|
|
257
|
+
case "ResourceAccessLevelsResult":
|
|
258
|
+
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) => {
|
|
259
|
+
return {
|
|
260
|
+
message: role.accessLevelName || "",
|
|
261
|
+
value: {
|
|
262
|
+
name: role.accessLevelName || "",
|
|
263
|
+
id: role.accessLevelRemoteId || "",
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
case "ResourceNotFoundError":
|
|
268
|
+
x = cmd.error((_e = (_d = resp.data) === null || _d === void 0 ? void 0 : _d.accessLevels) === null || _e === void 0 ? void 0 : _e.message);
|
|
269
|
+
break;
|
|
270
|
+
default:
|
|
271
|
+
cmd.error(resp.error || "Unknown error occurred.");
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
case graphql_2.EntityType.Group: {
|
|
276
|
+
const resp = await client.query({
|
|
277
|
+
query: GROUP_ROLES_QUERY,
|
|
278
|
+
variables: {
|
|
279
|
+
groupId: assetId,
|
|
280
|
+
},
|
|
281
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
282
|
+
});
|
|
283
|
+
// no fall through doesn't consider process.exit();
|
|
284
|
+
let x;
|
|
285
|
+
switch (resp.data.groupAccessLevels.__typename) {
|
|
286
|
+
case "GroupAccessLevelsResult":
|
|
287
|
+
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) => {
|
|
288
|
+
return {
|
|
289
|
+
message: role.accessLevelName,
|
|
290
|
+
value: {
|
|
291
|
+
name: role.accessLevelName,
|
|
292
|
+
id: role.accessLevelRemoteId,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
default:
|
|
297
|
+
x = cmd.error(resp.error || "Unknown error occurred.");
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
if (error instanceof Error || typeof error === "string") {
|
|
305
|
+
cmd.error(error);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const REQUEST_DEFAULTS_QUERY = (0, graphql_1.graphql)(`
|
|
310
|
+
query RequestDefaults(
|
|
311
|
+
$requestedResources: [RequestConfigurationResourceInput!]!
|
|
312
|
+
$requestedGroups: [RequestConfigurationGroupInput!]!
|
|
313
|
+
) {
|
|
314
|
+
requestDefaults(input: {
|
|
315
|
+
requestedResources: $requestedResources,
|
|
316
|
+
requestedGroups: $requestedGroups,
|
|
317
|
+
}
|
|
318
|
+
) {
|
|
319
|
+
... on RequestDefaults {
|
|
320
|
+
durationOptions {
|
|
321
|
+
durationInMinutes
|
|
322
|
+
label
|
|
323
|
+
}
|
|
324
|
+
recommendedDurationInMinutes
|
|
325
|
+
defaultDurationInMinutes
|
|
326
|
+
maxDurationInMinutes
|
|
327
|
+
requireSupportTicket
|
|
328
|
+
reasonOptional
|
|
329
|
+
requesterIsAdmin
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}`);
|
|
333
|
+
async function queryRequestDefaults(cmd, client, requestedResources, requestedGroups) {
|
|
180
334
|
try {
|
|
181
335
|
const resp = await client.query({
|
|
182
|
-
query:
|
|
336
|
+
query: REQUEST_DEFAULTS_QUERY,
|
|
183
337
|
variables: {
|
|
184
|
-
|
|
338
|
+
requestedResources: requestedResources,
|
|
339
|
+
requestedGroups: requestedGroups,
|
|
185
340
|
},
|
|
186
341
|
fetchPolicy: "network-only", // to avoid caching
|
|
187
342
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
343
|
+
return resp.data.requestDefaults;
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (error instanceof Error || typeof error === "string") {
|
|
347
|
+
cmd.error(error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const CREATE_REQUEST_MUTATION = (0, graphql_1.graphql)(`
|
|
352
|
+
mutation CreateRequest(
|
|
353
|
+
$requestedResources: [RequestedResourceInput!]!
|
|
354
|
+
$requestedGroups: [RequestedGroupInput!]!
|
|
355
|
+
$reason: String!
|
|
356
|
+
$durationInMinutes: Int
|
|
357
|
+
) {
|
|
358
|
+
createRequest(
|
|
359
|
+
input: {
|
|
360
|
+
requestedResources: $requestedResources
|
|
361
|
+
requestedGroups: $requestedGroups
|
|
362
|
+
reason: $reason
|
|
363
|
+
durationInMinutes: $durationInMinutes
|
|
364
|
+
}
|
|
365
|
+
) {
|
|
366
|
+
... on CreateRequestResult {
|
|
367
|
+
request {
|
|
368
|
+
id
|
|
369
|
+
status
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
... on RequestDurationTooLargeError {
|
|
373
|
+
message
|
|
374
|
+
}
|
|
375
|
+
... on RequestRequiresUserAuthTokenForConnectionError {
|
|
376
|
+
message
|
|
377
|
+
}
|
|
378
|
+
... on NoReviewersSetForOwnerError {
|
|
379
|
+
message
|
|
380
|
+
ownerId
|
|
381
|
+
}
|
|
382
|
+
... on NoReviewersSetForResourceError {
|
|
383
|
+
message
|
|
384
|
+
resourceId
|
|
385
|
+
}
|
|
386
|
+
... on NoReviewersSetForGroupError {
|
|
387
|
+
message
|
|
388
|
+
groupId
|
|
389
|
+
}
|
|
390
|
+
... on NoManagerSetForRequestingUserError {
|
|
391
|
+
message
|
|
392
|
+
}
|
|
393
|
+
... on MfaInvalidError {
|
|
394
|
+
message
|
|
395
|
+
}
|
|
396
|
+
... on BulkRequestTooLargeError {
|
|
397
|
+
message
|
|
398
|
+
}
|
|
399
|
+
... on ItemCannotBeRequestedError {
|
|
400
|
+
message
|
|
401
|
+
}
|
|
402
|
+
... on UserCannotRequestAccessForTargetGroupError {
|
|
403
|
+
message
|
|
404
|
+
groupId
|
|
405
|
+
userId
|
|
406
|
+
}
|
|
407
|
+
... on GroupNestingNotAllowedError {
|
|
408
|
+
message
|
|
409
|
+
fromGroupId
|
|
410
|
+
toGroupId
|
|
411
|
+
}
|
|
412
|
+
... on TargetUserHasNestedAccessError {
|
|
413
|
+
message
|
|
414
|
+
groupIds
|
|
415
|
+
}
|
|
416
|
+
... on RequestReasonMissingError {
|
|
417
|
+
message
|
|
418
|
+
}
|
|
419
|
+
... on RequestFieldValueMissingError {
|
|
420
|
+
message
|
|
421
|
+
fieldName
|
|
422
|
+
}
|
|
423
|
+
... on LinkedGroupNotRequestableError {
|
|
424
|
+
message
|
|
425
|
+
sourceGroupId
|
|
426
|
+
groupBindingId
|
|
427
|
+
}
|
|
428
|
+
... on RequestReasonBelowMinLengthError {
|
|
429
|
+
message
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
`);
|
|
435
|
+
async function createRequest(cmd, client, requestedResources, requestedGroups, reason, durationInMinutes) {
|
|
436
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
437
|
+
try {
|
|
438
|
+
const resp = await client.mutate({
|
|
439
|
+
mutation: CREATE_REQUEST_MUTATION,
|
|
440
|
+
variables: {
|
|
441
|
+
requestedResources: requestedResources,
|
|
442
|
+
requestedGroups: requestedGroups,
|
|
443
|
+
reason: reason,
|
|
444
|
+
durationInMinutes: durationInMinutes,
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.createRequest.__typename) {
|
|
448
|
+
case "CreateRequestResult":
|
|
449
|
+
return (_b = resp.data) === null || _b === void 0 ? void 0 : _b.createRequest.request;
|
|
450
|
+
case "RequestDurationTooLargeError":
|
|
451
|
+
cmd.log((_c = resp.data) === null || _c === void 0 ? void 0 : _c.createRequest.message);
|
|
452
|
+
break;
|
|
453
|
+
case "RequestRequiresUserAuthTokenForConnectionError":
|
|
454
|
+
cmd.log((_d = resp.data) === null || _d === void 0 ? void 0 : _d.createRequest.message);
|
|
455
|
+
break;
|
|
456
|
+
case "NoReviewersSetForOwnerError":
|
|
457
|
+
cmd.log((_e = resp.data) === null || _e === void 0 ? void 0 : _e.createRequest.message);
|
|
458
|
+
break;
|
|
459
|
+
case "NoReviewersSetForResourceError":
|
|
460
|
+
cmd.log((_f = resp.data) === null || _f === void 0 ? void 0 : _f.createRequest.message);
|
|
461
|
+
break;
|
|
462
|
+
case "NoReviewersSetForGroupError":
|
|
463
|
+
cmd.log((_g = resp.data) === null || _g === void 0 ? void 0 : _g.createRequest.message);
|
|
464
|
+
break;
|
|
465
|
+
case "NoManagerSetForRequestingUserError":
|
|
466
|
+
cmd.log((_h = resp.data) === null || _h === void 0 ? void 0 : _h.createRequest.message);
|
|
467
|
+
break;
|
|
468
|
+
case "MfaInvalidError":
|
|
469
|
+
cmd.log((_j = resp.data) === null || _j === void 0 ? void 0 : _j.createRequest.message);
|
|
470
|
+
break;
|
|
471
|
+
case "BulkRequestTooLargeError":
|
|
472
|
+
cmd.log((_k = resp.data) === null || _k === void 0 ? void 0 : _k.createRequest.message);
|
|
473
|
+
break;
|
|
474
|
+
case "ItemCannotBeRequestedError":
|
|
475
|
+
cmd.log((_l = resp.data) === null || _l === void 0 ? void 0 : _l.createRequest.message);
|
|
476
|
+
break;
|
|
477
|
+
case "UserCannotRequestAccessForTargetGroupError":
|
|
478
|
+
cmd.log((_m = resp.data) === null || _m === void 0 ? void 0 : _m.createRequest.message);
|
|
479
|
+
break;
|
|
480
|
+
case "GroupNestingNotAllowedError":
|
|
481
|
+
cmd.log((_o = resp.data) === null || _o === void 0 ? void 0 : _o.createRequest.message);
|
|
482
|
+
break;
|
|
483
|
+
case "TargetUserHasNestedAccessError":
|
|
484
|
+
cmd.log((_p = resp.data) === null || _p === void 0 ? void 0 : _p.createRequest.message);
|
|
485
|
+
break;
|
|
486
|
+
case "RequestReasonMissingError":
|
|
487
|
+
cmd.log((_q = resp.data) === null || _q === void 0 ? void 0 : _q.createRequest.message);
|
|
488
|
+
break;
|
|
489
|
+
case "RequestFieldValueMissingError":
|
|
490
|
+
cmd.log((_r = resp.data) === null || _r === void 0 ? void 0 : _r.createRequest.message);
|
|
491
|
+
break;
|
|
492
|
+
case "LinkedGroupNotRequestableError":
|
|
493
|
+
cmd.log((_s = resp.data) === null || _s === void 0 ? void 0 : _s.createRequest.message);
|
|
494
|
+
break;
|
|
495
|
+
case "RequestReasonBelowMinLengthError":
|
|
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
|
+
}
|
|
201
617
|
case "ResourceNotFoundError":
|
|
202
|
-
|
|
618
|
+
cmd.log((_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.resource) === null || _b === void 0 ? void 0 : _b.message);
|
|
203
619
|
break;
|
|
204
620
|
default:
|
|
205
621
|
cmd.error(resp.error || "Unknown error occurred.");
|
|
@@ -212,36 +628,126 @@ async function queryResourceRoles(cmd, client, resourceId) {
|
|
|
212
628
|
}
|
|
213
629
|
}
|
|
214
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");
|
|
215
633
|
async function selectRequestableItems(cmd, client, requestMap) {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
634
|
+
const initial = (await queryRequestableApps(cmd, client, "")) || [];
|
|
635
|
+
const appPrompt = new AutoComplete({
|
|
636
|
+
name: "App",
|
|
637
|
+
message: "Select an app",
|
|
638
|
+
hint: selectInstructions,
|
|
639
|
+
limit: 15,
|
|
640
|
+
choices: initial,
|
|
641
|
+
async suggest(input) {
|
|
642
|
+
const filteredChoices = await queryRequestableApps(cmd, client, input || "");
|
|
643
|
+
return filteredChoices || initial;
|
|
226
644
|
},
|
|
227
|
-
|
|
645
|
+
});
|
|
646
|
+
const App = await appPrompt.run();
|
|
228
647
|
// Set the app in the requestMap and call choose assets step
|
|
229
|
-
if (!
|
|
230
|
-
requestMap
|
|
648
|
+
if (!(App.id in requestMap)) {
|
|
649
|
+
requestMap[App.id] = {
|
|
650
|
+
appId: App.id,
|
|
231
651
|
appName: App.name,
|
|
232
|
-
assets:
|
|
233
|
-
}
|
|
652
|
+
assets: {},
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
if (App.type === "OKTA_APP" || App.type === "AZURE_ENTERPRISE_APP") {
|
|
656
|
+
await chooseOktaAzureRoles(cmd, client, App, requestMap);
|
|
657
|
+
return;
|
|
234
658
|
}
|
|
235
659
|
await chooseAssets(cmd, client, App.id, requestMap);
|
|
236
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
|
+
}
|
|
237
736
|
async function chooseAssets(cmd, client, appId, requestMap) {
|
|
238
|
-
|
|
239
|
-
const
|
|
737
|
+
const initial = (await queryRequestableAssets(cmd, client, appId, "")) || [];
|
|
738
|
+
const assetPrompt = new AutoComplete({
|
|
240
739
|
name: "Assets",
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
740
|
+
message: "Select one or more assets:",
|
|
741
|
+
hint: multiSelectInstructions,
|
|
742
|
+
limit: 15,
|
|
743
|
+
multiple: true,
|
|
744
|
+
async choices(input) {
|
|
745
|
+
if (!input) {
|
|
746
|
+
return initial;
|
|
747
|
+
}
|
|
748
|
+
const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
|
|
749
|
+
return filteredChoices || initial;
|
|
750
|
+
},
|
|
245
751
|
validate: (answer) => {
|
|
246
752
|
if (answer.length < 1) {
|
|
247
753
|
return "You must select at least one item.";
|
|
@@ -249,100 +755,398 @@ async function chooseAssets(cmd, client, appId, requestMap) {
|
|
|
249
755
|
return true;
|
|
250
756
|
},
|
|
251
757
|
});
|
|
252
|
-
const
|
|
758
|
+
const Assets = await assetPrompt.run();
|
|
759
|
+
const entry = requestMap[appId];
|
|
253
760
|
for (const asset of Assets) {
|
|
254
761
|
if (entry === undefined) {
|
|
255
|
-
throw new Error(`
|
|
762
|
+
throw new Error(`Error formatting app ${appId} in request`);
|
|
256
763
|
}
|
|
257
|
-
if (!
|
|
258
|
-
entry.assets
|
|
764
|
+
if (!(asset.id in entry.assets)) {
|
|
765
|
+
entry.assets[asset.id] = {
|
|
766
|
+
assetId: asset.id,
|
|
259
767
|
assetName: asset.name,
|
|
260
|
-
|
|
261
|
-
|
|
768
|
+
type: asset.type,
|
|
769
|
+
roles: {},
|
|
770
|
+
};
|
|
262
771
|
}
|
|
263
772
|
await chooseRoles(cmd, client, appId, asset.id, requestMap);
|
|
264
773
|
}
|
|
265
774
|
}
|
|
266
775
|
async function chooseRoles(cmd, client, appId, assetId, requestMap) {
|
|
267
|
-
var _a
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
776
|
+
var _a;
|
|
777
|
+
const entry = requestMap[appId];
|
|
778
|
+
const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets[assetId];
|
|
779
|
+
if (entry === undefined || assetEntry === undefined) {
|
|
780
|
+
throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
|
|
781
|
+
}
|
|
782
|
+
const assetRoles = (_a = (await queryAssetRoles(cmd, client, assetEntry.type, assetId))) !== null && _a !== void 0 ? _a : [];
|
|
783
|
+
if (assetRoles !== undefined &&
|
|
784
|
+
(assetRoles.length === 0 ||
|
|
785
|
+
(assetRoles.length === 1 && assetRoles[0].value.name === ""))) {
|
|
272
786
|
return;
|
|
273
787
|
}
|
|
274
|
-
const
|
|
275
|
-
name: "
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
788
|
+
const rolePrompt = new AutoComplete({
|
|
789
|
+
name: "Roles",
|
|
790
|
+
message: `Select one or more roles for ${assetEntry.assetName}:`,
|
|
791
|
+
hint: multiSelectInstructions,
|
|
792
|
+
limit: 15,
|
|
793
|
+
multiple: true,
|
|
794
|
+
choices: assetRoles,
|
|
279
795
|
validate: (answer) => {
|
|
280
|
-
if (
|
|
281
|
-
return "You must select at least one
|
|
796
|
+
if (answer.length < 1) {
|
|
797
|
+
return "You must select at least one item.";
|
|
282
798
|
}
|
|
283
799
|
return true;
|
|
284
800
|
},
|
|
285
801
|
});
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
|
|
802
|
+
const roles = await rolePrompt.run();
|
|
803
|
+
if (!assetEntry.roles) {
|
|
804
|
+
assetEntry.roles = {};
|
|
290
805
|
}
|
|
291
806
|
for (const role of roles) {
|
|
292
|
-
|
|
807
|
+
assetEntry.roles[role.id] = {
|
|
808
|
+
roleId: role.id,
|
|
293
809
|
roleName: role.name,
|
|
294
|
-
}
|
|
810
|
+
};
|
|
295
811
|
}
|
|
296
812
|
}
|
|
297
813
|
async function doneSelectingAssets() {
|
|
298
814
|
const submitMessage = "ā
Yes, proceed with request";
|
|
299
815
|
const addMoreMessage = "ā No, add more items";
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
},
|
|
307
|
-
]);
|
|
816
|
+
const prompt = new Select({
|
|
817
|
+
name: "submitOrAdd",
|
|
818
|
+
message: "Is this all you want to request?",
|
|
819
|
+
choices: [submitMessage, addMoreMessage],
|
|
820
|
+
});
|
|
821
|
+
const submitOrAdd = await prompt.run();
|
|
308
822
|
return submitOrAdd === submitMessage;
|
|
309
823
|
}
|
|
310
|
-
async function
|
|
311
|
-
|
|
824
|
+
async function setRequestDefaults(cmd, client, metadata) {
|
|
825
|
+
const requestMap = metadata.requestMap;
|
|
826
|
+
const requestedResources = [];
|
|
827
|
+
const requestedGroups = [];
|
|
828
|
+
for (const appNode of Object.values(requestMap)) {
|
|
829
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
830
|
+
if (assetNode.roles !== undefined) {
|
|
831
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
832
|
+
return roleId;
|
|
833
|
+
});
|
|
834
|
+
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
835
|
+
for (const roleId of roleIds) {
|
|
836
|
+
switch (assetNode.type) {
|
|
837
|
+
case graphql_2.EntityType.Resource: {
|
|
838
|
+
requestedResources.push({
|
|
839
|
+
resourceId: assetId,
|
|
840
|
+
accessLevelRemoteId: roleId,
|
|
841
|
+
});
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
case graphql_2.EntityType.Group: {
|
|
845
|
+
requestedGroups.push({
|
|
846
|
+
groupId: assetId,
|
|
847
|
+
accessLevelRemoteId: roleId,
|
|
848
|
+
});
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
try {
|
|
857
|
+
const requestDefaults = await queryRequestDefaults(cmd, client, requestedResources, requestedGroups);
|
|
858
|
+
if (requestDefaults !== undefined) {
|
|
859
|
+
metadata.requestDefaults.durationOptions =
|
|
860
|
+
requestDefaults.durationOptions;
|
|
861
|
+
metadata.requestDefaults.recommendedDurationInMinutes =
|
|
862
|
+
requestDefaults.recommendedDurationInMinutes;
|
|
863
|
+
metadata.requestDefaults.defaultDurationInMinutes =
|
|
864
|
+
requestDefaults.defaultDurationInMinutes;
|
|
865
|
+
metadata.requestDefaults.maxDurationInMinutes =
|
|
866
|
+
requestDefaults.maxDurationInMinutes;
|
|
867
|
+
metadata.requestDefaults.requireSupportTicket =
|
|
868
|
+
requestDefaults.requireSupportTicket;
|
|
869
|
+
metadata.requestDefaults.reasonOptional = requestDefaults.reasonOptional;
|
|
870
|
+
metadata.requestDefaults.requesterIsAdmin =
|
|
871
|
+
requestDefaults.requesterIsAdmin;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
catch (_a) {
|
|
875
|
+
cmd.error("Error fetching request defaults.");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async function promptForReason(metadata) {
|
|
879
|
+
const { reason } = await prompt([
|
|
312
880
|
{
|
|
313
881
|
name: "reason",
|
|
314
|
-
message: "
|
|
882
|
+
message: "Why do you need access?",
|
|
315
883
|
type: "input",
|
|
884
|
+
validate: (answer) => {
|
|
885
|
+
if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
|
|
886
|
+
return "A reason for requesting these assets is required.";
|
|
887
|
+
}
|
|
888
|
+
return true;
|
|
889
|
+
},
|
|
316
890
|
},
|
|
317
891
|
]);
|
|
892
|
+
metadata.reason = reason;
|
|
318
893
|
}
|
|
319
|
-
async function promptForExpiration() {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
894
|
+
async function promptForExpiration(metadata) {
|
|
895
|
+
var _a, _b;
|
|
896
|
+
const durations = ((_b = (_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.durationOptions) === null || _b === void 0 ? void 0 : _b.map((option) => {
|
|
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
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
message: label,
|
|
909
|
+
value: {
|
|
910
|
+
label: label,
|
|
911
|
+
durationInMinutes: option.durationInMinutes,
|
|
912
|
+
toString: () => label,
|
|
913
|
+
},
|
|
914
|
+
};
|
|
915
|
+
})) || [];
|
|
916
|
+
// Sort durations by minutes
|
|
917
|
+
durations.sort((a, b) => a.value.durationInMinutes - b.value.durationInMinutes);
|
|
918
|
+
const expirationSelect = new AutoComplete({
|
|
919
|
+
name: "expiration",
|
|
920
|
+
message: "When should access expire?",
|
|
921
|
+
hint: "Type to filter",
|
|
922
|
+
type: "list",
|
|
923
|
+
choices: durations,
|
|
924
|
+
pageSize: 15,
|
|
925
|
+
});
|
|
926
|
+
let selected = await expirationSelect.run();
|
|
927
|
+
switch (selected.label) {
|
|
928
|
+
case "Custom": {
|
|
929
|
+
selected = await setCustomDuration(metadata);
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
case "Permanent": {
|
|
933
|
+
selected.durationInMinutes = undefined;
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
metadata.durationInMinutes = selected.durationInMinutes;
|
|
938
|
+
metadata.durationLabel = selected.label;
|
|
939
|
+
}
|
|
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) {
|
|
970
|
+
const durationForm = new Form({
|
|
971
|
+
name: "user",
|
|
972
|
+
message: "Please set a custom access duration:",
|
|
973
|
+
choices: [
|
|
974
|
+
{ name: "days", message: "Days", initial: "0" },
|
|
975
|
+
{ name: "hours", message: "Hours", initial: "0" },
|
|
976
|
+
{ name: "minutes", message: "Minutes", initial: "0" },
|
|
977
|
+
],
|
|
978
|
+
validate: (answer) => {
|
|
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)) {
|
|
983
|
+
return "Please enter a valid duration.";
|
|
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
|
+
}
|
|
990
|
+
return true;
|
|
326
991
|
},
|
|
327
|
-
|
|
992
|
+
return: (answer) => {
|
|
993
|
+
return getDurationInMinutes(answer);
|
|
994
|
+
},
|
|
995
|
+
});
|
|
996
|
+
const durationResult = await durationForm.run();
|
|
997
|
+
const { d, h, m } = getDurationNumbers(durationResult);
|
|
998
|
+
const durationInMinutes = getDurationInMinutes(durationResult);
|
|
999
|
+
const durationLabel = getDHMFromMinutes(durationInMinutes);
|
|
1000
|
+
return {
|
|
1001
|
+
durationInMinutes: durationInMinutes,
|
|
1002
|
+
label: durationLabel,
|
|
1003
|
+
};
|
|
328
1004
|
}
|
|
329
|
-
async function
|
|
1005
|
+
async function promptRequestSubmission(cmd, metadata) {
|
|
1006
|
+
(0, displays_1.displayFinalRequestSummary)(cmd, metadata);
|
|
330
1007
|
const submitMessage = "ā
Yes, submit request";
|
|
331
1008
|
const cancelMessage = "ā No, cancel request";
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
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`);
|
|
344
1070
|
}
|
|
345
|
-
|
|
346
|
-
|
|
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;
|
|
1133
|
+
}
|
|
1134
|
+
default:
|
|
1135
|
+
cmd.error("Invalid asset id was passed in using the --id flag.");
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
catch (error) {
|
|
1140
|
+
if (error instanceof Error || typeof error === "string") {
|
|
1141
|
+
cmd.error(error);
|
|
1142
|
+
}
|
|
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}`);
|
|
347
1150
|
}
|
|
1151
|
+
metadata.durationInMinutes = duration;
|
|
348
1152
|
}
|