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