opal-security 3.1.3 ā 3.2.1
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 +160 -28
- package/lib/commands/groups/get.js +1 -2
- 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 +9 -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 +42 -2
- package/lib/graphql/gql.js +9 -1
- package/lib/graphql/graphql.d.ts +444 -117
- package/lib/graphql/graphql.js +2173 -173
- 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 +46 -14
- package/lib/lib/requests.js +1006 -120
- package/lib/utils/displays.d.ts +8 -3
- package/lib/utils/displays.js +181 -45
- package/oclif.manifest.json +190 -66
- package/package.json +5 -5
package/lib/lib/requests.js
CHANGED
|
@@ -1,16 +1,61 @@
|
|
|
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.getRequestLink = getRequestLink;
|
|
13
|
+
exports.bypassRequestSelection = bypassRequestSelection;
|
|
14
|
+
exports.bypassDuration = bypassDuration;
|
|
15
|
+
const chalk_1 = require("chalk");
|
|
11
16
|
const graphql_1 = require("../graphql");
|
|
12
|
-
|
|
17
|
+
const graphql_2 = require("../graphql/graphql");
|
|
18
|
+
const displays_1 = require("../utils/displays");
|
|
19
|
+
const config_1 = require("./config");
|
|
20
|
+
const { AutoComplete, Select, prompt, Form } = require("enquirer");
|
|
21
|
+
function entityTypeFromString(str) {
|
|
22
|
+
const capStr = str === null || str === void 0 ? void 0 : str.toLocaleUpperCase();
|
|
23
|
+
if (capStr === "RESOURCE") {
|
|
24
|
+
return graphql_2.EntityType.Resource;
|
|
25
|
+
}
|
|
26
|
+
if (capStr === "GROUP") {
|
|
27
|
+
return graphql_2.EntityType.Group;
|
|
28
|
+
}
|
|
29
|
+
// if type unknown, default to resource
|
|
30
|
+
return graphql_2.EntityType.Resource;
|
|
31
|
+
}
|
|
32
|
+
exports.DISPLAY_LABELS = {
|
|
33
|
+
[graphql_2.EntityType.Resource]: "Resource",
|
|
34
|
+
[graphql_2.EntityType.Group]: "Group",
|
|
35
|
+
};
|
|
36
|
+
function initEmptyRequestMetadata() {
|
|
37
|
+
// Initialize with empty defaults
|
|
38
|
+
const requestDefaults = {
|
|
39
|
+
durationOptions: [],
|
|
40
|
+
recommendedDurationInMinutes: undefined,
|
|
41
|
+
defaultDurationInMinutes: undefined,
|
|
42
|
+
maxDurationInMinutes: undefined,
|
|
43
|
+
requireSupportTicket: false,
|
|
44
|
+
reasonOptional: false,
|
|
45
|
+
requesterIsAdmin: false,
|
|
46
|
+
};
|
|
47
|
+
// Initialize with empty map
|
|
48
|
+
const requestMap = {};
|
|
49
|
+
return {
|
|
50
|
+
requestMap,
|
|
51
|
+
requestDefaults,
|
|
52
|
+
durationLabel: "",
|
|
53
|
+
durationInMinutes: 0,
|
|
54
|
+
reason: "",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
13
57
|
// Queries and Mutations
|
|
58
|
+
// TODO: add pagination ability from CLI. (Load more...) option
|
|
14
59
|
const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
|
|
15
60
|
query GetRequestableAppsQuery($searchQuery: String) {
|
|
16
61
|
appsV2(
|
|
@@ -40,6 +85,45 @@ const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
|
|
|
40
85
|
}
|
|
41
86
|
}
|
|
42
87
|
`);
|
|
88
|
+
async function queryRequestableApps(cmd, client, input) {
|
|
89
|
+
var _a, _b;
|
|
90
|
+
try {
|
|
91
|
+
const resp = await client.query({
|
|
92
|
+
query: GET_REQUESTABLE_APPS_QUERY,
|
|
93
|
+
variables: {
|
|
94
|
+
searchQuery: input || "",
|
|
95
|
+
},
|
|
96
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
97
|
+
});
|
|
98
|
+
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) => {
|
|
99
|
+
let type = undefined;
|
|
100
|
+
switch (edge.node.__typename) {
|
|
101
|
+
case "Resource":
|
|
102
|
+
type = edge.node.resourceType;
|
|
103
|
+
break;
|
|
104
|
+
case "Connection":
|
|
105
|
+
type = edge.node.connectionType;
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
type = edge.node.__typename;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
message: `${edge.node.displayName} [${type}]`,
|
|
112
|
+
value: {
|
|
113
|
+
id: edge.node.id,
|
|
114
|
+
name: edge.node.displayName,
|
|
115
|
+
type: type,
|
|
116
|
+
toString: () => edge.node.displayName,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error instanceof Error || typeof error === "string") {
|
|
123
|
+
cmd.error(error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
43
127
|
const GET_ASSETS_QUERY = (0, graphql_1.graphql)(`
|
|
44
128
|
query PaginatedEntityDropdown(
|
|
45
129
|
$id: UUID!
|
|
@@ -70,45 +154,14 @@ const GET_ASSETS_QUERY = (0, graphql_1.graphql)(`
|
|
|
70
154
|
cursor
|
|
71
155
|
}
|
|
72
156
|
}
|
|
157
|
+
... on AppNotFoundError {
|
|
158
|
+
message
|
|
159
|
+
}
|
|
73
160
|
}
|
|
74
161
|
}
|
|
75
162
|
`);
|
|
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
163
|
async function queryRequestableAssets(cmd, client, appId, input) {
|
|
111
|
-
var _a, _b, _c, _d;
|
|
164
|
+
var _a, _b, _c, _d, _e, _f;
|
|
112
165
|
try {
|
|
113
166
|
const resp = await client.query({
|
|
114
167
|
query: GET_ASSETS_QUERY,
|
|
@@ -123,24 +176,450 @@ async function queryRequestableAssets(cmd, client, appId, input) {
|
|
|
123
176
|
switch (resp.data.app.__typename) {
|
|
124
177
|
case "App":
|
|
125
178
|
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;
|
|
179
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
127
180
|
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
181
|
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
182
|
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
183
|
return {
|
|
132
|
-
|
|
184
|
+
message: `${name} [${type}]`,
|
|
133
185
|
value: {
|
|
134
|
-
name:
|
|
135
|
-
id: id,
|
|
186
|
+
name: name || "",
|
|
187
|
+
id: id || "",
|
|
188
|
+
type: entityTypeFromString(((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename)),
|
|
136
189
|
},
|
|
137
190
|
};
|
|
138
191
|
});
|
|
139
192
|
case "AppNotFoundError":
|
|
140
|
-
x = cmd.error(
|
|
193
|
+
x = cmd.error((_f = (_e = resp.data) === null || _e === void 0 ? void 0 : _e.app) === null || _f === void 0 ? void 0 : _f.message);
|
|
141
194
|
break;
|
|
142
195
|
default:
|
|
143
|
-
cmd.error("Unknown error occurred.");
|
|
196
|
+
cmd.error(resp.error || "Unknown error occurred.");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (error instanceof Error || typeof error === "string") {
|
|
201
|
+
cmd.error(error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
|
|
206
|
+
query ResourceAccessLevels($resourceId: ResourceId!) {
|
|
207
|
+
accessLevels(input: {
|
|
208
|
+
resourceId: $resourceId,
|
|
209
|
+
onlyMine: false,
|
|
210
|
+
}) {
|
|
211
|
+
__typename
|
|
212
|
+
... on ResourceAccessLevelsResult {
|
|
213
|
+
accessLevels {
|
|
214
|
+
... on ResourceAccessLevel {
|
|
215
|
+
accessLevelName
|
|
216
|
+
accessLevelRemoteId
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
... on ResourceNotFoundError {
|
|
221
|
+
message
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
`);
|
|
226
|
+
const GROUP_ROLES_QUERY = (0, graphql_1.graphql)(`
|
|
227
|
+
query GroupAccessLevels($groupId: GroupId!) {
|
|
228
|
+
groupAccessLevels(
|
|
229
|
+
input: { groupId: $groupId }
|
|
230
|
+
) {
|
|
231
|
+
... on GroupAccessLevelsResult {
|
|
232
|
+
groupId
|
|
233
|
+
accessLevels {
|
|
234
|
+
... on GroupAccessLevel {
|
|
235
|
+
accessLevelName
|
|
236
|
+
accessLevelRemoteId
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
`);
|
|
243
|
+
async function queryAssetRoles(cmd, client, assetType, assetId) {
|
|
244
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
245
|
+
try {
|
|
246
|
+
switch (assetType) {
|
|
247
|
+
case graphql_2.EntityType.Resource: {
|
|
248
|
+
const resp = await client.query({
|
|
249
|
+
query: RESOURCE_ROLES_QUERY,
|
|
250
|
+
variables: {
|
|
251
|
+
resourceId: assetId,
|
|
252
|
+
},
|
|
253
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
254
|
+
});
|
|
255
|
+
// no fall through doesn't consider process.exit();
|
|
256
|
+
let x;
|
|
257
|
+
switch (resp.data.accessLevels.__typename) {
|
|
258
|
+
case "ResourceAccessLevelsResult":
|
|
259
|
+
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) => {
|
|
260
|
+
return {
|
|
261
|
+
message: role.accessLevelName || "",
|
|
262
|
+
value: {
|
|
263
|
+
name: role.accessLevelName || "",
|
|
264
|
+
id: role.accessLevelRemoteId || "",
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
case "ResourceNotFoundError":
|
|
269
|
+
x = cmd.error((_e = (_d = resp.data) === null || _d === void 0 ? void 0 : _d.accessLevels) === null || _e === void 0 ? void 0 : _e.message);
|
|
270
|
+
break;
|
|
271
|
+
default:
|
|
272
|
+
cmd.error(resp.error || "Unknown error occurred.");
|
|
273
|
+
}
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
case graphql_2.EntityType.Group: {
|
|
277
|
+
const resp = await client.query({
|
|
278
|
+
query: GROUP_ROLES_QUERY,
|
|
279
|
+
variables: {
|
|
280
|
+
groupId: assetId,
|
|
281
|
+
},
|
|
282
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
283
|
+
});
|
|
284
|
+
// no fall through doesn't consider process.exit();
|
|
285
|
+
let x;
|
|
286
|
+
switch (resp.data.groupAccessLevels.__typename) {
|
|
287
|
+
case "GroupAccessLevelsResult":
|
|
288
|
+
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) => {
|
|
289
|
+
return {
|
|
290
|
+
message: role.accessLevelName,
|
|
291
|
+
value: {
|
|
292
|
+
name: role.accessLevelName,
|
|
293
|
+
id: role.accessLevelRemoteId,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
default:
|
|
298
|
+
x = cmd.error(resp.error || "Unknown error occurred.");
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
if (error instanceof Error || typeof error === "string") {
|
|
306
|
+
cmd.error(error);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const REQUEST_DEFAULTS_QUERY = (0, graphql_1.graphql)(`
|
|
311
|
+
query RequestDefaults(
|
|
312
|
+
$requestedResources: [RequestConfigurationResourceInput!]!
|
|
313
|
+
$requestedGroups: [RequestConfigurationGroupInput!]!
|
|
314
|
+
) {
|
|
315
|
+
requestDefaults(input: {
|
|
316
|
+
requestedResources: $requestedResources,
|
|
317
|
+
requestedGroups: $requestedGroups,
|
|
318
|
+
}
|
|
319
|
+
) {
|
|
320
|
+
... on RequestDefaults {
|
|
321
|
+
durationOptions {
|
|
322
|
+
durationInMinutes
|
|
323
|
+
label
|
|
324
|
+
}
|
|
325
|
+
recommendedDurationInMinutes
|
|
326
|
+
defaultDurationInMinutes
|
|
327
|
+
maxDurationInMinutes
|
|
328
|
+
requireSupportTicket
|
|
329
|
+
reasonOptional
|
|
330
|
+
requesterIsAdmin
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}`);
|
|
334
|
+
async function queryRequestDefaults(cmd, client, requestedResources, requestedGroups) {
|
|
335
|
+
try {
|
|
336
|
+
const resp = await client.query({
|
|
337
|
+
query: REQUEST_DEFAULTS_QUERY,
|
|
338
|
+
variables: {
|
|
339
|
+
requestedResources: requestedResources,
|
|
340
|
+
requestedGroups: requestedGroups,
|
|
341
|
+
},
|
|
342
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
343
|
+
});
|
|
344
|
+
return resp.data.requestDefaults;
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
if (error instanceof Error || typeof error === "string") {
|
|
348
|
+
cmd.error(error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const CREATE_REQUEST_MUTATION = (0, graphql_1.graphql)(`
|
|
353
|
+
mutation CreateRequest(
|
|
354
|
+
$requestedResources: [RequestedResourceInput!]!
|
|
355
|
+
$requestedGroups: [RequestedGroupInput!]!
|
|
356
|
+
$reason: String!
|
|
357
|
+
$durationInMinutes: Int
|
|
358
|
+
) {
|
|
359
|
+
createRequest(
|
|
360
|
+
input: {
|
|
361
|
+
requestedResources: $requestedResources
|
|
362
|
+
requestedGroups: $requestedGroups
|
|
363
|
+
reason: $reason
|
|
364
|
+
durationInMinutes: $durationInMinutes
|
|
365
|
+
}
|
|
366
|
+
) {
|
|
367
|
+
... on CreateRequestResult {
|
|
368
|
+
request {
|
|
369
|
+
id
|
|
370
|
+
status
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
... on RequestDurationTooLargeError {
|
|
374
|
+
message
|
|
375
|
+
}
|
|
376
|
+
... on RequestRequiresUserAuthTokenForConnectionError {
|
|
377
|
+
message
|
|
378
|
+
}
|
|
379
|
+
... on NoReviewersSetForOwnerError {
|
|
380
|
+
message
|
|
381
|
+
ownerId
|
|
382
|
+
}
|
|
383
|
+
... on NoReviewersSetForResourceError {
|
|
384
|
+
message
|
|
385
|
+
resourceId
|
|
386
|
+
}
|
|
387
|
+
... on NoReviewersSetForGroupError {
|
|
388
|
+
message
|
|
389
|
+
groupId
|
|
390
|
+
}
|
|
391
|
+
... on NoManagerSetForRequestingUserError {
|
|
392
|
+
message
|
|
393
|
+
}
|
|
394
|
+
... on MfaInvalidError {
|
|
395
|
+
message
|
|
396
|
+
}
|
|
397
|
+
... on BulkRequestTooLargeError {
|
|
398
|
+
message
|
|
399
|
+
}
|
|
400
|
+
... on ItemCannotBeRequestedError {
|
|
401
|
+
message
|
|
402
|
+
}
|
|
403
|
+
... on UserCannotRequestAccessForTargetGroupError {
|
|
404
|
+
message
|
|
405
|
+
groupId
|
|
406
|
+
userId
|
|
407
|
+
}
|
|
408
|
+
... on GroupNestingNotAllowedError {
|
|
409
|
+
message
|
|
410
|
+
fromGroupId
|
|
411
|
+
toGroupId
|
|
412
|
+
}
|
|
413
|
+
... on TargetUserHasNestedAccessError {
|
|
414
|
+
message
|
|
415
|
+
groupIds
|
|
416
|
+
}
|
|
417
|
+
... on RequestReasonMissingError {
|
|
418
|
+
message
|
|
419
|
+
}
|
|
420
|
+
... on RequestFieldValueMissingError {
|
|
421
|
+
message
|
|
422
|
+
fieldName
|
|
423
|
+
}
|
|
424
|
+
... on LinkedGroupNotRequestableError {
|
|
425
|
+
message
|
|
426
|
+
sourceGroupId
|
|
427
|
+
groupBindingId
|
|
428
|
+
}
|
|
429
|
+
... on RequestReasonBelowMinLengthError {
|
|
430
|
+
message
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
`);
|
|
436
|
+
async function createRequest(cmd, client, requestedResources, requestedGroups, reason, durationInMinutes) {
|
|
437
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
438
|
+
try {
|
|
439
|
+
const resp = await client.mutate({
|
|
440
|
+
mutation: CREATE_REQUEST_MUTATION,
|
|
441
|
+
variables: {
|
|
442
|
+
requestedResources: requestedResources,
|
|
443
|
+
requestedGroups: requestedGroups,
|
|
444
|
+
reason: reason,
|
|
445
|
+
durationInMinutes: durationInMinutes,
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.createRequest.__typename) {
|
|
449
|
+
case "CreateRequestResult":
|
|
450
|
+
return (_b = resp.data) === null || _b === void 0 ? void 0 : _b.createRequest.request;
|
|
451
|
+
case "RequestDurationTooLargeError":
|
|
452
|
+
cmd.log((_c = resp.data) === null || _c === void 0 ? void 0 : _c.createRequest.message);
|
|
453
|
+
break;
|
|
454
|
+
case "RequestRequiresUserAuthTokenForConnectionError":
|
|
455
|
+
cmd.log((_d = resp.data) === null || _d === void 0 ? void 0 : _d.createRequest.message);
|
|
456
|
+
break;
|
|
457
|
+
case "NoReviewersSetForOwnerError":
|
|
458
|
+
cmd.log((_e = resp.data) === null || _e === void 0 ? void 0 : _e.createRequest.message);
|
|
459
|
+
break;
|
|
460
|
+
case "NoReviewersSetForResourceError":
|
|
461
|
+
cmd.log((_f = resp.data) === null || _f === void 0 ? void 0 : _f.createRequest.message);
|
|
462
|
+
break;
|
|
463
|
+
case "NoReviewersSetForGroupError":
|
|
464
|
+
cmd.log((_g = resp.data) === null || _g === void 0 ? void 0 : _g.createRequest.message);
|
|
465
|
+
break;
|
|
466
|
+
case "NoManagerSetForRequestingUserError":
|
|
467
|
+
cmd.log((_h = resp.data) === null || _h === void 0 ? void 0 : _h.createRequest.message);
|
|
468
|
+
break;
|
|
469
|
+
case "MfaInvalidError":
|
|
470
|
+
cmd.log((_j = resp.data) === null || _j === void 0 ? void 0 : _j.createRequest.message);
|
|
471
|
+
break;
|
|
472
|
+
case "BulkRequestTooLargeError":
|
|
473
|
+
cmd.log((_k = resp.data) === null || _k === void 0 ? void 0 : _k.createRequest.message);
|
|
474
|
+
break;
|
|
475
|
+
case "ItemCannotBeRequestedError":
|
|
476
|
+
cmd.log((_l = resp.data) === null || _l === void 0 ? void 0 : _l.createRequest.message);
|
|
477
|
+
break;
|
|
478
|
+
case "UserCannotRequestAccessForTargetGroupError":
|
|
479
|
+
cmd.log((_m = resp.data) === null || _m === void 0 ? void 0 : _m.createRequest.message);
|
|
480
|
+
break;
|
|
481
|
+
case "GroupNestingNotAllowedError":
|
|
482
|
+
cmd.log((_o = resp.data) === null || _o === void 0 ? void 0 : _o.createRequest.message);
|
|
483
|
+
break;
|
|
484
|
+
case "TargetUserHasNestedAccessError":
|
|
485
|
+
cmd.log((_p = resp.data) === null || _p === void 0 ? void 0 : _p.createRequest.message);
|
|
486
|
+
break;
|
|
487
|
+
case "RequestReasonMissingError":
|
|
488
|
+
cmd.log((_q = resp.data) === null || _q === void 0 ? void 0 : _q.createRequest.message);
|
|
489
|
+
break;
|
|
490
|
+
case "RequestFieldValueMissingError":
|
|
491
|
+
cmd.log((_r = resp.data) === null || _r === void 0 ? void 0 : _r.createRequest.message);
|
|
492
|
+
break;
|
|
493
|
+
case "LinkedGroupNotRequestableError":
|
|
494
|
+
cmd.log((_s = resp.data) === null || _s === void 0 ? void 0 : _s.createRequest.message);
|
|
495
|
+
break;
|
|
496
|
+
case "RequestReasonBelowMinLengthError":
|
|
497
|
+
cmd.log((_t = resp.data) === null || _t === void 0 ? void 0 : _t.createRequest.message);
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
if (error instanceof Error || typeof error === "string") {
|
|
503
|
+
cmd.error(error);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const CATALOG_ITEM = (0, graphql_1.graphql)(`
|
|
508
|
+
query GetCatalogItem($uuid: UUID!) {
|
|
509
|
+
catalogItem(id: $uuid) {
|
|
510
|
+
__typename
|
|
511
|
+
... on Connection {
|
|
512
|
+
id
|
|
513
|
+
displayName
|
|
514
|
+
}
|
|
515
|
+
... on Resource {
|
|
516
|
+
id
|
|
517
|
+
displayName
|
|
518
|
+
connection {
|
|
519
|
+
id
|
|
520
|
+
displayName
|
|
521
|
+
}
|
|
522
|
+
accessLevels{
|
|
523
|
+
accessLevelName
|
|
524
|
+
accessLevelRemoteId
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
...on Group {
|
|
528
|
+
id
|
|
529
|
+
name
|
|
530
|
+
connection {
|
|
531
|
+
id
|
|
532
|
+
displayName
|
|
533
|
+
}
|
|
534
|
+
accessLevels{
|
|
535
|
+
accessLevelName
|
|
536
|
+
accessLevelRemoteId
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
... on UserFacingError {
|
|
540
|
+
message
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
`);
|
|
545
|
+
const ASSOCIATED_ITEMS_QUERY = (0, graphql_1.graphql)(`
|
|
546
|
+
query GetAssociatedItems($resourceId: ResourceId!, $searchQuery: String) {
|
|
547
|
+
resource(input: {
|
|
548
|
+
id: $resourceId
|
|
549
|
+
}) {
|
|
550
|
+
__typename
|
|
551
|
+
... on ResourceResult {
|
|
552
|
+
__typename
|
|
553
|
+
resource {
|
|
554
|
+
associatedItems(
|
|
555
|
+
first: 200
|
|
556
|
+
filters: {
|
|
557
|
+
searchQuery: {
|
|
558
|
+
contains: $searchQuery
|
|
559
|
+
}
|
|
560
|
+
access: REQUESTABLE
|
|
561
|
+
endUserVisible: true
|
|
562
|
+
entityType: {
|
|
563
|
+
in: [GROUP, RESOURCE]
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
) {
|
|
567
|
+
edges {
|
|
568
|
+
__typename
|
|
569
|
+
... on ResourceAssociatedItemEdge {
|
|
570
|
+
alias
|
|
571
|
+
node {
|
|
572
|
+
__typename
|
|
573
|
+
id
|
|
574
|
+
name
|
|
575
|
+
... on Resource {
|
|
576
|
+
accessLevels(
|
|
577
|
+
filters: {
|
|
578
|
+
skipRemoteAccessLevels: false # azure app roles are remote
|
|
579
|
+
}
|
|
580
|
+
) {
|
|
581
|
+
__typename
|
|
582
|
+
accessLevelName
|
|
583
|
+
accessLevelRemoteId
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
... on ResourceNotFoundError {
|
|
593
|
+
message
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
`);
|
|
598
|
+
async function queryAssociatedItems(cmd, client, id, input) {
|
|
599
|
+
var _a, _b;
|
|
600
|
+
try {
|
|
601
|
+
const resp = await client.query({
|
|
602
|
+
query: ASSOCIATED_ITEMS_QUERY,
|
|
603
|
+
variables: {
|
|
604
|
+
resourceId: id || "",
|
|
605
|
+
searchQuery: input || "",
|
|
606
|
+
},
|
|
607
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
608
|
+
});
|
|
609
|
+
switch (resp.data.resource.__typename) {
|
|
610
|
+
case "ResourceResult": {
|
|
611
|
+
const associatedItems = resp.data.resource.resource.associatedItems.edges.filter((edge) => edge.__typename === "ResourceAssociatedItemEdge");
|
|
612
|
+
const initial = [];
|
|
613
|
+
for (const edge of associatedItems) {
|
|
614
|
+
initial.push(...appRolesFromEdge(edge));
|
|
615
|
+
}
|
|
616
|
+
return initial;
|
|
617
|
+
}
|
|
618
|
+
case "ResourceNotFoundError":
|
|
619
|
+
cmd.log((_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.resource) === null || _b === void 0 ? void 0 : _b.message);
|
|
620
|
+
break;
|
|
621
|
+
default:
|
|
622
|
+
cmd.error(resp.error || "Unknown error occurred.");
|
|
144
623
|
}
|
|
145
624
|
}
|
|
146
625
|
catch (error) {
|
|
@@ -150,36 +629,126 @@ async function queryRequestableAssets(cmd, client, appId, input) {
|
|
|
150
629
|
}
|
|
151
630
|
}
|
|
152
631
|
// Helper functions
|
|
632
|
+
const selectInstructions = chalk_1.default.dim("[āā] Navigate Ā· [Enter] Select Ā· Type to filter");
|
|
633
|
+
const multiSelectInstructions = chalk_1.default.dim("[āā] Navigate Ā· [Space] Select Ā· [Enter] Confirm Ā· Type to filter");
|
|
153
634
|
async function selectRequestableItems(cmd, client, requestMap) {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
635
|
+
const initial = (await queryRequestableApps(cmd, client, "")) || [];
|
|
636
|
+
const appPrompt = new AutoComplete({
|
|
637
|
+
name: "App",
|
|
638
|
+
message: "Select an app",
|
|
639
|
+
hint: selectInstructions,
|
|
640
|
+
limit: 15,
|
|
641
|
+
choices: initial,
|
|
642
|
+
async suggest(input) {
|
|
643
|
+
const filteredChoices = await queryRequestableApps(cmd, client, input || "");
|
|
644
|
+
return filteredChoices || initial;
|
|
164
645
|
},
|
|
165
|
-
|
|
646
|
+
});
|
|
647
|
+
const App = await appPrompt.run();
|
|
166
648
|
// Set the app in the requestMap and call choose assets step
|
|
167
|
-
if (!
|
|
168
|
-
requestMap
|
|
649
|
+
if (!(App.id in requestMap)) {
|
|
650
|
+
requestMap[App.id] = {
|
|
651
|
+
appId: App.id,
|
|
169
652
|
appName: App.name,
|
|
170
|
-
assets:
|
|
171
|
-
}
|
|
653
|
+
assets: {},
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (App.type === "OKTA_APP" || App.type === "AZURE_ENTERPRISE_APP") {
|
|
657
|
+
await chooseOktaAzureRoles(cmd, client, App, requestMap);
|
|
658
|
+
return;
|
|
172
659
|
}
|
|
173
660
|
await chooseAssets(cmd, client, App.id, requestMap);
|
|
174
661
|
}
|
|
662
|
+
async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
|
|
663
|
+
const associatedItems = (await queryAssociatedItems(cmd, client, app.id, "")) || [];
|
|
664
|
+
const rolePrompt = new AutoComplete({
|
|
665
|
+
name: "Roles",
|
|
666
|
+
message: `Select a role for ${app.name}:`,
|
|
667
|
+
hint: multiSelectInstructions,
|
|
668
|
+
limit: 15,
|
|
669
|
+
multiple: true,
|
|
670
|
+
async choices(input) {
|
|
671
|
+
if (!input)
|
|
672
|
+
return associatedItems;
|
|
673
|
+
const filteredChoices = await queryAssociatedItems(cmd, client, app.id, input);
|
|
674
|
+
return filteredChoices || associatedItems;
|
|
675
|
+
},
|
|
676
|
+
validate: (answer) => {
|
|
677
|
+
if (answer.length !== 1) {
|
|
678
|
+
return "Only one role is allowed to be requested for on Okta or Azure apps.";
|
|
679
|
+
}
|
|
680
|
+
return true;
|
|
681
|
+
},
|
|
682
|
+
});
|
|
683
|
+
const Roles = await rolePrompt.run();
|
|
684
|
+
const entry = requestMap[app.id];
|
|
685
|
+
for (const role of Roles) {
|
|
686
|
+
if (!(role.id in entry.assets)) {
|
|
687
|
+
entry.assets[role.id] = {
|
|
688
|
+
assetId: role.id,
|
|
689
|
+
assetName: role.name,
|
|
690
|
+
type: entityTypeFromString(role.type),
|
|
691
|
+
roles: {},
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function appRolesFromEdge(edge) {
|
|
697
|
+
var _a, _b, _c, _d;
|
|
698
|
+
switch (edge.node.__typename) {
|
|
699
|
+
case "Resource": {
|
|
700
|
+
if (edge.node.accessLevels && edge.node.accessLevels.length > 0) {
|
|
701
|
+
return edge.node.accessLevels.map((accessLevel) => ({
|
|
702
|
+
message: accessLevel.accessLevelName || "No Role (Direct access)",
|
|
703
|
+
value: {
|
|
704
|
+
id: edge.node.id + accessLevel.accessLevelRemoteId,
|
|
705
|
+
name: accessLevel.accessLevelName,
|
|
706
|
+
type: exports.DISPLAY_LABELS[graphql_2.EntityType.Resource],
|
|
707
|
+
toString: () => accessLevel.accessLevelName,
|
|
708
|
+
},
|
|
709
|
+
}));
|
|
710
|
+
}
|
|
711
|
+
return [
|
|
712
|
+
{
|
|
713
|
+
message: (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name,
|
|
714
|
+
value: {
|
|
715
|
+
id: edge.node.id,
|
|
716
|
+
name: (_b = edge.alias) !== null && _b !== void 0 ? _b : edge.node.name,
|
|
717
|
+
type: exports.DISPLAY_LABELS[graphql_2.EntityType.Resource],
|
|
718
|
+
toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
];
|
|
722
|
+
}
|
|
723
|
+
case "Group":
|
|
724
|
+
return [
|
|
725
|
+
{
|
|
726
|
+
message: `${(_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name} ${graphql_2.EntityType.Group}`,
|
|
727
|
+
value: {
|
|
728
|
+
id: edge.node.id,
|
|
729
|
+
name: (_d = edge.alias) !== null && _d !== void 0 ? _d : edge.node.name,
|
|
730
|
+
type: exports.DISPLAY_LABELS[graphql_2.EntityType.Group],
|
|
731
|
+
toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
175
737
|
async function chooseAssets(cmd, client, appId, requestMap) {
|
|
176
|
-
|
|
177
|
-
const
|
|
738
|
+
const initial = (await queryRequestableAssets(cmd, client, appId, "")) || [];
|
|
739
|
+
const assetPrompt = new AutoComplete({
|
|
178
740
|
name: "Assets",
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
741
|
+
message: "Select one or more assets:",
|
|
742
|
+
hint: multiSelectInstructions,
|
|
743
|
+
limit: 15,
|
|
744
|
+
multiple: true,
|
|
745
|
+
async choices(input) {
|
|
746
|
+
if (!input) {
|
|
747
|
+
return initial;
|
|
748
|
+
}
|
|
749
|
+
const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
|
|
750
|
+
return filteredChoices || initial;
|
|
751
|
+
},
|
|
183
752
|
validate: (answer) => {
|
|
184
753
|
if (answer.length < 1) {
|
|
185
754
|
return "You must select at least one item.";
|
|
@@ -187,88 +756,405 @@ async function chooseAssets(cmd, client, appId, requestMap) {
|
|
|
187
756
|
return true;
|
|
188
757
|
},
|
|
189
758
|
});
|
|
190
|
-
const
|
|
759
|
+
const Assets = await assetPrompt.run();
|
|
760
|
+
const entry = requestMap[appId];
|
|
191
761
|
for (const asset of Assets) {
|
|
192
762
|
if (entry === undefined) {
|
|
193
|
-
throw new Error(`
|
|
763
|
+
throw new Error(`Error formatting app ${appId} in request`);
|
|
194
764
|
}
|
|
195
|
-
if (!
|
|
196
|
-
entry.assets
|
|
765
|
+
if (!(asset.id in entry.assets)) {
|
|
766
|
+
entry.assets[asset.id] = {
|
|
767
|
+
assetId: asset.id,
|
|
197
768
|
assetName: asset.name,
|
|
198
|
-
|
|
199
|
-
|
|
769
|
+
type: asset.type,
|
|
770
|
+
roles: {},
|
|
771
|
+
};
|
|
200
772
|
}
|
|
201
|
-
await chooseRoles(appId, asset.id, requestMap);
|
|
773
|
+
await chooseRoles(cmd, client, appId, asset.id, requestMap);
|
|
202
774
|
}
|
|
203
775
|
}
|
|
204
|
-
async function chooseRoles(appId, assetId, requestMap) {
|
|
776
|
+
async function chooseRoles(cmd, client, appId, assetId, requestMap) {
|
|
205
777
|
var _a;
|
|
206
|
-
const
|
|
207
|
-
|
|
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);
|
|
778
|
+
const entry = requestMap[appId];
|
|
779
|
+
const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets[assetId];
|
|
214
780
|
if (entry === undefined || assetEntry === undefined) {
|
|
215
781
|
throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
|
|
216
782
|
}
|
|
783
|
+
const assetRoles = (_a = (await queryAssetRoles(cmd, client, assetEntry.type, assetId))) !== null && _a !== void 0 ? _a : [];
|
|
784
|
+
if (assetRoles !== undefined &&
|
|
785
|
+
(assetRoles.length === 0 ||
|
|
786
|
+
(assetRoles.length === 1 && assetRoles[0].value.name === ""))) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const rolePrompt = new AutoComplete({
|
|
790
|
+
name: "Roles",
|
|
791
|
+
message: `Select one or more roles for ${assetEntry.assetName}:`,
|
|
792
|
+
hint: multiSelectInstructions,
|
|
793
|
+
limit: 15,
|
|
794
|
+
multiple: true,
|
|
795
|
+
choices: assetRoles,
|
|
796
|
+
validate: (answer) => {
|
|
797
|
+
if (answer.length < 1) {
|
|
798
|
+
return "You must select at least one item.";
|
|
799
|
+
}
|
|
800
|
+
return true;
|
|
801
|
+
},
|
|
802
|
+
});
|
|
803
|
+
const roles = await rolePrompt.run();
|
|
804
|
+
if (!assetEntry.roles) {
|
|
805
|
+
assetEntry.roles = {};
|
|
806
|
+
}
|
|
217
807
|
for (const role of roles) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
808
|
+
assetEntry.roles[role.id] = {
|
|
809
|
+
roleId: role.id,
|
|
810
|
+
roleName: role.name,
|
|
811
|
+
};
|
|
221
812
|
}
|
|
222
813
|
}
|
|
223
814
|
async function doneSelectingAssets() {
|
|
224
815
|
const submitMessage = "ā
Yes, proceed with request";
|
|
225
816
|
const addMoreMessage = "ā No, add more items";
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
},
|
|
233
|
-
]);
|
|
817
|
+
const prompt = new Select({
|
|
818
|
+
name: "submitOrAdd",
|
|
819
|
+
message: "Is this all you want to request?",
|
|
820
|
+
choices: [submitMessage, addMoreMessage],
|
|
821
|
+
});
|
|
822
|
+
const submitOrAdd = await prompt.run();
|
|
234
823
|
return submitOrAdd === submitMessage;
|
|
235
824
|
}
|
|
236
|
-
async function
|
|
237
|
-
|
|
825
|
+
async function setRequestDefaults(cmd, client, metadata) {
|
|
826
|
+
const requestMap = metadata.requestMap;
|
|
827
|
+
const requestedResources = [];
|
|
828
|
+
const requestedGroups = [];
|
|
829
|
+
for (const appNode of Object.values(requestMap)) {
|
|
830
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
831
|
+
if (assetNode.roles !== undefined) {
|
|
832
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
833
|
+
return roleId;
|
|
834
|
+
});
|
|
835
|
+
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
836
|
+
for (const roleId of roleIds) {
|
|
837
|
+
switch (assetNode.type) {
|
|
838
|
+
case graphql_2.EntityType.Resource: {
|
|
839
|
+
requestedResources.push({
|
|
840
|
+
resourceId: assetId,
|
|
841
|
+
accessLevelRemoteId: roleId,
|
|
842
|
+
});
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
845
|
+
case graphql_2.EntityType.Group: {
|
|
846
|
+
requestedGroups.push({
|
|
847
|
+
groupId: assetId,
|
|
848
|
+
accessLevelRemoteId: roleId,
|
|
849
|
+
});
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
try {
|
|
858
|
+
const requestDefaults = await queryRequestDefaults(cmd, client, requestedResources, requestedGroups);
|
|
859
|
+
if (requestDefaults !== undefined) {
|
|
860
|
+
metadata.requestDefaults.durationOptions =
|
|
861
|
+
requestDefaults.durationOptions;
|
|
862
|
+
metadata.requestDefaults.recommendedDurationInMinutes =
|
|
863
|
+
requestDefaults.recommendedDurationInMinutes;
|
|
864
|
+
metadata.requestDefaults.defaultDurationInMinutes =
|
|
865
|
+
requestDefaults.defaultDurationInMinutes;
|
|
866
|
+
metadata.requestDefaults.maxDurationInMinutes =
|
|
867
|
+
requestDefaults.maxDurationInMinutes;
|
|
868
|
+
metadata.requestDefaults.requireSupportTicket =
|
|
869
|
+
requestDefaults.requireSupportTicket;
|
|
870
|
+
metadata.requestDefaults.reasonOptional = requestDefaults.reasonOptional;
|
|
871
|
+
metadata.requestDefaults.requesterIsAdmin =
|
|
872
|
+
requestDefaults.requesterIsAdmin;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
catch (_a) {
|
|
876
|
+
cmd.error("Error fetching request defaults.");
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async function promptForReason(metadata) {
|
|
880
|
+
const { reason } = await prompt([
|
|
238
881
|
{
|
|
239
882
|
name: "reason",
|
|
240
|
-
message: "
|
|
883
|
+
message: "Why do you need access?",
|
|
241
884
|
type: "input",
|
|
885
|
+
validate: (answer) => {
|
|
886
|
+
if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
|
|
887
|
+
return "A reason for requesting these assets is required.";
|
|
888
|
+
}
|
|
889
|
+
return true;
|
|
890
|
+
},
|
|
242
891
|
},
|
|
243
892
|
]);
|
|
893
|
+
metadata.reason = reason;
|
|
244
894
|
}
|
|
245
|
-
async function promptForExpiration() {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
895
|
+
async function promptForExpiration(metadata) {
|
|
896
|
+
var _a, _b;
|
|
897
|
+
const durations = ((_b = (_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.durationOptions) === null || _b === void 0 ? void 0 : _b.map((option) => {
|
|
898
|
+
var _a;
|
|
899
|
+
let label = option.label;
|
|
900
|
+
if (option.durationInMinutes ===
|
|
901
|
+
((_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.maxDurationInMinutes)) {
|
|
902
|
+
label = `${label} (MAX)`;
|
|
903
|
+
}
|
|
904
|
+
if (option.durationInMinutes ===
|
|
905
|
+
metadata.requestDefaults.recommendedDurationInMinutes) {
|
|
906
|
+
label = `${label} (RECOMMENDED)`;
|
|
907
|
+
}
|
|
908
|
+
return {
|
|
909
|
+
message: label,
|
|
910
|
+
value: {
|
|
911
|
+
label: label,
|
|
912
|
+
durationInMinutes: option.durationInMinutes,
|
|
913
|
+
toString: () => label,
|
|
914
|
+
},
|
|
915
|
+
};
|
|
916
|
+
})) || [];
|
|
917
|
+
// Sort durations by minutes
|
|
918
|
+
durations.sort((a, b) => a.value.durationInMinutes - b.value.durationInMinutes);
|
|
919
|
+
const expirationSelect = new AutoComplete({
|
|
920
|
+
name: "expiration",
|
|
921
|
+
message: "When should access expire?",
|
|
922
|
+
hint: "Type to filter",
|
|
923
|
+
type: "list",
|
|
924
|
+
choices: durations,
|
|
925
|
+
pageSize: 15,
|
|
926
|
+
});
|
|
927
|
+
let selected = await expirationSelect.run();
|
|
928
|
+
switch (selected.label) {
|
|
929
|
+
case "Custom": {
|
|
930
|
+
selected = await setCustomDuration(metadata);
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
case "Permanent": {
|
|
934
|
+
selected.durationInMinutes = undefined;
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
metadata.durationInMinutes = selected.durationInMinutes;
|
|
939
|
+
metadata.durationLabel = selected.label;
|
|
940
|
+
}
|
|
941
|
+
function getDurationNumbers(duration) {
|
|
942
|
+
const d = +duration.days || 0;
|
|
943
|
+
const h = +duration.hours || 0;
|
|
944
|
+
const m = +duration.minutes || 0;
|
|
945
|
+
return { d, h, m };
|
|
946
|
+
}
|
|
947
|
+
function getDurationInMinutes(duration) {
|
|
948
|
+
const { d, h, m } = getDurationNumbers(duration);
|
|
949
|
+
const minutesInDay = 1440; // 24 hours * 60 minutes
|
|
950
|
+
const minutesInHour = 60;
|
|
951
|
+
return d * minutesInDay + h * minutesInHour + m;
|
|
952
|
+
}
|
|
953
|
+
function getDHMFromMinutes(minutes) {
|
|
954
|
+
const label = [];
|
|
955
|
+
const d = Math.floor(minutes / 1440);
|
|
956
|
+
if (d > 0) {
|
|
957
|
+
label.push(`${d}d`);
|
|
958
|
+
}
|
|
959
|
+
const remainingMinutes = minutes % 1440;
|
|
960
|
+
const h = Math.floor(remainingMinutes / 60);
|
|
961
|
+
if (h > 0) {
|
|
962
|
+
label.push(`${h}h`);
|
|
963
|
+
}
|
|
964
|
+
const m = remainingMinutes % 60;
|
|
965
|
+
if (m > 0) {
|
|
966
|
+
label.push(`${m}m`);
|
|
967
|
+
}
|
|
968
|
+
return label.join(" ");
|
|
969
|
+
}
|
|
970
|
+
async function setCustomDuration(metadata) {
|
|
971
|
+
const durationForm = new Form({
|
|
972
|
+
name: "user",
|
|
973
|
+
message: "Please set a custom access duration:",
|
|
974
|
+
choices: [
|
|
975
|
+
{ name: "days", message: "Days", initial: "0" },
|
|
976
|
+
{ name: "hours", message: "Hours", initial: "0" },
|
|
977
|
+
{ name: "minutes", message: "Minutes", initial: "0" },
|
|
978
|
+
],
|
|
979
|
+
validate: (answer) => {
|
|
980
|
+
var _a, _b, _c;
|
|
981
|
+
const { d, h, m } = getDurationNumbers(answer);
|
|
982
|
+
const durationInMinutes = getDurationInMinutes(answer);
|
|
983
|
+
if (d < 0 || h < 0 || m < 0 || d + h + m === 0 || (h > 23 && m > 59)) {
|
|
984
|
+
return "Please enter a valid duration.";
|
|
985
|
+
}
|
|
986
|
+
if (((_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.maxDurationInMinutes) &&
|
|
987
|
+
durationInMinutes > ((_b = metadata.requestDefaults) === null || _b === void 0 ? void 0 : _b.maxDurationInMinutes)) {
|
|
988
|
+
const maxDHM = getDHMFromMinutes((_c = metadata.requestDefaults) === null || _c === void 0 ? void 0 : _c.maxDurationInMinutes);
|
|
989
|
+
return `The max duration for the selected assets is ${maxDHM}.`;
|
|
990
|
+
}
|
|
991
|
+
return true;
|
|
252
992
|
},
|
|
253
|
-
|
|
993
|
+
return: (answer) => {
|
|
994
|
+
return getDurationInMinutes(answer);
|
|
995
|
+
},
|
|
996
|
+
});
|
|
997
|
+
const durationResult = await durationForm.run();
|
|
998
|
+
const { d, h, m } = getDurationNumbers(durationResult);
|
|
999
|
+
const durationInMinutes = getDurationInMinutes(durationResult);
|
|
1000
|
+
const durationLabel = getDHMFromMinutes(durationInMinutes);
|
|
1001
|
+
return {
|
|
1002
|
+
durationInMinutes: durationInMinutes,
|
|
1003
|
+
label: durationLabel,
|
|
1004
|
+
};
|
|
254
1005
|
}
|
|
255
|
-
async function
|
|
1006
|
+
async function promptRequestSubmission(cmd, metadata) {
|
|
1007
|
+
(0, displays_1.displayFinalRequestSummary)(cmd, metadata);
|
|
256
1008
|
const submitMessage = "ā
Yes, submit request";
|
|
257
1009
|
const cancelMessage = "ā No, cancel request";
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
1010
|
+
const prompt = new Select({
|
|
1011
|
+
name: "submitOrCancel",
|
|
1012
|
+
message: "Is this all you want to request?",
|
|
1013
|
+
choices: [submitMessage, cancelMessage],
|
|
1014
|
+
});
|
|
1015
|
+
const submitOrCancel = await prompt.run();
|
|
1016
|
+
return submitOrCancel === submitMessage;
|
|
1017
|
+
}
|
|
1018
|
+
async function submitFinalRequest(cmd, client, metadata) {
|
|
1019
|
+
var _a, _b, _c, _d;
|
|
1020
|
+
// Build requested assets lists for the mutation
|
|
1021
|
+
const requestedResources = [];
|
|
1022
|
+
const requestedGroups = [];
|
|
1023
|
+
for (const appNode of Object.values(metadata.requestMap)) {
|
|
1024
|
+
// This extraction is different than the one in setRequestDefaults.
|
|
1025
|
+
// Both extract the requestedResources and requestedGroups,
|
|
1026
|
+
// use different formats.
|
|
1027
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
1028
|
+
if (assetNode.roles) {
|
|
1029
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
1030
|
+
return roleId;
|
|
1031
|
+
});
|
|
1032
|
+
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
1033
|
+
for (const roleId of roleIds) {
|
|
1034
|
+
switch (assetNode.type) {
|
|
1035
|
+
case graphql_2.EntityType.Resource: {
|
|
1036
|
+
requestedResources.push({
|
|
1037
|
+
resourceId: assetId,
|
|
1038
|
+
accessLevel: {
|
|
1039
|
+
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
1040
|
+
accessLevelRemoteId: roleId,
|
|
1041
|
+
},
|
|
1042
|
+
});
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
case graphql_2.EntityType.Group: {
|
|
1046
|
+
requestedGroups.push({
|
|
1047
|
+
groupId: assetId,
|
|
1048
|
+
accessLevel: {
|
|
1049
|
+
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
1050
|
+
accessLevelRemoteId: roleId,
|
|
1051
|
+
},
|
|
1052
|
+
});
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
1061
|
+
// Build link to request
|
|
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 = getRequestLink(cmd, resp.id);
|
|
1069
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
1070
|
+
}
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
function getRequestLink(cmd, id) {
|
|
1074
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
1075
|
+
return `${configData[config_1.urlKey]}/requests/sent/${id}`;
|
|
1076
|
+
}
|
|
1077
|
+
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
1078
|
+
var _a, _b;
|
|
1079
|
+
try {
|
|
1080
|
+
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
1081
|
+
for (const val of flagValue) {
|
|
1082
|
+
const delimiterIndex = val.indexOf(":");
|
|
1083
|
+
const assetId = delimiterIndex === -1 ? val : val.substring(0, delimiterIndex);
|
|
1084
|
+
const roleName = delimiterIndex === -1 ? "" : val.substring(delimiterIndex + 1);
|
|
1085
|
+
const resp = await client.query({
|
|
1086
|
+
query: CATALOG_ITEM,
|
|
1087
|
+
variables: {
|
|
1088
|
+
uuid: assetId || "",
|
|
1089
|
+
},
|
|
1090
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
1091
|
+
});
|
|
1092
|
+
switch (resp.data.catalogItem.__typename) {
|
|
1093
|
+
case "Group":
|
|
1094
|
+
case "Resource": {
|
|
1095
|
+
const item = resp.data.catalogItem;
|
|
1096
|
+
const assetName = item.__typename === "Resource" ? item.displayName : item.name;
|
|
1097
|
+
const requestableRoles = (item.accessLevels || [])
|
|
1098
|
+
// TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
|
|
1099
|
+
.map((role) => ({
|
|
1100
|
+
id: role.accessLevelRemoteId,
|
|
1101
|
+
name: role.accessLevelName,
|
|
1102
|
+
}));
|
|
1103
|
+
const appId = ((_a = item.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
1104
|
+
if (!(appId in metadata.requestMap)) {
|
|
1105
|
+
metadata.requestMap[appId] = {
|
|
1106
|
+
appName: ((_b = item.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
|
|
1107
|
+
appId: appId,
|
|
1108
|
+
assets: {},
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
const assetEntry = metadata.requestMap[appId].assets[assetId];
|
|
1112
|
+
if (!assetEntry) {
|
|
1113
|
+
metadata.requestMap[appId].assets[assetId] = {
|
|
1114
|
+
assetId: assetId,
|
|
1115
|
+
assetName: assetName,
|
|
1116
|
+
type: entityTypeFromString(item.__typename),
|
|
1117
|
+
roles: {},
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
if (requestableRoles.length > 0 &&
|
|
1121
|
+
!(requestableRoles.length === 1 && requestableRoles[0].name === "")) {
|
|
1122
|
+
const selectedRole = requestableRoles.find((role) => role.name === roleName);
|
|
1123
|
+
if (selectedRole !== undefined) {
|
|
1124
|
+
if (!metadata.requestMap[appId].assets[assetId].roles) {
|
|
1125
|
+
metadata.requestMap[appId].assets[assetId].roles = {};
|
|
1126
|
+
}
|
|
1127
|
+
metadata.requestMap[appId].assets[assetId].roles[selectedRole.id] = {
|
|
1128
|
+
roleId: selectedRole.id,
|
|
1129
|
+
roleName: selectedRole.name,
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
cmd.error(`Access level specified does not match one of ${assetName}'s defined access levels: ${requestableRoles.map((role) => `"${role.name}"`)}`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
default:
|
|
1139
|
+
cmd.error("Invalid asset id was passed in using the --id flag.");
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
catch (error) {
|
|
1144
|
+
if (error instanceof Error || typeof error === "string") {
|
|
1145
|
+
cmd.error(error);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
function bypassDuration(cmd, duration, metadata) {
|
|
1151
|
+
if (duration === 0) {
|
|
1152
|
+
metadata.durationInMinutes = undefined;
|
|
1153
|
+
return;
|
|
270
1154
|
}
|
|
271
|
-
|
|
272
|
-
|
|
1155
|
+
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
1156
|
+
if (maxDuration && duration > maxDuration) {
|
|
1157
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${maxDuration}`);
|
|
273
1158
|
}
|
|
1159
|
+
metadata.durationInMinutes = duration;
|
|
274
1160
|
}
|