opal-security 3.2.1 → 3.2.3
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 +18 -11
- package/lib/commands/request/create.d.ts +2 -0
- package/lib/commands/request/create.js +41 -20
- package/lib/commands/request/get.js +7 -58
- 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 +1138 -383
- package/lib/graphql/graphql.js +1780 -1104
- package/lib/handler.d.ts +6 -6
- package/lib/handler.js +1 -1
- package/lib/labels.d.ts +3 -0
- package/lib/labels.js +40 -0
- package/lib/lib/apollo.d.ts +3 -3
- package/lib/lib/apollo.js +24 -47
- 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 +52 -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 +7 -5
- package/lib/{utils → lib/request}/displays.js +52 -30
- 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 +125 -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 +14 -0
- package/lib/lib/request/request-utils.js +468 -0
- package/lib/lib/request/types.d.ts +55 -0
- package/lib/lib/request/types.js +15 -0
- package/lib/lib/resources.d.ts +1 -1
- package/lib/lib/sessions.d.ts +1 -1
- package/lib/lib/sessions.js +3 -2
- 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 +4 -3
- 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,468 @@
|
|
|
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 api_1 = require("./api");
|
|
18
|
+
const requests_1 = require("./api/queries/requests");
|
|
19
|
+
const displays_1 = require("./displays");
|
|
20
|
+
const types_1 = require("./types");
|
|
21
|
+
/*
|
|
22
|
+
Init Request Metadata
|
|
23
|
+
This function initializes the request metadata with empty defaults and an empty request map.
|
|
24
|
+
*/
|
|
25
|
+
function initEmptyRequestMetadata() {
|
|
26
|
+
// Initialize with empty defaults
|
|
27
|
+
const requestDefaults = {
|
|
28
|
+
durationOptions: [],
|
|
29
|
+
recommendedDurationInMinutes: undefined,
|
|
30
|
+
defaultDurationInMinutes: undefined,
|
|
31
|
+
maxDurationInMinutes: undefined,
|
|
32
|
+
requireSupportTicket: false,
|
|
33
|
+
reasonOptional: false,
|
|
34
|
+
requesterIsAdmin: false,
|
|
35
|
+
};
|
|
36
|
+
// Initialize with empty map
|
|
37
|
+
const requestMap = {};
|
|
38
|
+
return {
|
|
39
|
+
requestMap,
|
|
40
|
+
requestDefaults,
|
|
41
|
+
durationLabel: "",
|
|
42
|
+
durationInMinutes: 0,
|
|
43
|
+
reason: "",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/*
|
|
47
|
+
After setting up the request map, this function fetches the request defaults
|
|
48
|
+
from the server based on the requested resources and groups.
|
|
49
|
+
It updates the metadata with the fetched defaults.
|
|
50
|
+
*/
|
|
51
|
+
async function setRequestDefaults(cmd, client, metadata) {
|
|
52
|
+
var _a;
|
|
53
|
+
const requestMap = metadata.requestMap;
|
|
54
|
+
const requestedResources = [];
|
|
55
|
+
const requestedGroups = [];
|
|
56
|
+
for (const appNode of Object.values(requestMap)) {
|
|
57
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
58
|
+
if (assetNode.roles !== undefined) {
|
|
59
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
60
|
+
return roleId;
|
|
61
|
+
});
|
|
62
|
+
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
63
|
+
for (const roleId of roleIds) {
|
|
64
|
+
switch (assetNode.type) {
|
|
65
|
+
case graphql_1.EntityType.Resource: {
|
|
66
|
+
requestedResources.push({
|
|
67
|
+
resourceId: assetId,
|
|
68
|
+
accessLevelRemoteId: roleId,
|
|
69
|
+
});
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case graphql_1.EntityType.Group: {
|
|
73
|
+
requestedGroups.push({
|
|
74
|
+
groupId: assetId,
|
|
75
|
+
accessLevelRemoteId: roleId,
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const requestDefaults = await (0, api_1.queryRequestDefaults)(cmd, client, requestedResources, requestedGroups);
|
|
86
|
+
if (requestDefaults !== undefined) {
|
|
87
|
+
metadata.requestDefaults.durationOptions =
|
|
88
|
+
requestDefaults.durationOptions;
|
|
89
|
+
metadata.requestDefaults.recommendedDurationInMinutes =
|
|
90
|
+
requestDefaults.recommendedDurationInMinutes;
|
|
91
|
+
metadata.requestDefaults.defaultDurationInMinutes =
|
|
92
|
+
requestDefaults.defaultDurationInMinutes;
|
|
93
|
+
metadata.requestDefaults.maxDurationInMinutes =
|
|
94
|
+
requestDefaults.maxDurationInMinutes;
|
|
95
|
+
metadata.requestDefaults.requireSupportTicket =
|
|
96
|
+
requestDefaults.requireSupportTicket;
|
|
97
|
+
metadata.requestDefaults.reasonOptional = requestDefaults.reasonOptional;
|
|
98
|
+
metadata.requestDefaults.requesterIsAdmin =
|
|
99
|
+
requestDefaults.requesterIsAdmin;
|
|
100
|
+
for (const message of requestDefaults.messages || []) {
|
|
101
|
+
let connectionName = undefined;
|
|
102
|
+
let connectionType = undefined;
|
|
103
|
+
// If the message has an entityId, retrieve the connection name and type
|
|
104
|
+
if (message.entityId) {
|
|
105
|
+
for (const appNode of Object.values(requestMap)) {
|
|
106
|
+
for (const assetNode of Object.values(appNode.assets)) {
|
|
107
|
+
if (assetNode.assetId === message.entityId) {
|
|
108
|
+
connectionName = appNode.appName;
|
|
109
|
+
connectionType = appNode.appType;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Log the request message based on the code
|
|
116
|
+
cmd.log(chalk_1.default.dim(`\n${chalk_1.default.italic(message.level)}:`), getRequestMessageFromCode(cmd, message.code, connectionName, connectionType));
|
|
117
|
+
if (message.level === "ERROR") {
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (requestDefaults.requireSupportTicket) {
|
|
122
|
+
cmd.log("\nA support ticket is required for this request. Please submit through the Opal website.");
|
|
123
|
+
const requestLink = generateRequestLink(cmd, (_a = metadata.requestDefaults.defaultDurationInMinutes) !== null && _a !== void 0 ? _a : 60);
|
|
124
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${requestLink}\n`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (_b) {
|
|
130
|
+
cmd.error("Error fetching request defaults.");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function submitFinalRequest(cmd, client, metadata) {
|
|
134
|
+
var _a, _b, _c, _d;
|
|
135
|
+
// Build requested assets lists for the mutation
|
|
136
|
+
const requestedResources = [];
|
|
137
|
+
const requestedGroups = [];
|
|
138
|
+
for (const appNode of Object.values(metadata.requestMap)) {
|
|
139
|
+
// This extraction is different than the one in setRequestDefaults.
|
|
140
|
+
// Both extract the requestedResources and requestedGroups,
|
|
141
|
+
// use different formats.
|
|
142
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
143
|
+
if (assetNode.roles) {
|
|
144
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
145
|
+
return roleId;
|
|
146
|
+
});
|
|
147
|
+
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
148
|
+
for (const roleId of roleIds) {
|
|
149
|
+
switch (assetNode.type) {
|
|
150
|
+
case graphql_1.EntityType.Resource: {
|
|
151
|
+
requestedResources.push({
|
|
152
|
+
resourceId: assetId,
|
|
153
|
+
accessLevel: {
|
|
154
|
+
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
155
|
+
accessLevelRemoteId: roleId,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case graphql_1.EntityType.Group: {
|
|
161
|
+
requestedGroups.push({
|
|
162
|
+
groupId: assetId,
|
|
163
|
+
accessLevel: {
|
|
164
|
+
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
165
|
+
accessLevelRemoteId: roleId,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const resp = await (0, api_1.createRequest)(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
176
|
+
// Build link to request
|
|
177
|
+
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
178
|
+
cmd.log("\n🎉 Your Access Request has been submitted!\n");
|
|
179
|
+
cmd.log(`${chalk_1.default.bold("ID:")} ${chalk_1.default.cyan(resp.id)}`);
|
|
180
|
+
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
181
|
+
cmd.log((0, displays_1.getStyledStatus)(resp.status));
|
|
182
|
+
}
|
|
183
|
+
const requestLink = getRequestLink(cmd, resp.id);
|
|
184
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
185
|
+
(0, displays_1.displayRequestAgain)(cmd, resp.id);
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
function getRequestLink(cmd, id) {
|
|
190
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
191
|
+
return `${configData[config_1.urlKey]}/requests/sent/${id}`;
|
|
192
|
+
}
|
|
193
|
+
function generateRequestLink(cmd, defaultDurationInMinutes) {
|
|
194
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
195
|
+
// Including the duration in the URL so that the cancel button on the request page works as expected
|
|
196
|
+
return `${configData[config_1.urlKey]}/request-access?durationInMinutes=${defaultDurationInMinutes}`;
|
|
197
|
+
}
|
|
198
|
+
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
199
|
+
var _a, _b, _c, _d;
|
|
200
|
+
try {
|
|
201
|
+
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
202
|
+
for (const val of flagValue) {
|
|
203
|
+
const delimiterIndex = val.indexOf(":");
|
|
204
|
+
const assetId = delimiterIndex === -1 ? val : val.substring(0, delimiterIndex);
|
|
205
|
+
const roleName = delimiterIndex === -1 ? "" : val.substring(delimiterIndex + 1);
|
|
206
|
+
const resp = await (0, api_1.queryCatalogItems)(cmd, client, assetId);
|
|
207
|
+
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.catalogItem.__typename) {
|
|
208
|
+
case "Group":
|
|
209
|
+
case "Resource": {
|
|
210
|
+
const item = (_b = resp.data) === null || _b === void 0 ? void 0 : _b.catalogItem;
|
|
211
|
+
const assetName = item.__typename === "Resource" ? item.displayName : item.name;
|
|
212
|
+
const requestableRoles = (item.accessLevels || [])
|
|
213
|
+
// TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
|
|
214
|
+
.map((role) => ({
|
|
215
|
+
id: role.accessLevelRemoteId,
|
|
216
|
+
name: role.accessLevelName,
|
|
217
|
+
}));
|
|
218
|
+
const appId = ((_c = item.connection) === null || _c === void 0 ? void 0 : _c.id) || "";
|
|
219
|
+
if (!(appId in metadata.requestMap)) {
|
|
220
|
+
metadata.requestMap[appId] = {
|
|
221
|
+
appName: ((_d = item.connection) === null || _d === void 0 ? void 0 : _d.displayName) || "",
|
|
222
|
+
appId: appId,
|
|
223
|
+
assets: {},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const assetEntry = metadata.requestMap[appId].assets[assetId];
|
|
227
|
+
if (!assetEntry) {
|
|
228
|
+
metadata.requestMap[appId].assets[assetId] = {
|
|
229
|
+
assetId: assetId,
|
|
230
|
+
assetName: assetName,
|
|
231
|
+
type: (0, types_1.entityTypeFromString)(item.__typename),
|
|
232
|
+
roles: {},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (requestableRoles.length > 0 &&
|
|
236
|
+
!(requestableRoles.length === 1 && requestableRoles[0].name === "")) {
|
|
237
|
+
const selectedRole = requestableRoles.find((role) => role.name === roleName);
|
|
238
|
+
if (selectedRole !== undefined) {
|
|
239
|
+
if (!metadata.requestMap[appId].assets[assetId].roles) {
|
|
240
|
+
metadata.requestMap[appId].assets[assetId].roles = {};
|
|
241
|
+
}
|
|
242
|
+
metadata.requestMap[appId].assets[assetId].roles[selectedRole.id] = {
|
|
243
|
+
roleId: selectedRole.id,
|
|
244
|
+
roleName: selectedRole.name,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
cmd.error(`Access level specified does not match one of ${assetName}'s defined access levels: ${requestableRoles.map((role) => `"${role.name}"`)}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
default:
|
|
254
|
+
cmd.error("Invalid asset id was passed in using the --id flag.");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
if (error instanceof Error || typeof error === "string") {
|
|
260
|
+
cmd.error(error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
function bypassDuration(cmd, duration, metadata) {
|
|
266
|
+
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
267
|
+
// Permanent duration is represented by 0, but should not be allowed
|
|
268
|
+
// if a max duration is set.
|
|
269
|
+
if (maxDuration && (duration > maxDuration || duration === 0)) {
|
|
270
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${(0, displays_1.formatDuration)(maxDuration)}. Please choose a shorter duration.`);
|
|
271
|
+
}
|
|
272
|
+
if (duration === 0) {
|
|
273
|
+
metadata.durationInMinutes = undefined;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
metadata.durationInMinutes = duration;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function getRequestMessageFromCode(cmd, code, connectionName, connectionType, extraParams,
|
|
280
|
+
// sourceGroup?: PropsFor<typeof GroupBindingGroupLabel>["group"];
|
|
281
|
+
sourceGroupRedirect) {
|
|
282
|
+
switch (code) {
|
|
283
|
+
case graphql_1.RequestMessageCode.RequireUserAuthToken: {
|
|
284
|
+
if (connectionType) {
|
|
285
|
+
const connectionLabel = labels_1.connectionTypeLabelByType[connectionType];
|
|
286
|
+
// This case the connection has a third party provider such as GitLab,
|
|
287
|
+
// GitHub that requires the use to create a manual step into the end
|
|
288
|
+
// system.
|
|
289
|
+
const isThirdPartyProvider = Object.values(graphql_1.ThirdPartyProvider).find((v) => v === connectionType);
|
|
290
|
+
if (isThirdPartyProvider) {
|
|
291
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
292
|
+
return `This item requires you to link your ${connectionLabel} account to Opal before requesting access.
|
|
293
|
+
You can link your ${connectionLabel} account at ${`${configData[config_1.urlKey]}/user/settings/identities`}`;
|
|
294
|
+
}
|
|
295
|
+
const name = connectionName !== null && connectionName !== void 0 ? connectionName : connectionLabel;
|
|
296
|
+
// This case would be hit in case the user is not provisioned onto the end system, such as AWS, GCP, Okta, etc.
|
|
297
|
+
return `This item requires you to be provisioned on ${name} before requesting access.\
|
|
298
|
+
Please contact your Opal admin to provision your user on ${name}.`;
|
|
299
|
+
}
|
|
300
|
+
// This should never happen, but just in case we forgot to pass in the third party provider or the connection type
|
|
301
|
+
return "Your user does not exist on the end system. Please contact your Opal admin to add your user to the end system.";
|
|
302
|
+
}
|
|
303
|
+
case graphql_1.RequestMessageCode.NestedGroupAccessNotAllowed:
|
|
304
|
+
return "You cannot request access to this group because you already have nested access.";
|
|
305
|
+
case graphql_1.RequestMessageCode.LinkedGroupNotRequestable:
|
|
306
|
+
return "You cannot request access to this group because it is linked to another group";
|
|
307
|
+
default:
|
|
308
|
+
return `Unknown request message code: ${code}`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function addRequestedResourcesToMetadata(requestedResources, requestMap) {
|
|
312
|
+
var _a;
|
|
313
|
+
for (const { resource, accessLevel } of requestedResources) {
|
|
314
|
+
if (!resource)
|
|
315
|
+
continue;
|
|
316
|
+
const { id: assetId, displayName: assetName, connection, connectionId, __typename, } = resource;
|
|
317
|
+
const type = (0, types_1.entityTypeFromString)(__typename);
|
|
318
|
+
const roleId = accessLevel.accessLevelRemoteId;
|
|
319
|
+
const roleName = accessLevel.accessLevelName;
|
|
320
|
+
if (!requestMap[connectionId]) {
|
|
321
|
+
requestMap[connectionId] = {
|
|
322
|
+
appId: connectionId,
|
|
323
|
+
appName: (_a = connection === null || connection === void 0 ? void 0 : connection.name) !== null && _a !== void 0 ? _a : "",
|
|
324
|
+
appType: connection === null || connection === void 0 ? void 0 : connection.connectionType,
|
|
325
|
+
assets: {},
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const appAssets = requestMap[connectionId].assets;
|
|
329
|
+
if (!appAssets[assetId]) {
|
|
330
|
+
appAssets[assetId] = {
|
|
331
|
+
assetId,
|
|
332
|
+
assetName,
|
|
333
|
+
type,
|
|
334
|
+
roles: {},
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (!appAssets[assetId].roles) {
|
|
338
|
+
appAssets[assetId].roles = {};
|
|
339
|
+
}
|
|
340
|
+
appAssets[assetId].roles[roleId] = { roleId, roleName };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function addRequestedGroupsToMetadata(requestedGroups, requestMap) {
|
|
344
|
+
var _a;
|
|
345
|
+
for (const { group, accessLevel } of requestedGroups) {
|
|
346
|
+
if (!group)
|
|
347
|
+
continue;
|
|
348
|
+
const { id: assetId, name: assetName, connection, connectionId, __typename, } = group;
|
|
349
|
+
const type = (0, types_1.entityTypeFromString)(__typename);
|
|
350
|
+
const roleId = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelRemoteId;
|
|
351
|
+
const roleName = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelName;
|
|
352
|
+
if (!requestMap[connectionId]) {
|
|
353
|
+
requestMap[connectionId] = {
|
|
354
|
+
appId: connectionId,
|
|
355
|
+
appName: (_a = connection === null || connection === void 0 ? void 0 : connection.name) !== null && _a !== void 0 ? _a : "",
|
|
356
|
+
appType: connection === null || connection === void 0 ? void 0 : connection.connectionType,
|
|
357
|
+
assets: {},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const appAssets = requestMap[connectionId].assets;
|
|
361
|
+
if (!appAssets[assetId]) {
|
|
362
|
+
appAssets[assetId] = {
|
|
363
|
+
assetId,
|
|
364
|
+
assetName,
|
|
365
|
+
type,
|
|
366
|
+
roles: {},
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (!appAssets[assetId].roles) {
|
|
370
|
+
appAssets[assetId].roles = {};
|
|
371
|
+
}
|
|
372
|
+
if (roleId) {
|
|
373
|
+
appAssets[assetId].roles[roleId] = { roleId, roleName: roleName !== null && roleName !== void 0 ? roleName : "" };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function convertRequestToMetadata(cmd, request, metadata) {
|
|
378
|
+
const { status, durationInMinutes, reason, requestedResources, requestedGroups, } = request;
|
|
379
|
+
if (status === graphql_1.RequestStatus.Pending) {
|
|
380
|
+
cmd.log("⏳ Request is still in progress");
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (durationInMinutes === undefined) {
|
|
384
|
+
cmd.error("Duration in minutes is required but was not provided.");
|
|
385
|
+
}
|
|
386
|
+
// if durationInMinutes is null, the request had a permanent duration
|
|
387
|
+
metadata.durationInMinutes = durationInMinutes !== null && durationInMinutes !== void 0 ? durationInMinutes : 0;
|
|
388
|
+
metadata.reason = reason;
|
|
389
|
+
addRequestedResourcesToMetadata(requestedResources, metadata.requestMap);
|
|
390
|
+
addRequestedGroupsToMetadata(requestedGroups, metadata.requestMap);
|
|
391
|
+
}
|
|
392
|
+
async function duplicateRequestTemplate(cmd, client, requestId, metadata) {
|
|
393
|
+
var _a, _b, _c, _d;
|
|
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 ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.request.__typename) {
|
|
399
|
+
case "RequestResult": {
|
|
400
|
+
if (((_b = resp.data) === null || _b === void 0 ? void 0 : _b.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, (_d = (_c = resp.data) === null || _c === void 0 ? void 0 : _c.request) === null || _d === void 0 ? void 0 : _d.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
|
+
var _a, _b;
|
|
452
|
+
cmd.log("Loading assets from bundle: ", bundleId);
|
|
453
|
+
const resp = await (0, requests_1.queryBundle)(client, bundleId);
|
|
454
|
+
// no fall through doesn't consider process.exit();
|
|
455
|
+
let x;
|
|
456
|
+
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.bundle.__typename) {
|
|
457
|
+
case "BundleResult": {
|
|
458
|
+
cmd.log("Creating new request with assets in the bundle...");
|
|
459
|
+
convertBundleToMetadata(cmd, (_b = resp.data) === null || _b === void 0 ? void 0 : _b.bundle.bundle, requestMap);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case "BundleNotFoundError":
|
|
463
|
+
x = cmd.error(`🚫 Bundle with id ${bundleId} was not found`);
|
|
464
|
+
break;
|
|
465
|
+
default:
|
|
466
|
+
cmd.error("🚫 Error retrieving bundle data");
|
|
467
|
+
}
|
|
468
|
+
}
|
|
@@ -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/resources.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from "@oclif/core";
|
|
2
|
-
import type { ResourceAccessLevel, ResourceAccessLevelInput } from "../
|
|
2
|
+
import type { ResourceAccessLevel, ResourceAccessLevelInput } from "../graphql/graphql";
|
|
3
3
|
export type ResourceInfo = {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
package/lib/lib/sessions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Command } from "@oclif/core";
|
|
2
|
-
import type { ResourceAccessLevelInput } from "../
|
|
2
|
+
import type { ResourceAccessLevelInput } from "../graphql/graphql";
|
|
3
3
|
export declare const getOrCreateSession: (command: Command, resourceId: string, accessLevel: ResourceAccessLevelInput, sessionId: string | undefined, metadataFragment: string, wantNewSession?: boolean) => Promise<any>;
|
|
4
4
|
export declare const getSessionExpirationMessage: (session: any) => string;
|
package/lib/lib/sessions.js
CHANGED
|
@@ -118,6 +118,7 @@ const openBrowserAndPollForSession = async (command, message, resourceId, access
|
|
|
118
118
|
return session;
|
|
119
119
|
};
|
|
120
120
|
const createSession = async (command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession) => {
|
|
121
|
+
var _a, _b, _c, _d;
|
|
121
122
|
const { resp, error } = await (0, handler_1.runMutation)({
|
|
122
123
|
command: command,
|
|
123
124
|
query: CreateSessionDocument.replace("METADATA_FRAGMENT", metadataFragment),
|
|
@@ -130,9 +131,9 @@ const createSession = async (command, resourceId, accessLevel, sessionId, metada
|
|
|
130
131
|
if (error) {
|
|
131
132
|
return (0, apollo_1.handleError)(command, error);
|
|
132
133
|
}
|
|
133
|
-
switch (resp === null || resp === void 0 ? void 0 : resp.data.createSession.__typename) {
|
|
134
|
+
switch ((_b = (_a = resp === null || resp === void 0 ? void 0 : resp.data) === null || _a === void 0 ? void 0 : _a.createSession) === null || _b === void 0 ? void 0 : _b.__typename) {
|
|
134
135
|
case "CreateSessionResult": {
|
|
135
|
-
return resp.data.createSession.session;
|
|
136
|
+
return (_d = (_c = resp.data) === null || _c === void 0 ? void 0 : _c.createSession) === null || _d === void 0 ? void 0 : _d.session;
|
|
136
137
|
}
|
|
137
138
|
case "MfaInvalidError": {
|
|
138
139
|
return openBrowserAndPollForSession(command, "❗ MFA validation needed. Please connect via browser. Opening browser and awaiting validation...", resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment, wantNewSession);
|
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";
|