opal-security 3.2.1 → 3.2.2
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 +28 -25
- package/lib/commands/login.js +8 -1
- package/lib/commands/request/create.d.ts +2 -0
- package/lib/commands/request/create.js +41 -20
- package/lib/commands/request/get.js +5 -57
- package/lib/commands/request/list.js +3 -60
- package/lib/graphql/gql.d.ts +35 -15
- package/lib/graphql/gql.js +9 -5
- package/lib/graphql/graphql.d.ts +286 -228
- package/lib/graphql/graphql.js +1674 -1095
- package/lib/labels.d.ts +3 -0
- package/lib/labels.js +37 -0
- package/lib/lib/request/api/index.d.ts +6 -0
- package/lib/lib/request/api/index.js +20 -0
- package/lib/lib/request/api/mutations/create-request.d.ts +8 -0
- package/lib/lib/request/api/mutations/create-request.js +159 -0
- package/lib/lib/request/api/queries/apps.d.ts +4 -0
- package/lib/lib/request/api/queries/apps.js +73 -0
- package/lib/lib/request/api/queries/assets.d.ts +6 -0
- package/lib/lib/request/api/queries/assets.js +136 -0
- package/lib/lib/request/api/queries/request-defaults.d.ts +5 -0
- package/lib/lib/request/api/queries/request-defaults.js +51 -0
- package/lib/lib/request/api/queries/requests.d.ts +4 -0
- package/lib/lib/request/api/queries/requests.js +163 -0
- package/lib/lib/request/api/queries/roles.d.ts +5 -0
- package/lib/lib/request/api/queries/roles.js +239 -0
- package/lib/{utils → lib/request}/displays.d.ts +4 -2
- package/lib/{utils → lib/request}/displays.js +41 -19
- package/lib/lib/request/prompts/apps-prompt.d.ts +4 -0
- package/lib/lib/request/prompts/apps-prompt.js +35 -0
- package/lib/lib/request/prompts/asset-prompt.d.ts +5 -0
- package/lib/lib/request/prompts/asset-prompt.js +81 -0
- package/lib/lib/request/prompts/duration-prompt.d.ts +2 -0
- package/lib/lib/request/prompts/duration-prompt.js +122 -0
- package/lib/lib/request/prompts/index.d.ts +8 -0
- package/lib/lib/request/prompts/index.js +20 -0
- package/lib/lib/request/prompts/reason-prompt.d.ts +2 -0
- package/lib/lib/request/prompts/reason-prompt.js +20 -0
- package/lib/lib/request/prompts/role-prompt.d.ts +4 -0
- package/lib/lib/request/prompts/role-prompt.js +44 -0
- package/lib/lib/request/prompts/validate-prompt.d.ts +4 -0
- package/lib/lib/request/prompts/validate-prompt.js +29 -0
- package/lib/lib/request/request-utils.d.ts +15 -0
- package/lib/lib/request/request-utils.js +467 -0
- package/lib/lib/request/types.d.ts +55 -0
- package/lib/lib/request/types.js +15 -0
- package/lib/lib/util.d.ts +1 -0
- package/lib/lib/util.js +16 -0
- package/lib/types.d.ts +19 -3
- package/lib/types.js +18 -2
- package/oclif.manifest.json +54 -38
- package/package.json +1 -1
- package/lib/lib/requests.d.ts +0 -54
- package/lib/lib/requests.js +0 -1160
- package/lib/utils/utils.d.ts +0 -1
- package/lib/utils/utils.js +0 -18
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initEmptyRequestMetadata = initEmptyRequestMetadata;
|
|
4
|
+
exports.setRequestDefaults = setRequestDefaults;
|
|
5
|
+
exports.submitFinalRequest = submitFinalRequest;
|
|
6
|
+
exports.getRequestLink = getRequestLink;
|
|
7
|
+
exports.generateRequestLink = generateRequestLink;
|
|
8
|
+
exports.bypassRequestSelection = bypassRequestSelection;
|
|
9
|
+
exports.bypassDuration = bypassDuration;
|
|
10
|
+
exports.getRequestMessageFromCode = getRequestMessageFromCode;
|
|
11
|
+
exports.duplicateRequestTemplate = duplicateRequestTemplate;
|
|
12
|
+
exports.copyBundleAssets = copyBundleAssets;
|
|
13
|
+
const chalk_1 = require("chalk");
|
|
14
|
+
const graphql_1 = require("../../graphql/graphql");
|
|
15
|
+
const labels_1 = require("../../labels");
|
|
16
|
+
const config_1 = require("../../lib/config");
|
|
17
|
+
const types_1 = require("../../types");
|
|
18
|
+
const api_1 = require("./api");
|
|
19
|
+
const requests_1 = require("./api/queries/requests");
|
|
20
|
+
const displays_1 = require("./displays");
|
|
21
|
+
const types_2 = require("./types");
|
|
22
|
+
/*
|
|
23
|
+
Init Request Metadata
|
|
24
|
+
This function initializes the request metadata with empty defaults and an empty request map.
|
|
25
|
+
*/
|
|
26
|
+
function initEmptyRequestMetadata() {
|
|
27
|
+
// Initialize with empty defaults
|
|
28
|
+
const requestDefaults = {
|
|
29
|
+
durationOptions: [],
|
|
30
|
+
recommendedDurationInMinutes: undefined,
|
|
31
|
+
defaultDurationInMinutes: undefined,
|
|
32
|
+
maxDurationInMinutes: undefined,
|
|
33
|
+
requireSupportTicket: false,
|
|
34
|
+
reasonOptional: false,
|
|
35
|
+
requesterIsAdmin: false,
|
|
36
|
+
};
|
|
37
|
+
// Initialize with empty map
|
|
38
|
+
const requestMap = {};
|
|
39
|
+
return {
|
|
40
|
+
requestMap,
|
|
41
|
+
requestDefaults,
|
|
42
|
+
durationLabel: "",
|
|
43
|
+
durationInMinutes: 0,
|
|
44
|
+
reason: "",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/*
|
|
48
|
+
After setting up the request map, this function fetches the request defaults
|
|
49
|
+
from the server based on the requested resources and groups.
|
|
50
|
+
It updates the metadata with the fetched defaults.
|
|
51
|
+
*/
|
|
52
|
+
async function setRequestDefaults(cmd, client, metadata) {
|
|
53
|
+
var _a;
|
|
54
|
+
const requestMap = metadata.requestMap;
|
|
55
|
+
const requestedResources = [];
|
|
56
|
+
const requestedGroups = [];
|
|
57
|
+
for (const appNode of Object.values(requestMap)) {
|
|
58
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
59
|
+
if (assetNode.roles !== undefined) {
|
|
60
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
61
|
+
return roleId;
|
|
62
|
+
});
|
|
63
|
+
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
64
|
+
for (const roleId of roleIds) {
|
|
65
|
+
switch (assetNode.type) {
|
|
66
|
+
case graphql_1.EntityType.Resource: {
|
|
67
|
+
requestedResources.push({
|
|
68
|
+
resourceId: assetId,
|
|
69
|
+
accessLevelRemoteId: roleId,
|
|
70
|
+
});
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case graphql_1.EntityType.Group: {
|
|
74
|
+
requestedGroups.push({
|
|
75
|
+
groupId: assetId,
|
|
76
|
+
accessLevelRemoteId: roleId,
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const requestDefaults = await (0, api_1.queryRequestDefaults)(cmd, client, requestedResources, requestedGroups);
|
|
87
|
+
if (requestDefaults !== undefined) {
|
|
88
|
+
metadata.requestDefaults.durationOptions =
|
|
89
|
+
requestDefaults.durationOptions;
|
|
90
|
+
metadata.requestDefaults.recommendedDurationInMinutes =
|
|
91
|
+
requestDefaults.recommendedDurationInMinutes;
|
|
92
|
+
metadata.requestDefaults.defaultDurationInMinutes =
|
|
93
|
+
requestDefaults.defaultDurationInMinutes;
|
|
94
|
+
metadata.requestDefaults.maxDurationInMinutes =
|
|
95
|
+
requestDefaults.maxDurationInMinutes;
|
|
96
|
+
metadata.requestDefaults.requireSupportTicket =
|
|
97
|
+
requestDefaults.requireSupportTicket;
|
|
98
|
+
metadata.requestDefaults.reasonOptional = requestDefaults.reasonOptional;
|
|
99
|
+
metadata.requestDefaults.requesterIsAdmin =
|
|
100
|
+
requestDefaults.requesterIsAdmin;
|
|
101
|
+
for (const message of requestDefaults.messages || []) {
|
|
102
|
+
let connectionName = undefined;
|
|
103
|
+
let connectionType = undefined;
|
|
104
|
+
// If the message has an entityId, retrieve the connection name and type
|
|
105
|
+
if (message.entityId) {
|
|
106
|
+
for (const appNode of Object.values(requestMap)) {
|
|
107
|
+
for (const assetNode of Object.values(appNode.assets)) {
|
|
108
|
+
if (assetNode.assetId === message.entityId) {
|
|
109
|
+
connectionName = appNode.appName;
|
|
110
|
+
connectionType = appNode.appType;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Log the request message based on the code
|
|
117
|
+
cmd.log(chalk_1.default.dim(`\n${chalk_1.default.italic(message.level)}:`), getRequestMessageFromCode(cmd, message.code, connectionName, connectionType));
|
|
118
|
+
if (message.level === "ERROR") {
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (requestDefaults.requireSupportTicket) {
|
|
123
|
+
cmd.log("\nA support ticket is required for this request. Please submit through the Opal website.");
|
|
124
|
+
const requestLink = generateRequestLink(cmd, (_a = metadata.requestDefaults.defaultDurationInMinutes) !== null && _a !== void 0 ? _a : 60);
|
|
125
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${requestLink}\n`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (_b) {
|
|
131
|
+
cmd.error("Error fetching request defaults.");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function submitFinalRequest(cmd, client, metadata) {
|
|
135
|
+
var _a, _b, _c, _d;
|
|
136
|
+
// Build requested assets lists for the mutation
|
|
137
|
+
const requestedResources = [];
|
|
138
|
+
const requestedGroups = [];
|
|
139
|
+
for (const appNode of Object.values(metadata.requestMap)) {
|
|
140
|
+
// This extraction is different than the one in setRequestDefaults.
|
|
141
|
+
// Both extract the requestedResources and requestedGroups,
|
|
142
|
+
// use different formats.
|
|
143
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
144
|
+
if (assetNode.roles) {
|
|
145
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
146
|
+
return roleId;
|
|
147
|
+
});
|
|
148
|
+
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
149
|
+
for (const roleId of roleIds) {
|
|
150
|
+
switch (assetNode.type) {
|
|
151
|
+
case graphql_1.EntityType.Resource: {
|
|
152
|
+
requestedResources.push({
|
|
153
|
+
resourceId: assetId,
|
|
154
|
+
accessLevel: {
|
|
155
|
+
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
156
|
+
accessLevelRemoteId: roleId,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case graphql_1.EntityType.Group: {
|
|
162
|
+
requestedGroups.push({
|
|
163
|
+
groupId: assetId,
|
|
164
|
+
accessLevel: {
|
|
165
|
+
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
166
|
+
accessLevelRemoteId: roleId,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const resp = await (0, api_1.createRequest)(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
177
|
+
// Build link to request
|
|
178
|
+
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
179
|
+
cmd.log("\n🎉 Your Access Request has been submitted!\n");
|
|
180
|
+
cmd.log(`${chalk_1.default.bold("ID:")} ${chalk_1.default.cyan(resp.id)}`);
|
|
181
|
+
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
182
|
+
cmd.log((0, displays_1.getStyledStatus)(resp.status));
|
|
183
|
+
}
|
|
184
|
+
const requestLink = getRequestLink(cmd, resp.id);
|
|
185
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
186
|
+
(0, displays_1.displayRequestAgain)(cmd, resp.id);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
function getRequestLink(cmd, id) {
|
|
191
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
192
|
+
return `${configData[config_1.urlKey]}/requests/sent/${id}`;
|
|
193
|
+
}
|
|
194
|
+
function generateRequestLink(cmd, defaultDurationInMinutes) {
|
|
195
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
196
|
+
// Including the duration in the URL so that the cancel button on the request page works as expected
|
|
197
|
+
return `${configData[config_1.urlKey]}/request-access?durationInMinutes=${defaultDurationInMinutes}`;
|
|
198
|
+
}
|
|
199
|
+
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
200
|
+
var _a, _b;
|
|
201
|
+
try {
|
|
202
|
+
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
203
|
+
for (const val of flagValue) {
|
|
204
|
+
const delimiterIndex = val.indexOf(":");
|
|
205
|
+
const assetId = delimiterIndex === -1 ? val : val.substring(0, delimiterIndex);
|
|
206
|
+
const roleName = delimiterIndex === -1 ? "" : val.substring(delimiterIndex + 1);
|
|
207
|
+
const resp = await (0, api_1.queryCatalogItems)(cmd, client, assetId);
|
|
208
|
+
switch (resp.data.catalogItem.__typename) {
|
|
209
|
+
case "Group":
|
|
210
|
+
case "Resource": {
|
|
211
|
+
const item = resp.data.catalogItem;
|
|
212
|
+
const assetName = item.__typename === "Resource" ? item.displayName : item.name;
|
|
213
|
+
const requestableRoles = (item.accessLevels || [])
|
|
214
|
+
// TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
|
|
215
|
+
.map((role) => ({
|
|
216
|
+
id: role.accessLevelRemoteId,
|
|
217
|
+
name: role.accessLevelName,
|
|
218
|
+
}));
|
|
219
|
+
const appId = ((_a = item.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
220
|
+
if (!(appId in metadata.requestMap)) {
|
|
221
|
+
metadata.requestMap[appId] = {
|
|
222
|
+
appName: ((_b = item.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
|
|
223
|
+
appId: appId,
|
|
224
|
+
assets: {},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const assetEntry = metadata.requestMap[appId].assets[assetId];
|
|
228
|
+
if (!assetEntry) {
|
|
229
|
+
metadata.requestMap[appId].assets[assetId] = {
|
|
230
|
+
assetId: assetId,
|
|
231
|
+
assetName: assetName,
|
|
232
|
+
type: (0, types_2.entityTypeFromString)(item.__typename),
|
|
233
|
+
roles: {},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (requestableRoles.length > 0 &&
|
|
237
|
+
!(requestableRoles.length === 1 && requestableRoles[0].name === "")) {
|
|
238
|
+
const selectedRole = requestableRoles.find((role) => role.name === roleName);
|
|
239
|
+
if (selectedRole !== undefined) {
|
|
240
|
+
if (!metadata.requestMap[appId].assets[assetId].roles) {
|
|
241
|
+
metadata.requestMap[appId].assets[assetId].roles = {};
|
|
242
|
+
}
|
|
243
|
+
metadata.requestMap[appId].assets[assetId].roles[selectedRole.id] = {
|
|
244
|
+
roleId: selectedRole.id,
|
|
245
|
+
roleName: selectedRole.name,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
cmd.error(`Access level specified does not match one of ${assetName}'s defined access levels: ${requestableRoles.map((role) => `"${role.name}"`)}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
default:
|
|
255
|
+
cmd.error("Invalid asset id was passed in using the --id flag.");
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
if (error instanceof Error || typeof error === "string") {
|
|
261
|
+
cmd.error(error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
function bypassDuration(cmd, duration, metadata) {
|
|
267
|
+
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
268
|
+
// Permanent duration is represented by 0, but should not be allowed
|
|
269
|
+
// if a max duration is set.
|
|
270
|
+
if (maxDuration && (duration > maxDuration || duration === 0)) {
|
|
271
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${(0, displays_1.formatDuration)(maxDuration)}. Please choose a shorter duration.`);
|
|
272
|
+
}
|
|
273
|
+
if (duration === 0) {
|
|
274
|
+
metadata.durationInMinutes = undefined;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
metadata.durationInMinutes = duration;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function getRequestMessageFromCode(cmd, code, connectionName, connectionType, extraParams,
|
|
281
|
+
// sourceGroup?: PropsFor<typeof GroupBindingGroupLabel>["group"];
|
|
282
|
+
sourceGroupRedirect) {
|
|
283
|
+
switch (code) {
|
|
284
|
+
case graphql_1.RequestMessageCode.RequireUserAuthToken: {
|
|
285
|
+
if (connectionType) {
|
|
286
|
+
const connectionLabel = labels_1.connectionTypeLabelByType[connectionType];
|
|
287
|
+
// This case the connection has a third party provider such as GitLab,
|
|
288
|
+
// GitHub that requires the use to create a manual step into the end
|
|
289
|
+
// system.
|
|
290
|
+
const isThirdPartyProvider = Object.values(types_1.ThirdPartyProvider).find((v) => v === connectionType);
|
|
291
|
+
if (isThirdPartyProvider) {
|
|
292
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
293
|
+
return `This item requires you to link your ${connectionLabel} account to Opal before requesting access.
|
|
294
|
+
You can link your ${connectionLabel} account at ${`${configData[config_1.urlKey]}/user/settings/identities`}`;
|
|
295
|
+
}
|
|
296
|
+
const name = connectionName !== null && connectionName !== void 0 ? connectionName : connectionLabel;
|
|
297
|
+
// This case would be hit in case the user is not provisioned onto the end system, such as AWS, GCP, Okta, etc.
|
|
298
|
+
return `This item requires you to be provisioned on ${name} before requesting access.\
|
|
299
|
+
Please contact your Opal admin to provision your user on ${name}.`;
|
|
300
|
+
}
|
|
301
|
+
// This should never happen, but just in case we forgot to pass in the third party provider or the connection type
|
|
302
|
+
return "Your user does not exist on the end system. Please contact your Opal admin to add your user to the end system.";
|
|
303
|
+
}
|
|
304
|
+
case graphql_1.RequestMessageCode.NestedGroupAccessNotAllowed:
|
|
305
|
+
return "You cannot request access to this group because you already have nested access.";
|
|
306
|
+
case graphql_1.RequestMessageCode.LinkedGroupNotRequestable:
|
|
307
|
+
return "You cannot request access to this group because it is linked to another group";
|
|
308
|
+
default:
|
|
309
|
+
return `Unknown request message code: ${code}`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function addRequestedResourcesToMetadata(requestedResources, requestMap) {
|
|
313
|
+
var _a;
|
|
314
|
+
for (const { resource, accessLevel } of requestedResources) {
|
|
315
|
+
if (!resource)
|
|
316
|
+
continue;
|
|
317
|
+
const { id: assetId, displayName: assetName, connection, connectionId, __typename, } = resource;
|
|
318
|
+
const type = (0, types_2.entityTypeFromString)(__typename);
|
|
319
|
+
const roleId = accessLevel.accessLevelRemoteId;
|
|
320
|
+
const roleName = accessLevel.accessLevelName;
|
|
321
|
+
if (!requestMap[connectionId]) {
|
|
322
|
+
requestMap[connectionId] = {
|
|
323
|
+
appId: connectionId,
|
|
324
|
+
appName: (_a = connection === null || connection === void 0 ? void 0 : connection.name) !== null && _a !== void 0 ? _a : "",
|
|
325
|
+
appType: connection === null || connection === void 0 ? void 0 : connection.connectionType,
|
|
326
|
+
assets: {},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const appAssets = requestMap[connectionId].assets;
|
|
330
|
+
if (!appAssets[assetId]) {
|
|
331
|
+
appAssets[assetId] = {
|
|
332
|
+
assetId,
|
|
333
|
+
assetName,
|
|
334
|
+
type,
|
|
335
|
+
roles: {},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (!appAssets[assetId].roles) {
|
|
339
|
+
appAssets[assetId].roles = {};
|
|
340
|
+
}
|
|
341
|
+
appAssets[assetId].roles[roleId] = { roleId, roleName };
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function addRequestedGroupsToMetadata(requestedGroups, requestMap) {
|
|
345
|
+
var _a;
|
|
346
|
+
for (const { group, accessLevel } of requestedGroups) {
|
|
347
|
+
if (!group)
|
|
348
|
+
continue;
|
|
349
|
+
const { id: assetId, name: assetName, connection, connectionId, __typename, } = group;
|
|
350
|
+
const type = (0, types_2.entityTypeFromString)(__typename);
|
|
351
|
+
const roleId = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelRemoteId;
|
|
352
|
+
const roleName = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelName;
|
|
353
|
+
if (!requestMap[connectionId]) {
|
|
354
|
+
requestMap[connectionId] = {
|
|
355
|
+
appId: connectionId,
|
|
356
|
+
appName: (_a = connection === null || connection === void 0 ? void 0 : connection.name) !== null && _a !== void 0 ? _a : "",
|
|
357
|
+
appType: connection === null || connection === void 0 ? void 0 : connection.connectionType,
|
|
358
|
+
assets: {},
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const appAssets = requestMap[connectionId].assets;
|
|
362
|
+
if (!appAssets[assetId]) {
|
|
363
|
+
appAssets[assetId] = {
|
|
364
|
+
assetId,
|
|
365
|
+
assetName,
|
|
366
|
+
type,
|
|
367
|
+
roles: {},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (!appAssets[assetId].roles) {
|
|
371
|
+
appAssets[assetId].roles = {};
|
|
372
|
+
}
|
|
373
|
+
if (roleId) {
|
|
374
|
+
appAssets[assetId].roles[roleId] = { roleId, roleName: roleName !== null && roleName !== void 0 ? roleName : "" };
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async function convertRequestToMetadata(cmd, request, metadata) {
|
|
379
|
+
const { status, durationInMinutes, reason, requestedResources, requestedGroups, } = request;
|
|
380
|
+
if (status === graphql_1.RequestStatus.Pending) {
|
|
381
|
+
cmd.log("⏳ Request is still in progress");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (durationInMinutes === undefined) {
|
|
385
|
+
cmd.error("Duration in minutes is required but was not provided.");
|
|
386
|
+
}
|
|
387
|
+
// if durationInMinutes is null, the request had a permanent duration
|
|
388
|
+
metadata.durationInMinutes = durationInMinutes !== null && durationInMinutes !== void 0 ? durationInMinutes : 0;
|
|
389
|
+
metadata.reason = reason;
|
|
390
|
+
addRequestedResourcesToMetadata(requestedResources, metadata.requestMap);
|
|
391
|
+
addRequestedGroupsToMetadata(requestedGroups, metadata.requestMap);
|
|
392
|
+
}
|
|
393
|
+
async function duplicateRequestTemplate(cmd, client, requestId, metadata) {
|
|
394
|
+
cmd.log("Loading request template from ID: ", requestId);
|
|
395
|
+
const resp = await (0, api_1.queryRequest)(client, requestId);
|
|
396
|
+
// no fall through doesn't consider process.exit();
|
|
397
|
+
let x;
|
|
398
|
+
switch (resp.data.request.__typename) {
|
|
399
|
+
case "RequestResult": {
|
|
400
|
+
if (resp.data.request.request.status ===
|
|
401
|
+
graphql_1.RequestStatus.Pending) {
|
|
402
|
+
cmd.error("⏳ Cannot duplicate a request that is still in progress");
|
|
403
|
+
}
|
|
404
|
+
cmd.log("Creating new request with same configuration...");
|
|
405
|
+
convertRequestToMetadata(cmd, resp.data.request.request, metadata);
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case "RequestNotFoundError":
|
|
409
|
+
x = cmd.error(`🚫 Request with id ${requestId} was not found`);
|
|
410
|
+
break;
|
|
411
|
+
default:
|
|
412
|
+
cmd.error("🚫 Error retrieving request data");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async function convertBundleToMetadata(cmd, bundle, requestMap) {
|
|
416
|
+
const { name, id, items } = bundle;
|
|
417
|
+
const requestedResources = [];
|
|
418
|
+
const requestedGroups = [];
|
|
419
|
+
let notRequestableCount = 0;
|
|
420
|
+
for (const item of items.edges) {
|
|
421
|
+
if (item.node.isRequestable) {
|
|
422
|
+
if (item.node.__typename === "Resource") {
|
|
423
|
+
requestedResources.push({
|
|
424
|
+
resource: item.node,
|
|
425
|
+
accessLevel: item.accessLevel,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
else if (item.node.__typename === "Group") {
|
|
429
|
+
requestedGroups.push({
|
|
430
|
+
group: item.node,
|
|
431
|
+
accessLevel: item.accessLevel,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
notRequestableCount++;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
addRequestedResourcesToMetadata(requestedResources, requestMap);
|
|
440
|
+
addRequestedGroupsToMetadata(requestedGroups, requestMap);
|
|
441
|
+
const requestableCount = requestedResources.length + requestedGroups.length;
|
|
442
|
+
if (requestableCount === 0) {
|
|
443
|
+
cmd.error(`No requestable items in the bundle: ${name}`);
|
|
444
|
+
}
|
|
445
|
+
if (notRequestableCount > 0) {
|
|
446
|
+
cmd.log(`You do not have permission to request access to ${notRequestableCount} items in this bundle`);
|
|
447
|
+
}
|
|
448
|
+
cmd.log("Added all requestable items in the bundle");
|
|
449
|
+
}
|
|
450
|
+
async function copyBundleAssets(cmd, client, bundleId, requestMap) {
|
|
451
|
+
cmd.log("Loading assets from bundle: ", bundleId);
|
|
452
|
+
const resp = await (0, requests_1.queryBundle)(client, bundleId);
|
|
453
|
+
// no fall through doesn't consider process.exit();
|
|
454
|
+
let x;
|
|
455
|
+
switch (resp.data.bundle.__typename) {
|
|
456
|
+
case "BundleResult": {
|
|
457
|
+
cmd.log("Creating new request with assets in the bundle...");
|
|
458
|
+
convertBundleToMetadata(cmd, resp.data.bundle.bundle, requestMap);
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
case "BundleNotFoundError":
|
|
462
|
+
x = cmd.error(`🚫 Bundle with id ${bundleId} was not found`);
|
|
463
|
+
break;
|
|
464
|
+
default:
|
|
465
|
+
cmd.error("🚫 Error retrieving bundle data");
|
|
466
|
+
}
|
|
467
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type ConnectionType, EntityType, type RequestMessageCode, type RequestMessageLevel } from "../../graphql/graphql";
|
|
2
|
+
type AppNode = {
|
|
3
|
+
appId: string;
|
|
4
|
+
appName: string;
|
|
5
|
+
appType?: ConnectionType;
|
|
6
|
+
assets: Record<string, AssetNode>;
|
|
7
|
+
};
|
|
8
|
+
type AssetNode = {
|
|
9
|
+
assetId: string;
|
|
10
|
+
assetName: string;
|
|
11
|
+
type: EntityType;
|
|
12
|
+
roles?: Record<string, RoleNode>;
|
|
13
|
+
};
|
|
14
|
+
type RoleNode = {
|
|
15
|
+
roleId: string;
|
|
16
|
+
roleName: string;
|
|
17
|
+
};
|
|
18
|
+
export type RequestMap = Record<string, AppNode>;
|
|
19
|
+
export declare function entityTypeFromString(str?: string): EntityType;
|
|
20
|
+
export type DurationOption = {
|
|
21
|
+
durationInMinutes: number;
|
|
22
|
+
label: string;
|
|
23
|
+
};
|
|
24
|
+
export type RequestDefaults = {
|
|
25
|
+
durationOptions?: DurationOption[];
|
|
26
|
+
recommendedDurationInMinutes?: number | null;
|
|
27
|
+
defaultDurationInMinutes?: number;
|
|
28
|
+
maxDurationInMinutes?: number | null;
|
|
29
|
+
requireSupportTicket?: boolean;
|
|
30
|
+
reasonOptional?: boolean;
|
|
31
|
+
requesterIsAdmin?: boolean;
|
|
32
|
+
messages?: {
|
|
33
|
+
entityId?: string | null;
|
|
34
|
+
level: RequestMessageLevel;
|
|
35
|
+
code: RequestMessageCode;
|
|
36
|
+
}[];
|
|
37
|
+
};
|
|
38
|
+
export type RequestMetadata = {
|
|
39
|
+
requestMap: RequestMap;
|
|
40
|
+
requestDefaults: RequestDefaults;
|
|
41
|
+
durationLabel: string;
|
|
42
|
+
durationInMinutes?: number;
|
|
43
|
+
reason: string;
|
|
44
|
+
};
|
|
45
|
+
export type EntityValue = {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
type?: string;
|
|
49
|
+
toString?: () => string;
|
|
50
|
+
};
|
|
51
|
+
export type PromptChoice = {
|
|
52
|
+
message: string;
|
|
53
|
+
value: EntityValue;
|
|
54
|
+
};
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.entityTypeFromString = entityTypeFromString;
|
|
4
|
+
const graphql_1 = require("../../graphql/graphql");
|
|
5
|
+
function entityTypeFromString(str) {
|
|
6
|
+
const capStr = str === null || str === void 0 ? void 0 : str.toLocaleUpperCase();
|
|
7
|
+
if (capStr === "RESOURCE") {
|
|
8
|
+
return graphql_1.EntityType.Resource;
|
|
9
|
+
}
|
|
10
|
+
if (capStr === "GROUP") {
|
|
11
|
+
return graphql_1.EntityType.Group;
|
|
12
|
+
}
|
|
13
|
+
// if type unknown, default to resource
|
|
14
|
+
return graphql_1.EntityType.Resource;
|
|
15
|
+
}
|
package/lib/lib/util.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function restrictToDev(): void;
|
|
1
2
|
export declare const sleep: (ms: number) => Promise<unknown>;
|
|
2
3
|
export declare const copyToClipboard: (content: string) => import("child_process").ChildProcess;
|
|
3
4
|
export declare const displayContent: (content: string) => import("child_process").ChildProcess;
|
package/lib/lib/util.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.displayContent = exports.copyToClipboard = exports.sleep = void 0;
|
|
4
|
+
exports.restrictToDev = restrictToDev;
|
|
4
5
|
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
/*
|
|
7
|
+
Use restrictToDev function in the run functions of commands still in development-
|
|
8
|
+
|
|
9
|
+
static hidden = true; # Use this in tandem with restrictToDev function to hide from help menu.
|
|
10
|
+
async run() {
|
|
11
|
+
restrictToDev();
|
|
12
|
+
...
|
|
13
|
+
}
|
|
14
|
+
*/
|
|
15
|
+
function restrictToDev() {
|
|
16
|
+
if (process.env.NODE_ENV !== "development") {
|
|
17
|
+
console.error("Command still under development. Please look out for product announcements for official release.");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
5
21
|
const sleep = (ms) => {
|
|
6
22
|
return new Promise((resolve) => {
|
|
7
23
|
setTimeout(resolve, ms);
|
package/lib/types.d.ts
CHANGED
|
@@ -1047,20 +1047,36 @@ export type ConnectionResult = {
|
|
|
1047
1047
|
};
|
|
1048
1048
|
export declare enum ConnectionType {
|
|
1049
1049
|
ActiveDirectory = "ACTIVE_DIRECTORY",
|
|
1050
|
+
/**
|
|
1051
|
+
* Deprecated. Legacy integration no longer offered - use AWS_SSO instead.
|
|
1052
|
+
* @deprecated Legacy integration no longer offered - use AWS_SSO instead.
|
|
1053
|
+
*/
|
|
1050
1054
|
Aws = "AWS",
|
|
1055
|
+
AwsSso = "AWS_SSO",
|
|
1056
|
+
AzureAd = "AZURE_AD",
|
|
1051
1057
|
Custom = "CUSTOM",
|
|
1058
|
+
CustomConnector = "CUSTOM_CONNECTOR",
|
|
1059
|
+
Databricks = "DATABRICKS",
|
|
1052
1060
|
Duo = "DUO",
|
|
1053
|
-
GitHub = "GIT_HUB",
|
|
1054
1061
|
Gcp = "GCP",
|
|
1062
|
+
GitHub = "GIT_HUB",
|
|
1063
|
+
GitLab = "GIT_LAB",
|
|
1055
1064
|
GoogleGroups = "GOOGLE_GROUPS",
|
|
1056
1065
|
GoogleWorkspace = "GOOGLE_WORKSPACE",
|
|
1057
|
-
Salesforce = "SALESFORCE",
|
|
1058
1066
|
Ldap = "LDAP",
|
|
1067
|
+
Mariadb = "MARIADB",
|
|
1059
1068
|
Mongo = "MONGO",
|
|
1060
1069
|
MongoAtlas = "MONGO_ATLAS",
|
|
1070
|
+
Mysql = "MYSQL",
|
|
1061
1071
|
OktaDirectory = "OKTA_DIRECTORY",
|
|
1062
1072
|
Opal = "OPAL",
|
|
1063
|
-
Pagerduty = "PAGERDUTY"
|
|
1073
|
+
Pagerduty = "PAGERDUTY",
|
|
1074
|
+
Postgres = "POSTGRES",
|
|
1075
|
+
Salesforce = "SALESFORCE",
|
|
1076
|
+
Snowflake = "SNOWFLAKE",
|
|
1077
|
+
Tailscale = "TAILSCALE",
|
|
1078
|
+
Teleport = "TELEPORT",
|
|
1079
|
+
Workday = "WORKDAY"
|
|
1064
1080
|
}
|
|
1065
1081
|
export type ConnectionUrlInvalidError = Error & {
|
|
1066
1082
|
__typename?: "ConnectionURLInvalidError";
|
package/lib/types.js
CHANGED
|
@@ -96,20 +96,36 @@ var AuthType;
|
|
|
96
96
|
var ConnectionType;
|
|
97
97
|
(function (ConnectionType) {
|
|
98
98
|
ConnectionType["ActiveDirectory"] = "ACTIVE_DIRECTORY";
|
|
99
|
+
/**
|
|
100
|
+
* Deprecated. Legacy integration no longer offered - use AWS_SSO instead.
|
|
101
|
+
* @deprecated Legacy integration no longer offered - use AWS_SSO instead.
|
|
102
|
+
*/
|
|
99
103
|
ConnectionType["Aws"] = "AWS";
|
|
104
|
+
ConnectionType["AwsSso"] = "AWS_SSO";
|
|
105
|
+
ConnectionType["AzureAd"] = "AZURE_AD";
|
|
100
106
|
ConnectionType["Custom"] = "CUSTOM";
|
|
107
|
+
ConnectionType["CustomConnector"] = "CUSTOM_CONNECTOR";
|
|
108
|
+
ConnectionType["Databricks"] = "DATABRICKS";
|
|
101
109
|
ConnectionType["Duo"] = "DUO";
|
|
102
|
-
ConnectionType["GitHub"] = "GIT_HUB";
|
|
103
110
|
ConnectionType["Gcp"] = "GCP";
|
|
111
|
+
ConnectionType["GitHub"] = "GIT_HUB";
|
|
112
|
+
ConnectionType["GitLab"] = "GIT_LAB";
|
|
104
113
|
ConnectionType["GoogleGroups"] = "GOOGLE_GROUPS";
|
|
105
114
|
ConnectionType["GoogleWorkspace"] = "GOOGLE_WORKSPACE";
|
|
106
|
-
ConnectionType["Salesforce"] = "SALESFORCE";
|
|
107
115
|
ConnectionType["Ldap"] = "LDAP";
|
|
116
|
+
ConnectionType["Mariadb"] = "MARIADB";
|
|
108
117
|
ConnectionType["Mongo"] = "MONGO";
|
|
109
118
|
ConnectionType["MongoAtlas"] = "MONGO_ATLAS";
|
|
119
|
+
ConnectionType["Mysql"] = "MYSQL";
|
|
110
120
|
ConnectionType["OktaDirectory"] = "OKTA_DIRECTORY";
|
|
111
121
|
ConnectionType["Opal"] = "OPAL";
|
|
112
122
|
ConnectionType["Pagerduty"] = "PAGERDUTY";
|
|
123
|
+
ConnectionType["Postgres"] = "POSTGRES";
|
|
124
|
+
ConnectionType["Salesforce"] = "SALESFORCE";
|
|
125
|
+
ConnectionType["Snowflake"] = "SNOWFLAKE";
|
|
126
|
+
ConnectionType["Tailscale"] = "TAILSCALE";
|
|
127
|
+
ConnectionType["Teleport"] = "TELEPORT";
|
|
128
|
+
ConnectionType["Workday"] = "WORKDAY";
|
|
113
129
|
})(ConnectionType || (exports.ConnectionType = ConnectionType = {}));
|
|
114
130
|
var EntityType;
|
|
115
131
|
(function (EntityType) {
|