opal-security 3.2.3 → 4.0.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 +46 -61
- package/bin/dev +5 -5
- package/bin/run +2 -4
- package/build/commands/aws/identity.js +16 -0
- package/build/commands/clear-auth-config.d.ts +6 -0
- package/build/commands/clear-auth-config.js +22 -0
- package/{lib → build}/commands/groups/get.js +14 -16
- package/{lib → build}/commands/iam-roles/start.js +28 -30
- package/build/commands/kube-roles/start.js +71 -0
- package/{lib → build}/commands/login.d.ts +1 -0
- package/build/commands/login.js +379 -0
- package/build/commands/logout.js +22 -0
- package/{lib → build}/commands/postgres-instances/start.js +25 -27
- package/{lib → build}/commands/request/create.js +34 -36
- package/{lib → build}/commands/request/get.js +22 -24
- package/{lib → build}/commands/request/list.js +17 -19
- package/{lib → build}/commands/resources/get.js +15 -18
- package/build/commands/set-auth-config.d.ts +11 -0
- package/build/commands/set-auth-config.js +59 -0
- package/build/commands/set-custom-header.js +35 -0
- package/{lib → build}/commands/set-token.js +15 -17
- package/{lib → build}/commands/set-url.js +26 -28
- package/{lib → build}/commands/ssh/copyFrom.js +22 -24
- package/{lib → build}/commands/ssh/copyTo.js +22 -24
- package/{lib → build}/commands/ssh/start.js +30 -33
- package/build/commands/whoami.js +27 -0
- package/{lib → build}/graphql/fragment-masking.d.ts +1 -1
- package/{lib → build}/graphql/fragment-masking.js +3 -8
- package/{lib → build}/graphql/gql.d.ts +1 -1
- package/{lib → build}/graphql/gql.js +2 -5
- package/{lib → build}/graphql/graphql.js +256 -261
- package/build/graphql/index.d.ts +2 -0
- package/build/graphql/index.js +2 -0
- package/{lib → build}/handler.d.ts +1 -1
- package/build/handler.js +36 -0
- package/build/index.js +1 -0
- package/{lib → build}/labels.d.ts +1 -1
- package/build/labels.js +37 -0
- package/{lib → build}/lib/apollo.d.ts +2 -2
- package/{lib → build}/lib/apollo.js +62 -69
- package/build/lib/auth-success-template.d.ts +3 -0
- package/build/lib/auth-success-template.js +149 -0
- package/{lib → build}/lib/aws.js +2 -7
- package/{lib → build}/lib/cmd.d.ts +4 -4
- package/{lib → build}/lib/cmd.js +16 -20
- package/build/lib/config.js +46 -0
- package/{lib → build}/lib/credentials/index.d.ts +3 -2
- package/build/lib/credentials/index.js +85 -0
- package/{lib → build}/lib/credentials/keychain.js +4 -10
- package/{lib → build}/lib/credentials/localEncryption.js +12 -17
- package/{lib → build}/lib/flags.js +7 -10
- package/build/lib/local-auth-server.d.ts +5 -0
- package/build/lib/local-auth-server.js +69 -0
- package/build/lib/request/api/index.d.ts +6 -0
- package/build/lib/request/api/index.js +8 -0
- package/{lib → build}/lib/request/api/mutations/create-request.d.ts +2 -2
- package/{lib → build}/lib/request/api/mutations/create-request.js +3 -6
- package/{lib → build}/lib/request/api/queries/apps.d.ts +1 -1
- package/{lib → build}/lib/request/api/queries/apps.js +3 -6
- package/{lib → build}/lib/request/api/queries/assets.d.ts +2 -2
- package/{lib → build}/lib/request/api/queries/assets.js +7 -11
- package/{lib → build}/lib/request/api/queries/request-defaults.d.ts +2 -2
- package/{lib → build}/lib/request/api/queries/request-defaults.js +3 -6
- package/{lib → build}/lib/request/api/queries/requests.d.ts +3 -3
- package/{lib → build}/lib/request/api/queries/requests.js +10 -16
- package/{lib → build}/lib/request/api/queries/roles.d.ts +1 -1
- package/{lib → build}/lib/request/api/queries/roles.js +14 -18
- package/{lib → build}/lib/request/displays.d.ts +2 -2
- package/{lib → build}/lib/request/displays.js +27 -37
- package/{lib → build}/lib/request/prompts/apps-prompt.d.ts +1 -1
- package/build/lib/request/prompts/apps-prompt.js +33 -0
- package/{lib → build}/lib/request/prompts/asset-prompt.d.ts +1 -1
- package/build/lib/request/prompts/asset-prompt.js +61 -0
- package/{lib → build}/lib/request/prompts/duration-prompt.d.ts +1 -1
- package/{lib → build}/lib/request/prompts/duration-prompt.js +6 -10
- package/build/lib/request/prompts/index.d.ts +7 -0
- package/build/lib/request/prompts/index.js +8 -0
- package/{lib → build}/lib/request/prompts/reason-prompt.d.ts +1 -1
- package/{lib → build}/lib/request/prompts/reason-prompt.js +3 -6
- package/{lib → build}/lib/request/prompts/role-prompt.d.ts +1 -1
- package/build/lib/request/prompts/role-prompt.js +33 -0
- package/{lib → build}/lib/request/prompts/validate-prompt.d.ts +1 -1
- package/{lib → build}/lib/request/prompts/validate-prompt.js +9 -13
- package/{lib → build}/lib/request/request-utils.d.ts +2 -2
- package/{lib → build}/lib/request/request-utils.js +50 -62
- package/{lib → build}/lib/request/types.d.ts +1 -1
- package/build/lib/request/types.js +12 -0
- package/{lib → build}/lib/resources.d.ts +1 -1
- package/{lib → build}/lib/resources.js +18 -23
- package/{lib → build}/lib/sessions.d.ts +1 -1
- package/{lib → build}/lib/sessions.js +57 -32
- package/{lib → build}/lib/ssh.d.ts +1 -1
- package/{lib → build}/lib/ssh.js +6 -11
- package/{lib → build}/lib/util.js +7 -14
- package/{lib → build}/types.js +98 -101
- package/oclif.manifest.json +77 -98
- package/package.json +24 -14
- package/lib/commands/aws/identity.js +0 -18
- package/lib/commands/clear-auth-provider.d.ts +0 -9
- package/lib/commands/clear-auth-provider.js +0 -28
- package/lib/commands/curl-example.d.ts +0 -8
- package/lib/commands/curl-example.js +0 -34
- package/lib/commands/kube-roles/start.js +0 -73
- package/lib/commands/login.js +0 -286
- package/lib/commands/logout.js +0 -23
- package/lib/commands/set-auth-provider.d.ts +0 -11
- package/lib/commands/set-auth-provider.js +0 -44
- package/lib/commands/set-custom-header.js +0 -37
- package/lib/commands/whoami.js +0 -34
- package/lib/graphql/index.d.ts +0 -2
- package/lib/graphql/index.js +0 -5
- package/lib/handler.js +0 -41
- package/lib/index.js +0 -5
- package/lib/labels.js +0 -40
- package/lib/lib/config.js +0 -54
- package/lib/lib/credentials/index.js +0 -67
- package/lib/lib/request/api/index.d.ts +0 -6
- package/lib/lib/request/api/index.js +0 -20
- package/lib/lib/request/prompts/apps-prompt.js +0 -35
- package/lib/lib/request/prompts/asset-prompt.js +0 -81
- package/lib/lib/request/prompts/index.d.ts +0 -8
- package/lib/lib/request/prompts/index.js +0 -20
- package/lib/lib/request/prompts/role-prompt.js +0 -44
- package/lib/lib/request/types.js +0 -15
- /package/{lib → build}/commands/aws/identity.d.ts +0 -0
- /package/{lib → build}/commands/groups/get.d.ts +0 -0
- /package/{lib → build}/commands/iam-roles/start.d.ts +0 -0
- /package/{lib → build}/commands/kube-roles/start.d.ts +0 -0
- /package/{lib → build}/commands/logout.d.ts +0 -0
- /package/{lib → build}/commands/postgres-instances/start.d.ts +0 -0
- /package/{lib → build}/commands/request/create.d.ts +0 -0
- /package/{lib → build}/commands/request/get.d.ts +0 -0
- /package/{lib → build}/commands/request/list.d.ts +0 -0
- /package/{lib → build}/commands/resources/get.d.ts +0 -0
- /package/{lib → build}/commands/set-custom-header.d.ts +0 -0
- /package/{lib → build}/commands/set-token.d.ts +0 -0
- /package/{lib → build}/commands/set-url.d.ts +0 -0
- /package/{lib → build}/commands/ssh/copyFrom.d.ts +0 -0
- /package/{lib → build}/commands/ssh/copyTo.d.ts +0 -0
- /package/{lib → build}/commands/ssh/start.d.ts +0 -0
- /package/{lib → build}/commands/whoami.d.ts +0 -0
- /package/{lib → build}/graphql/graphql.d.ts +0 -0
- /package/{lib → build}/index.d.ts +0 -0
- /package/{lib → build}/lib/aws.d.ts +0 -0
- /package/{lib → build}/lib/config.d.ts +0 -0
- /package/{lib → build}/lib/credentials/keychain.d.ts +0 -0
- /package/{lib → build}/lib/credentials/localEncryption.d.ts +0 -0
- /package/{lib → build}/lib/flags.d.ts +0 -0
- /package/{lib → build}/lib/util.d.ts +0 -0
- /package/{lib → build}/types.d.ts +0 -0
|
@@ -1,28 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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");
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { EntityType, RequestMessageCode, RequestStatus, ThirdPartyProvider, } from "../../graphql/graphql.js";
|
|
3
|
+
import { connectionTypeLabelByType } from "../../labels.js";
|
|
4
|
+
import { getOrCreateConfigData, urlKey } from "../../lib/config.js";
|
|
5
|
+
import { createRequest, queryCatalogItems, queryRequest, queryRequestDefaults, } from "./api/index.js";
|
|
6
|
+
import { queryBundle } from "./api/queries/requests.js";
|
|
7
|
+
import { displayRequestAgain, formatDuration, getStyledStatus, } from "./displays.js";
|
|
8
|
+
import { entityTypeFromString, } from "./types.js";
|
|
21
9
|
/*
|
|
22
10
|
Init Request Metadata
|
|
23
11
|
This function initializes the request metadata with empty defaults and an empty request map.
|
|
24
12
|
*/
|
|
25
|
-
function initEmptyRequestMetadata() {
|
|
13
|
+
export function initEmptyRequestMetadata() {
|
|
26
14
|
// Initialize with empty defaults
|
|
27
15
|
const requestDefaults = {
|
|
28
16
|
durationOptions: [],
|
|
@@ -48,7 +36,7 @@ After setting up the request map, this function fetches the request defaults
|
|
|
48
36
|
from the server based on the requested resources and groups.
|
|
49
37
|
It updates the metadata with the fetched defaults.
|
|
50
38
|
*/
|
|
51
|
-
async function setRequestDefaults(cmd, client, metadata) {
|
|
39
|
+
export async function setRequestDefaults(cmd, client, metadata) {
|
|
52
40
|
var _a;
|
|
53
41
|
const requestMap = metadata.requestMap;
|
|
54
42
|
const requestedResources = [];
|
|
@@ -62,14 +50,14 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
62
50
|
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
63
51
|
for (const roleId of roleIds) {
|
|
64
52
|
switch (assetNode.type) {
|
|
65
|
-
case
|
|
53
|
+
case EntityType.Resource: {
|
|
66
54
|
requestedResources.push({
|
|
67
55
|
resourceId: assetId,
|
|
68
56
|
accessLevelRemoteId: roleId,
|
|
69
57
|
});
|
|
70
58
|
break;
|
|
71
59
|
}
|
|
72
|
-
case
|
|
60
|
+
case EntityType.Group: {
|
|
73
61
|
requestedGroups.push({
|
|
74
62
|
groupId: assetId,
|
|
75
63
|
accessLevelRemoteId: roleId,
|
|
@@ -82,7 +70,7 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
82
70
|
}
|
|
83
71
|
}
|
|
84
72
|
try {
|
|
85
|
-
const requestDefaults = await
|
|
73
|
+
const requestDefaults = await queryRequestDefaults(cmd, client, requestedResources, requestedGroups);
|
|
86
74
|
if (requestDefaults !== undefined) {
|
|
87
75
|
metadata.requestDefaults.durationOptions =
|
|
88
76
|
requestDefaults.durationOptions;
|
|
@@ -113,7 +101,7 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
113
101
|
}
|
|
114
102
|
}
|
|
115
103
|
// Log the request message based on the code
|
|
116
|
-
cmd.log(
|
|
104
|
+
cmd.log(chalk.dim(`\n${chalk.italic(message.level)}:`), getRequestMessageFromCode(cmd, message.code, connectionName, connectionType));
|
|
117
105
|
if (message.level === "ERROR") {
|
|
118
106
|
process.exit(1);
|
|
119
107
|
}
|
|
@@ -121,7 +109,7 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
121
109
|
if (requestDefaults.requireSupportTicket) {
|
|
122
110
|
cmd.log("\nA support ticket is required for this request. Please submit through the Opal website.");
|
|
123
111
|
const requestLink = generateRequestLink(cmd, (_a = metadata.requestDefaults.defaultDurationInMinutes) !== null && _a !== void 0 ? _a : 60);
|
|
124
|
-
cmd.log(`${
|
|
112
|
+
cmd.log(`${chalk.bold("Link:")} ${requestLink}\n`);
|
|
125
113
|
process.exit(1);
|
|
126
114
|
}
|
|
127
115
|
}
|
|
@@ -130,7 +118,7 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
130
118
|
cmd.error("Error fetching request defaults.");
|
|
131
119
|
}
|
|
132
120
|
}
|
|
133
|
-
async function submitFinalRequest(cmd, client, metadata) {
|
|
121
|
+
export async function submitFinalRequest(cmd, client, metadata) {
|
|
134
122
|
var _a, _b, _c, _d;
|
|
135
123
|
// Build requested assets lists for the mutation
|
|
136
124
|
const requestedResources = [];
|
|
@@ -147,7 +135,7 @@ async function submitFinalRequest(cmd, client, metadata) {
|
|
|
147
135
|
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
148
136
|
for (const roleId of roleIds) {
|
|
149
137
|
switch (assetNode.type) {
|
|
150
|
-
case
|
|
138
|
+
case EntityType.Resource: {
|
|
151
139
|
requestedResources.push({
|
|
152
140
|
resourceId: assetId,
|
|
153
141
|
accessLevel: {
|
|
@@ -157,7 +145,7 @@ async function submitFinalRequest(cmd, client, metadata) {
|
|
|
157
145
|
});
|
|
158
146
|
break;
|
|
159
147
|
}
|
|
160
|
-
case
|
|
148
|
+
case EntityType.Group: {
|
|
161
149
|
requestedGroups.push({
|
|
162
150
|
groupId: assetId,
|
|
163
151
|
accessLevel: {
|
|
@@ -172,30 +160,30 @@ async function submitFinalRequest(cmd, client, metadata) {
|
|
|
172
160
|
}
|
|
173
161
|
}
|
|
174
162
|
}
|
|
175
|
-
const resp = await
|
|
163
|
+
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
176
164
|
// Build link to request
|
|
177
165
|
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
178
166
|
cmd.log("\n🎉 Your Access Request has been submitted!\n");
|
|
179
|
-
cmd.log(`${
|
|
167
|
+
cmd.log(`${chalk.bold("ID:")} ${chalk.cyan(resp.id)}`);
|
|
180
168
|
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
181
|
-
cmd.log(
|
|
169
|
+
cmd.log(getStyledStatus(resp.status));
|
|
182
170
|
}
|
|
183
171
|
const requestLink = getRequestLink(cmd, resp.id);
|
|
184
|
-
cmd.log(`${
|
|
185
|
-
|
|
172
|
+
cmd.log(`${chalk.bold("Link:")} ${chalk.underline(requestLink)}\n`);
|
|
173
|
+
displayRequestAgain(cmd, resp.id);
|
|
186
174
|
}
|
|
187
175
|
return;
|
|
188
176
|
}
|
|
189
|
-
function getRequestLink(cmd, id) {
|
|
190
|
-
const configData =
|
|
191
|
-
return `${configData[
|
|
177
|
+
export function getRequestLink(cmd, id) {
|
|
178
|
+
const configData = getOrCreateConfigData(cmd.config.configDir);
|
|
179
|
+
return `${configData[urlKey]}/requests/sent/${id}`;
|
|
192
180
|
}
|
|
193
|
-
function generateRequestLink(cmd, defaultDurationInMinutes) {
|
|
194
|
-
const configData =
|
|
181
|
+
export function generateRequestLink(cmd, defaultDurationInMinutes) {
|
|
182
|
+
const configData = getOrCreateConfigData(cmd.config.configDir);
|
|
195
183
|
// Including the duration in the URL so that the cancel button on the request page works as expected
|
|
196
|
-
return `${configData[
|
|
184
|
+
return `${configData[urlKey]}/request-access?durationInMinutes=${defaultDurationInMinutes}`;
|
|
197
185
|
}
|
|
198
|
-
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
186
|
+
export async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
199
187
|
var _a, _b, _c, _d;
|
|
200
188
|
try {
|
|
201
189
|
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
@@ -203,7 +191,7 @@ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
|
203
191
|
const delimiterIndex = val.indexOf(":");
|
|
204
192
|
const assetId = delimiterIndex === -1 ? val : val.substring(0, delimiterIndex);
|
|
205
193
|
const roleName = delimiterIndex === -1 ? "" : val.substring(delimiterIndex + 1);
|
|
206
|
-
const resp = await
|
|
194
|
+
const resp = await queryCatalogItems(cmd, client, assetId);
|
|
207
195
|
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.catalogItem.__typename) {
|
|
208
196
|
case "Group":
|
|
209
197
|
case "Resource": {
|
|
@@ -228,7 +216,7 @@ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
|
228
216
|
metadata.requestMap[appId].assets[assetId] = {
|
|
229
217
|
assetId: assetId,
|
|
230
218
|
assetName: assetName,
|
|
231
|
-
type:
|
|
219
|
+
type: entityTypeFromString(item.__typename),
|
|
232
220
|
roles: {},
|
|
233
221
|
};
|
|
234
222
|
}
|
|
@@ -262,12 +250,12 @@ async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
|
262
250
|
}
|
|
263
251
|
return;
|
|
264
252
|
}
|
|
265
|
-
function bypassDuration(cmd, duration, metadata) {
|
|
253
|
+
export function bypassDuration(cmd, duration, metadata) {
|
|
266
254
|
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
267
255
|
// Permanent duration is represented by 0, but should not be allowed
|
|
268
256
|
// if a max duration is set.
|
|
269
257
|
if (maxDuration && (duration > maxDuration || duration === 0)) {
|
|
270
|
-
cmd.error(`The requested duration exceeds the allowed limit of ${
|
|
258
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${formatDuration(maxDuration)}. Please choose a shorter duration.`);
|
|
271
259
|
}
|
|
272
260
|
if (duration === 0) {
|
|
273
261
|
metadata.durationInMinutes = undefined;
|
|
@@ -276,21 +264,21 @@ function bypassDuration(cmd, duration, metadata) {
|
|
|
276
264
|
metadata.durationInMinutes = duration;
|
|
277
265
|
}
|
|
278
266
|
}
|
|
279
|
-
function getRequestMessageFromCode(cmd, code, connectionName, connectionType, extraParams,
|
|
267
|
+
export function getRequestMessageFromCode(cmd, code, connectionName, connectionType, extraParams,
|
|
280
268
|
// sourceGroup?: PropsFor<typeof GroupBindingGroupLabel>["group"];
|
|
281
269
|
sourceGroupRedirect) {
|
|
282
270
|
switch (code) {
|
|
283
|
-
case
|
|
271
|
+
case RequestMessageCode.RequireUserAuthToken: {
|
|
284
272
|
if (connectionType) {
|
|
285
|
-
const connectionLabel =
|
|
273
|
+
const connectionLabel = connectionTypeLabelByType[connectionType];
|
|
286
274
|
// This case the connection has a third party provider such as GitLab,
|
|
287
275
|
// GitHub that requires the use to create a manual step into the end
|
|
288
276
|
// system.
|
|
289
|
-
const isThirdPartyProvider = Object.values(
|
|
277
|
+
const isThirdPartyProvider = Object.values(ThirdPartyProvider).find((v) => v === connectionType);
|
|
290
278
|
if (isThirdPartyProvider) {
|
|
291
|
-
const configData =
|
|
279
|
+
const configData = getOrCreateConfigData(cmd.config.configDir);
|
|
292
280
|
return `This item requires you to link your ${connectionLabel} account to Opal before requesting access.
|
|
293
|
-
You can link your ${connectionLabel} account at ${`${configData[
|
|
281
|
+
You can link your ${connectionLabel} account at ${`${configData[urlKey]}/user/settings/identities`}`;
|
|
294
282
|
}
|
|
295
283
|
const name = connectionName !== null && connectionName !== void 0 ? connectionName : connectionLabel;
|
|
296
284
|
// This case would be hit in case the user is not provisioned onto the end system, such as AWS, GCP, Okta, etc.
|
|
@@ -300,9 +288,9 @@ Please contact your Opal admin to provision your user on ${name}.`;
|
|
|
300
288
|
// This should never happen, but just in case we forgot to pass in the third party provider or the connection type
|
|
301
289
|
return "Your user does not exist on the end system. Please contact your Opal admin to add your user to the end system.";
|
|
302
290
|
}
|
|
303
|
-
case
|
|
291
|
+
case RequestMessageCode.NestedGroupAccessNotAllowed:
|
|
304
292
|
return "You cannot request access to this group because you already have nested access.";
|
|
305
|
-
case
|
|
293
|
+
case RequestMessageCode.LinkedGroupNotRequestable:
|
|
306
294
|
return "You cannot request access to this group because it is linked to another group";
|
|
307
295
|
default:
|
|
308
296
|
return `Unknown request message code: ${code}`;
|
|
@@ -314,7 +302,7 @@ function addRequestedResourcesToMetadata(requestedResources, requestMap) {
|
|
|
314
302
|
if (!resource)
|
|
315
303
|
continue;
|
|
316
304
|
const { id: assetId, displayName: assetName, connection, connectionId, __typename, } = resource;
|
|
317
|
-
const type =
|
|
305
|
+
const type = entityTypeFromString(__typename);
|
|
318
306
|
const roleId = accessLevel.accessLevelRemoteId;
|
|
319
307
|
const roleName = accessLevel.accessLevelName;
|
|
320
308
|
if (!requestMap[connectionId]) {
|
|
@@ -346,7 +334,7 @@ function addRequestedGroupsToMetadata(requestedGroups, requestMap) {
|
|
|
346
334
|
if (!group)
|
|
347
335
|
continue;
|
|
348
336
|
const { id: assetId, name: assetName, connection, connectionId, __typename, } = group;
|
|
349
|
-
const type =
|
|
337
|
+
const type = entityTypeFromString(__typename);
|
|
350
338
|
const roleId = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelRemoteId;
|
|
351
339
|
const roleName = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelName;
|
|
352
340
|
if (!requestMap[connectionId]) {
|
|
@@ -376,7 +364,7 @@ function addRequestedGroupsToMetadata(requestedGroups, requestMap) {
|
|
|
376
364
|
}
|
|
377
365
|
async function convertRequestToMetadata(cmd, request, metadata) {
|
|
378
366
|
const { status, durationInMinutes, reason, requestedResources, requestedGroups, } = request;
|
|
379
|
-
if (status ===
|
|
367
|
+
if (status === RequestStatus.Pending) {
|
|
380
368
|
cmd.log("⏳ Request is still in progress");
|
|
381
369
|
return;
|
|
382
370
|
}
|
|
@@ -389,16 +377,16 @@ async function convertRequestToMetadata(cmd, request, metadata) {
|
|
|
389
377
|
addRequestedResourcesToMetadata(requestedResources, metadata.requestMap);
|
|
390
378
|
addRequestedGroupsToMetadata(requestedGroups, metadata.requestMap);
|
|
391
379
|
}
|
|
392
|
-
async function duplicateRequestTemplate(cmd, client, requestId, metadata) {
|
|
380
|
+
export async function duplicateRequestTemplate(cmd, client, requestId, metadata) {
|
|
393
381
|
var _a, _b, _c, _d;
|
|
394
382
|
cmd.log("Loading request template from ID: ", requestId);
|
|
395
|
-
const resp = await
|
|
383
|
+
const resp = await queryRequest(client, requestId);
|
|
396
384
|
// no fall through doesn't consider process.exit();
|
|
397
385
|
let x;
|
|
398
386
|
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.request.__typename) {
|
|
399
387
|
case "RequestResult": {
|
|
400
388
|
if (((_b = resp.data) === null || _b === void 0 ? void 0 : _b.request.request).status ===
|
|
401
|
-
|
|
389
|
+
RequestStatus.Pending) {
|
|
402
390
|
cmd.error("⏳ Cannot duplicate a request that is still in progress");
|
|
403
391
|
}
|
|
404
392
|
cmd.log("Creating new request with same configuration...");
|
|
@@ -447,10 +435,10 @@ async function convertBundleToMetadata(cmd, bundle, requestMap) {
|
|
|
447
435
|
}
|
|
448
436
|
cmd.log("Added all requestable items in the bundle");
|
|
449
437
|
}
|
|
450
|
-
async function copyBundleAssets(cmd, client, bundleId, requestMap) {
|
|
438
|
+
export async function copyBundleAssets(cmd, client, bundleId, requestMap) {
|
|
451
439
|
var _a, _b;
|
|
452
440
|
cmd.log("Loading assets from bundle: ", bundleId);
|
|
453
|
-
const resp = await
|
|
441
|
+
const resp = await queryBundle(client, bundleId);
|
|
454
442
|
// no fall through doesn't consider process.exit();
|
|
455
443
|
let x;
|
|
456
444
|
switch ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.bundle.__typename) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ConnectionType, EntityType, type RequestMessageCode, type RequestMessageLevel } from "../../graphql/graphql";
|
|
1
|
+
import { type ConnectionType, EntityType, type RequestMessageCode, type RequestMessageLevel } from "../../graphql/graphql.js";
|
|
2
2
|
type AppNode = {
|
|
3
3
|
appId: string;
|
|
4
4
|
appName: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EntityType, } from "../../graphql/graphql.js";
|
|
2
|
+
export function entityTypeFromString(str) {
|
|
3
|
+
const capStr = str === null || str === void 0 ? void 0 : str.toLocaleUpperCase();
|
|
4
|
+
if (capStr === "RESOURCE") {
|
|
5
|
+
return EntityType.Resource;
|
|
6
|
+
}
|
|
7
|
+
if (capStr === "GROUP") {
|
|
8
|
+
return EntityType.Group;
|
|
9
|
+
}
|
|
10
|
+
// if type unknown, default to resource
|
|
11
|
+
return EntityType.Resource;
|
|
12
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from "@oclif/core";
|
|
2
|
-
import type { ResourceAccessLevel, ResourceAccessLevelInput } from "../graphql/graphql";
|
|
2
|
+
import type { ResourceAccessLevel, ResourceAccessLevelInput } from "../graphql/graphql.js";
|
|
3
3
|
export type ResourceInfo = {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const apollo_1 = require("./apollo");
|
|
7
|
-
exports.DEFAULT_ACCESS_LEVEL = {
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import inquirerPrompt from "inquirer-autocomplete-prompt";
|
|
3
|
+
import { runQueryDeprecated } from "../handler.js";
|
|
4
|
+
import { handleError } from "./apollo.js";
|
|
5
|
+
export const DEFAULT_ACCESS_LEVEL = {
|
|
8
6
|
accessLevelName: "",
|
|
9
7
|
accessLevelRemoteId: "",
|
|
10
8
|
};
|
|
11
9
|
// Returns a filtered array of items that match the input
|
|
12
|
-
const filterChoices = (input, choices) => {
|
|
10
|
+
export const filterChoices = (input, choices) => {
|
|
13
11
|
if (!input) {
|
|
14
12
|
return choices;
|
|
15
13
|
}
|
|
16
14
|
return choices.filter((choice) => choice.name.toLowerCase().includes(input.toLowerCase()));
|
|
17
15
|
};
|
|
18
|
-
exports.filterChoices = filterChoices;
|
|
19
16
|
const ListResourcesDocumentTemplate = `
|
|
20
17
|
query ListResources {
|
|
21
18
|
resources(input: {resourceTypes: [RESOURCE_TYPE], onlyMine: true, maxNumEntries: 1000}) {
|
|
@@ -35,15 +32,15 @@ query ListResources {
|
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
34
|
}`;
|
|
38
|
-
const promptUserForResource = async (command, resourceType, message) => {
|
|
35
|
+
export const promptUserForResource = async (command, resourceType, message) => {
|
|
39
36
|
const listResourcesDocument = ListResourcesDocumentTemplate.replace("RESOURCE_TYPE", resourceType);
|
|
40
|
-
const { resp, error } = await
|
|
37
|
+
const { resp, error } = await runQueryDeprecated({
|
|
41
38
|
command: command,
|
|
42
39
|
query: listResourcesDocument,
|
|
43
40
|
variables: {},
|
|
44
41
|
});
|
|
45
42
|
if (error) {
|
|
46
|
-
return
|
|
43
|
+
return handleError(command, error, resp);
|
|
47
44
|
}
|
|
48
45
|
const resourceInfos = resp === null || resp === void 0 ? void 0 : resp.data.resources.resources.map((resource) => {
|
|
49
46
|
let name = resource.name;
|
|
@@ -59,25 +56,24 @@ const promptUserForResource = async (command, resourceType, message) => {
|
|
|
59
56
|
};
|
|
60
57
|
});
|
|
61
58
|
if (resourceInfos.length === 0) {
|
|
62
|
-
return
|
|
59
|
+
return handleError(command, "You don't have access to any resources of this type. Please go to Opal to request access.");
|
|
63
60
|
}
|
|
64
61
|
const resourceInfoByName = {};
|
|
65
62
|
// biome-ignore lint/complexity/noForEach: fix it when you get the chance
|
|
66
63
|
resourceInfos.forEach((resourceInfo) => {
|
|
67
64
|
resourceInfoByName[resourceInfo.name] = resourceInfo;
|
|
68
65
|
});
|
|
69
|
-
inquirer.registerPrompt("autocomplete",
|
|
66
|
+
inquirer.registerPrompt("autocomplete", inquirerPrompt);
|
|
70
67
|
const selectedResourceInfo = await inquirer.prompt([
|
|
71
68
|
{
|
|
72
69
|
name: "resource",
|
|
73
70
|
message: message,
|
|
74
71
|
type: "autocomplete",
|
|
75
|
-
source: (answers, input) =>
|
|
72
|
+
source: (answers, input) => filterChoices(input, resourceInfos),
|
|
76
73
|
},
|
|
77
74
|
]);
|
|
78
75
|
return resourceInfoByName[selectedResourceInfo.resource];
|
|
79
76
|
};
|
|
80
|
-
exports.promptUserForResource = promptUserForResource;
|
|
81
77
|
const ListAccessLevelsForResource = `
|
|
82
78
|
query ListAccessLevelsForResource($resourceId: ResourceId!) {
|
|
83
79
|
accessLevels(input: {resourceId: $resourceId, onlyMine: true}) {
|
|
@@ -90,22 +86,22 @@ query ListAccessLevelsForResource($resourceId: ResourceId!) {
|
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
88
|
}`;
|
|
93
|
-
const promptUserForAccessLevels = async (command, resourceId, instanceType, accessLevelRemoteId) => {
|
|
89
|
+
export const promptUserForAccessLevels = async (command, resourceId, instanceType, accessLevelRemoteId) => {
|
|
94
90
|
var _a, _b, _c;
|
|
95
|
-
const { resp, error } = await
|
|
91
|
+
const { resp, error } = await runQueryDeprecated({
|
|
96
92
|
command: command,
|
|
97
93
|
query: ListAccessLevelsForResource,
|
|
98
94
|
variables: { resourceId },
|
|
99
95
|
});
|
|
100
96
|
if (error) {
|
|
101
|
-
return
|
|
97
|
+
return handleError(command, error, resp);
|
|
102
98
|
}
|
|
103
99
|
const accessLevelInfos = (_c = (_b = (_a = resp === null || resp === void 0 ? void 0 : 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((resp) => ({
|
|
104
100
|
id: resp.accessLevelRemoteId,
|
|
105
101
|
name: resp.accessLevelName,
|
|
106
102
|
}));
|
|
107
103
|
if (!accessLevelInfos) {
|
|
108
|
-
return
|
|
104
|
+
return handleError(command, "This resource requires an access level, but none have been set up in Opal. Please contact your Opal admin.");
|
|
109
105
|
}
|
|
110
106
|
const accessLevelInfoByName = {};
|
|
111
107
|
const accessLevelInfoByRemoteId = {};
|
|
@@ -120,13 +116,13 @@ const promptUserForAccessLevels = async (command, resourceId, instanceType, acce
|
|
|
120
116
|
}
|
|
121
117
|
// Prompt user to pick access levels available to them
|
|
122
118
|
if (!selectedAccessLevel) {
|
|
123
|
-
inquirer.registerPrompt("autocomplete",
|
|
119
|
+
inquirer.registerPrompt("autocomplete", inquirerPrompt);
|
|
124
120
|
const selectedAccessLevelInfo = await inquirer.prompt([
|
|
125
121
|
{
|
|
126
122
|
name: "accessLevel",
|
|
127
123
|
message: `Select an access level to the ${instanceType}`,
|
|
128
124
|
type: "autocomplete",
|
|
129
|
-
source: (answers, input) =>
|
|
125
|
+
source: (answers, input) => filterChoices(input, accessLevelInfos),
|
|
130
126
|
},
|
|
131
127
|
]);
|
|
132
128
|
selectedAccessLevel =
|
|
@@ -137,4 +133,3 @@ const promptUserForAccessLevels = async (command, resourceId, instanceType, acce
|
|
|
137
133
|
accessLevelRemoteId: selectedAccessLevel.id,
|
|
138
134
|
};
|
|
139
135
|
};
|
|
140
|
-
exports.promptUserForAccessLevels = promptUserForAccessLevels;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Command } from "@oclif/core";
|
|
2
|
-
import type { ResourceAccessLevelInput } from "../graphql/graphql";
|
|
2
|
+
import type { ResourceAccessLevelInput } from "../graphql/graphql.js";
|
|
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;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const util_1 = require("./util");
|
|
1
|
+
import { ux } from "@oclif/core";
|
|
2
|
+
import inquirer from "inquirer";
|
|
3
|
+
import moment from "moment";
|
|
4
|
+
import open from "open";
|
|
5
|
+
import { runMutation, runQueryDeprecated } from "../handler.js";
|
|
6
|
+
import { handleError } from "./apollo.js";
|
|
7
|
+
import { getOrCreateConfigData, urlKey } from "./config.js";
|
|
8
|
+
import { sleep } from "./util.js";
|
|
10
9
|
const CreateSessionDocument = `
|
|
11
10
|
mutation CreateSession($id: ResourceId!, $accessLevel: ResourceAccessLevelInput!, $sessionId: SessionId) {
|
|
12
11
|
createSession(input: {resourceId: $id, accessLevel: $accessLevel, sessionId: $sessionId}) {
|
|
@@ -59,7 +58,7 @@ query ListSessions($id: ResourceId!) {
|
|
|
59
58
|
}
|
|
60
59
|
`;
|
|
61
60
|
const getSession = async (command, resourceId, accessLevelRemoteId, sessionId, metadataFragment, minCreatedAt) => {
|
|
62
|
-
const { resp, error } = await
|
|
61
|
+
const { resp, error } = await runQueryDeprecated({
|
|
63
62
|
command: command,
|
|
64
63
|
query: ListSessionsDocument.replace("METADATA_FRAGMENT", metadataFragment),
|
|
65
64
|
variables: {
|
|
@@ -67,7 +66,7 @@ const getSession = async (command, resourceId, accessLevelRemoteId, sessionId, m
|
|
|
67
66
|
},
|
|
68
67
|
});
|
|
69
68
|
if (error) {
|
|
70
|
-
return
|
|
69
|
+
return handleError(command, error);
|
|
71
70
|
}
|
|
72
71
|
switch (resp === null || resp === void 0 ? void 0 : resp.data.sessions.__typename) {
|
|
73
72
|
case "SessionsResult": {
|
|
@@ -94,32 +93,60 @@ const getSession = async (command, resourceId, accessLevelRemoteId, sessionId, m
|
|
|
94
93
|
return selectedSession;
|
|
95
94
|
}
|
|
96
95
|
default:
|
|
97
|
-
return
|
|
96
|
+
return handleError(command, error, resp);
|
|
98
97
|
}
|
|
99
98
|
};
|
|
100
|
-
const openBrowserAndPollForSession = async (command,
|
|
99
|
+
const openBrowserAndPollForSession = async (command, authenticationType, resourceId, accessLevelRemoteId, sessionId, metadataFragment, wantNewSession) => {
|
|
101
100
|
let minCreatedAt;
|
|
102
101
|
if (wantNewSession) {
|
|
103
102
|
// Ensure we poll for a new session rather than get an existing one.
|
|
104
103
|
minCreatedAt = moment();
|
|
105
104
|
}
|
|
106
|
-
command.
|
|
107
|
-
const
|
|
108
|
-
const url =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
const configData = getOrCreateConfigData(command.config.configDir);
|
|
106
|
+
const urlPrefix = configData[urlKey];
|
|
107
|
+
const url = `${urlPrefix}/resources/${resourceId}?showModal=true&refresh=true`;
|
|
108
|
+
command.log(`
|
|
109
|
+
🔐 ${authenticationType} Required
|
|
110
|
+
|
|
111
|
+
To continue, authenicate in your browser:
|
|
112
|
+
|
|
113
|
+
⚠️ Security Check:
|
|
114
|
+
• Verify the URL is exactly: ${url}
|
|
115
|
+
• Never enter this code on any other website
|
|
116
|
+
`);
|
|
117
|
+
await inquirer.prompt([
|
|
118
|
+
{
|
|
119
|
+
type: "input",
|
|
120
|
+
name: "continue",
|
|
121
|
+
message: "Press Enter to open your browser and continue",
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
open(url);
|
|
125
|
+
ux.action.start("Waiting for session approval");
|
|
126
|
+
const timeoutMs = 5 * 60 * 1000; // 5 minutes
|
|
127
|
+
const startTime = Date.now();
|
|
112
128
|
// biome-ignore lint/suspicious/noImplicitAnyLet: please fix typing for queries
|
|
113
129
|
let session;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
130
|
+
try {
|
|
131
|
+
while (!session) {
|
|
132
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
133
|
+
ux.action.stop("✗ Timed out");
|
|
134
|
+
throw new Error(`Timed out waiting for session after ${timeoutMs / 1000} seconds. Please ensure you've approved the access request in your browser.`);
|
|
135
|
+
}
|
|
136
|
+
await sleep(2000);
|
|
137
|
+
session = await getSession(command, resourceId, accessLevelRemoteId, sessionId, metadataFragment, minCreatedAt);
|
|
138
|
+
}
|
|
139
|
+
ux.action.stop("✓ Session approved");
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
ux.action.stop("✗ Failed");
|
|
143
|
+
throw error;
|
|
117
144
|
}
|
|
118
145
|
return session;
|
|
119
146
|
};
|
|
120
147
|
const createSession = async (command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession) => {
|
|
121
148
|
var _a, _b, _c, _d;
|
|
122
|
-
const { resp, error } = await
|
|
149
|
+
const { resp, error } = await runMutation({
|
|
123
150
|
command: command,
|
|
124
151
|
query: CreateSessionDocument.replace("METADATA_FRAGMENT", metadataFragment),
|
|
125
152
|
variables: {
|
|
@@ -129,23 +156,23 @@ const createSession = async (command, resourceId, accessLevel, sessionId, metada
|
|
|
129
156
|
},
|
|
130
157
|
});
|
|
131
158
|
if (error) {
|
|
132
|
-
return
|
|
159
|
+
return handleError(command, error);
|
|
133
160
|
}
|
|
134
161
|
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) {
|
|
135
162
|
case "CreateSessionResult": {
|
|
136
163
|
return (_d = (_c = resp.data) === null || _c === void 0 ? void 0 : _c.createSession) === null || _d === void 0 ? void 0 : _d.session;
|
|
137
164
|
}
|
|
138
165
|
case "MfaInvalidError": {
|
|
139
|
-
return openBrowserAndPollForSession(command, "
|
|
166
|
+
return openBrowserAndPollForSession(command, "MFA Validation", resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment, wantNewSession);
|
|
140
167
|
}
|
|
141
168
|
case "OidcIDTokenNotFoundError": {
|
|
142
|
-
return openBrowserAndPollForSession(command, "
|
|
169
|
+
return openBrowserAndPollForSession(command, "OIDC Authenication", resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment, wantNewSession);
|
|
143
170
|
}
|
|
144
171
|
default:
|
|
145
|
-
return
|
|
172
|
+
return handleError(command, error, resp);
|
|
146
173
|
}
|
|
147
174
|
};
|
|
148
|
-
const getOrCreateSession = async (command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession) => {
|
|
175
|
+
export const getOrCreateSession = async (command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession) => {
|
|
149
176
|
if (!wantNewSession) {
|
|
150
177
|
// Use existing session if it exists
|
|
151
178
|
const existingSession = await getSession(command, resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment);
|
|
@@ -154,16 +181,14 @@ const getOrCreateSession = async (command, resourceId, accessLevel, sessionId, m
|
|
|
154
181
|
}
|
|
155
182
|
}
|
|
156
183
|
if (sessionId) {
|
|
157
|
-
return
|
|
184
|
+
return handleError(command, `Session not found for given id: ${sessionId}`);
|
|
158
185
|
}
|
|
159
186
|
// Create new session
|
|
160
187
|
return createSession(command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession);
|
|
161
188
|
};
|
|
162
|
-
|
|
163
|
-
const getSessionExpirationMessage = (session) => {
|
|
189
|
+
export const getSessionExpirationMessage = (session) => {
|
|
164
190
|
const diff = moment(session.endTime).diff(moment(), "minutes");
|
|
165
191
|
const hours = Math.floor(diff / 60);
|
|
166
192
|
const minutes = diff % 60;
|
|
167
193
|
return `${hours}h ${minutes}m`;
|
|
168
194
|
};
|
|
169
|
-
exports.getSessionExpirationMessage = getSessionExpirationMessage;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Command } from "@oclif/core";
|
|
2
|
-
export declare const selectComputeInstance: (command: Command, action: string) => Promise<void | import("./resources").ResourceInfo>;
|
|
2
|
+
export declare const selectComputeInstance: (command: Command, action: string) => Promise<void | import("./resources.js").ResourceInfo>;
|
|
3
3
|
export declare const assertSessionManagerPluginExists: () => Promise<boolean>;
|