opal-security 4.0.4 → 5.0.0
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 +64 -67
- package/build/commands/curl-example.d.ts +8 -0
- package/build/commands/curl-example.js +35 -0
- package/build/commands/iam-roles/start.d.ts +0 -2
- package/build/commands/iam-roles/start.js +5 -7
- package/build/commands/kube-roles/start.d.ts +0 -2
- package/build/commands/kube-roles/start.js +5 -7
- package/build/commands/login.js +15 -16
- package/build/commands/postgres-instances/start.d.ts +0 -2
- package/build/commands/postgres-instances/start.js +4 -6
- package/build/commands/ssh/copyFrom.d.ts +0 -1
- package/build/commands/ssh/copyFrom.js +4 -5
- package/build/commands/ssh/copyTo.d.ts +0 -1
- package/build/commands/ssh/copyTo.js +4 -5
- package/build/commands/ssh/start.d.ts +0 -2
- package/build/commands/ssh/start.js +5 -7
- package/build/graphql/gql.d.ts +10 -0
- package/build/graphql/gql.js +2 -0
- package/build/graphql/graphql.d.ts +377 -378
- package/build/graphql/graphql.js +109 -38
- package/build/labels.js +4 -0
- package/build/lib/apollo.d.ts +1 -1
- package/build/lib/apollo.js +1 -1
- package/build/lib/config.js +1 -0
- package/build/lib/flags.d.ts +0 -2
- package/build/lib/flags.js +0 -9
- package/build/lib/local-auth-server.d.ts +7 -3
- package/build/lib/local-auth-server.js +38 -13
- package/build/lib/mfa.d.ts +2 -0
- package/build/lib/mfa.js +62 -0
- package/build/lib/oidc.d.ts +3 -0
- package/build/lib/oidc.js +64 -0
- package/build/lib/sessions.d.ts +3 -3
- package/build/lib/sessions.js +14 -133
- package/oclif.manifest.json +29 -77
- package/package.json +4 -2
package/build/lib/sessions.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { ux } from "@oclif/core";
|
|
2
|
-
import inquirer from "inquirer";
|
|
3
1
|
import moment from "moment";
|
|
4
|
-
import
|
|
5
|
-
import { runMutation
|
|
2
|
+
import { OidcProviderType } from "../graphql/graphql.js";
|
|
3
|
+
import { runMutation } from "../handler.js";
|
|
6
4
|
import { handleError } from "./apollo.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { waitForMfa } from "./mfa.js";
|
|
6
|
+
import { waitForValidOidcToken } from "./oidc.js";
|
|
9
7
|
const CreateSessionDocument = `
|
|
10
8
|
mutation CreateSession($id: ResourceId!, $accessLevel: ResourceAccessLevelInput!, $sessionId: SessionId) {
|
|
11
9
|
createSession(input: {resourceId: $id, accessLevel: $accessLevel, sessionId: $sessionId}) {
|
|
@@ -16,9 +14,9 @@ mutation CreateSession($id: ResourceId!, $accessLevel: ResourceAccessLevelInput!
|
|
|
16
14
|
accessLevelRemoteId
|
|
17
15
|
createdAt
|
|
18
16
|
endTime
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
}
|
|
18
|
+
sessionMetadata {
|
|
19
|
+
METADATA_FRAGMENT
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
22
|
... on SessionNotFoundError {
|
|
@@ -39,113 +37,8 @@ mutation CreateSession($id: ResourceId!, $accessLevel: ResourceAccessLevelInput!
|
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
39
|
`;
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
sessions(input: {resourceId: $id}) {
|
|
45
|
-
__typename
|
|
46
|
-
... on SessionsResult {
|
|
47
|
-
sessions {
|
|
48
|
-
id
|
|
49
|
-
accessLevelRemoteId
|
|
50
|
-
createdAt
|
|
51
|
-
endTime
|
|
52
|
-
metadata {
|
|
53
|
-
METADATA_FRAGMENT
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
`;
|
|
60
|
-
const getSession = async (command, resourceId, accessLevelRemoteId, sessionId, metadataFragment, minCreatedAt) => {
|
|
61
|
-
const { resp, error } = await runQueryDeprecated({
|
|
62
|
-
command: command,
|
|
63
|
-
query: ListSessionsDocument.replace("METADATA_FRAGMENT", metadataFragment),
|
|
64
|
-
variables: {
|
|
65
|
-
id: resourceId,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
if (error) {
|
|
69
|
-
return handleError(command, error);
|
|
70
|
-
}
|
|
71
|
-
switch (resp === null || resp === void 0 ? void 0 : resp.data.sessions.__typename) {
|
|
72
|
-
case "SessionsResult": {
|
|
73
|
-
const sessions = resp.data.sessions.sessions;
|
|
74
|
-
// biome-ignore lint/suspicious/noImplicitAnyLet: please fix how we do queries in this cli
|
|
75
|
-
let selectedSession;
|
|
76
|
-
for (const session of sessions) {
|
|
77
|
-
if (sessionId && session.id !== sessionId) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (session.accessLevelRemoteId !== accessLevelRemoteId) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (minCreatedAt && moment(session.createdAt).diff(minCreatedAt) < 0) {
|
|
84
|
-
// This lets us wait until we get a session that's newly created
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (!selectedSession ||
|
|
88
|
-
moment(session.endTime).diff(selectedSession.endTime) > 0) {
|
|
89
|
-
// Select the session with the latest end time
|
|
90
|
-
selectedSession = session;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return selectedSession;
|
|
94
|
-
}
|
|
95
|
-
default:
|
|
96
|
-
return handleError(command, error, resp);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
const openBrowserAndPollForSession = async (command, authenticationType, resourceId, accessLevelRemoteId, sessionId, metadataFragment, wantNewSession) => {
|
|
100
|
-
let minCreatedAt;
|
|
101
|
-
if (wantNewSession) {
|
|
102
|
-
// Ensure we poll for a new session rather than get an existing one.
|
|
103
|
-
minCreatedAt = moment();
|
|
104
|
-
}
|
|
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();
|
|
128
|
-
// biome-ignore lint/suspicious/noImplicitAnyLet: please fix typing for queries
|
|
129
|
-
let session;
|
|
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;
|
|
144
|
-
}
|
|
145
|
-
return session;
|
|
146
|
-
};
|
|
147
|
-
const createSession = async (command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession) => {
|
|
148
|
-
var _a, _b, _c, _d;
|
|
40
|
+
export const createSession = async (command, resourceId, accessLevel, sessionId, metadataFragment) => {
|
|
41
|
+
var _a, _b, _c;
|
|
149
42
|
const { resp, error } = await runMutation({
|
|
150
43
|
command: command,
|
|
151
44
|
query: CreateSessionDocument.replace("METADATA_FRAGMENT", metadataFragment),
|
|
@@ -160,32 +53,20 @@ const createSession = async (command, resourceId, accessLevel, sessionId, metada
|
|
|
160
53
|
}
|
|
161
54
|
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) {
|
|
162
55
|
case "CreateSessionResult": {
|
|
163
|
-
return (
|
|
56
|
+
return (_c = resp.data) === null || _c === void 0 ? void 0 : _c.createSession;
|
|
164
57
|
}
|
|
165
58
|
case "MfaInvalidError": {
|
|
166
|
-
|
|
59
|
+
await waitForMfa(command);
|
|
60
|
+
return createSession(command, resourceId, accessLevel, sessionId, metadataFragment);
|
|
167
61
|
}
|
|
168
62
|
case "OidcIDTokenNotFoundError": {
|
|
169
|
-
|
|
63
|
+
await waitForValidOidcToken(command, OidcProviderType.AwsSession);
|
|
64
|
+
return createSession(command, resourceId, accessLevel, sessionId, metadataFragment);
|
|
170
65
|
}
|
|
171
66
|
default:
|
|
172
67
|
return handleError(command, error, resp);
|
|
173
68
|
}
|
|
174
69
|
};
|
|
175
|
-
export const getOrCreateSession = async (command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession) => {
|
|
176
|
-
if (!wantNewSession) {
|
|
177
|
-
// Use existing session if it exists
|
|
178
|
-
const existingSession = await getSession(command, resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment);
|
|
179
|
-
if (existingSession) {
|
|
180
|
-
return existingSession;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (sessionId) {
|
|
184
|
-
return handleError(command, `Session not found for given id: ${sessionId}`);
|
|
185
|
-
}
|
|
186
|
-
// Create new session
|
|
187
|
-
return createSession(command, resourceId, accessLevel, sessionId, metadataFragment, wantNewSession);
|
|
188
|
-
};
|
|
189
70
|
export const getSessionExpirationMessage = (session) => {
|
|
190
71
|
const diff = moment(session.endTime).diff(moment(), "minutes");
|
|
191
72
|
const hours = Math.floor(diff / 60);
|
package/oclif.manifest.json
CHANGED
|
@@ -23,6 +23,34 @@
|
|
|
23
23
|
"clear-auth-config.js"
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
|
+
"curl-example": {
|
|
27
|
+
"aliases": [],
|
|
28
|
+
"args": {},
|
|
29
|
+
"description": "Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.",
|
|
30
|
+
"flags": {
|
|
31
|
+
"help": {
|
|
32
|
+
"char": "h",
|
|
33
|
+
"description": "Show CLI help.",
|
|
34
|
+
"name": "help",
|
|
35
|
+
"allowNo": false,
|
|
36
|
+
"type": "boolean"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"hasDynamicHelp": false,
|
|
40
|
+
"hiddenAliases": [],
|
|
41
|
+
"id": "curl-example",
|
|
42
|
+
"pluginAlias": "opal-security",
|
|
43
|
+
"pluginName": "opal-security",
|
|
44
|
+
"pluginType": "core",
|
|
45
|
+
"strict": true,
|
|
46
|
+
"enableJsonFlag": false,
|
|
47
|
+
"isESM": true,
|
|
48
|
+
"relativePath": [
|
|
49
|
+
"build",
|
|
50
|
+
"commands",
|
|
51
|
+
"curl-example.js"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
26
54
|
"login": {
|
|
27
55
|
"aliases": [],
|
|
28
56
|
"args": {},
|
|
@@ -420,21 +448,6 @@
|
|
|
420
448
|
"multiple": false,
|
|
421
449
|
"type": "option"
|
|
422
450
|
},
|
|
423
|
-
"sessionId": {
|
|
424
|
-
"char": "s",
|
|
425
|
-
"description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
|
|
426
|
-
"name": "sessionId",
|
|
427
|
-
"hasDynamicHelp": false,
|
|
428
|
-
"multiple": false,
|
|
429
|
-
"type": "option"
|
|
430
|
-
},
|
|
431
|
-
"refresh": {
|
|
432
|
-
"char": "r",
|
|
433
|
-
"description": "Starts a new session even if one already exists. Useful if a session is about to expire.",
|
|
434
|
-
"name": "refresh",
|
|
435
|
-
"allowNo": false,
|
|
436
|
-
"type": "boolean"
|
|
437
|
-
},
|
|
438
451
|
"profileName": {
|
|
439
452
|
"description": "Uses a custom AWS profile name for the IAM role. Default value is the role's name.",
|
|
440
453
|
"name": "profileName",
|
|
@@ -491,21 +504,6 @@
|
|
|
491
504
|
"hasDynamicHelp": false,
|
|
492
505
|
"multiple": false,
|
|
493
506
|
"type": "option"
|
|
494
|
-
},
|
|
495
|
-
"sessionId": {
|
|
496
|
-
"char": "s",
|
|
497
|
-
"description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
|
|
498
|
-
"name": "sessionId",
|
|
499
|
-
"hasDynamicHelp": false,
|
|
500
|
-
"multiple": false,
|
|
501
|
-
"type": "option"
|
|
502
|
-
},
|
|
503
|
-
"refresh": {
|
|
504
|
-
"char": "r",
|
|
505
|
-
"description": "Starts a new session even if one already exists. Useful if a session is about to expire.",
|
|
506
|
-
"name": "refresh",
|
|
507
|
-
"allowNo": false,
|
|
508
|
-
"type": "boolean"
|
|
509
507
|
}
|
|
510
508
|
},
|
|
511
509
|
"hasDynamicHelp": false,
|
|
@@ -558,21 +556,6 @@
|
|
|
558
556
|
"multiple": false,
|
|
559
557
|
"type": "option"
|
|
560
558
|
},
|
|
561
|
-
"sessionId": {
|
|
562
|
-
"char": "s",
|
|
563
|
-
"description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
|
|
564
|
-
"name": "sessionId",
|
|
565
|
-
"hasDynamicHelp": false,
|
|
566
|
-
"multiple": false,
|
|
567
|
-
"type": "option"
|
|
568
|
-
},
|
|
569
|
-
"refresh": {
|
|
570
|
-
"char": "r",
|
|
571
|
-
"description": "Starts a new session even if one already exists. Useful if a session is about to expire.",
|
|
572
|
-
"name": "refresh",
|
|
573
|
-
"allowNo": false,
|
|
574
|
-
"type": "boolean"
|
|
575
|
-
},
|
|
576
559
|
"action": {
|
|
577
560
|
"description": "Method of connecting to the database.\n- open: Open external database app\n- psql: Start psql session in shell\n- view: View connection configuration details",
|
|
578
561
|
"name": "action",
|
|
@@ -868,14 +851,6 @@
|
|
|
868
851
|
"hasDynamicHelp": false,
|
|
869
852
|
"multiple": false,
|
|
870
853
|
"type": "option"
|
|
871
|
-
},
|
|
872
|
-
"sessionId": {
|
|
873
|
-
"char": "s",
|
|
874
|
-
"description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
|
|
875
|
-
"name": "sessionId",
|
|
876
|
-
"hasDynamicHelp": false,
|
|
877
|
-
"multiple": false,
|
|
878
|
-
"type": "option"
|
|
879
854
|
}
|
|
880
855
|
},
|
|
881
856
|
"hasDynamicHelp": false,
|
|
@@ -943,14 +918,6 @@
|
|
|
943
918
|
"hasDynamicHelp": false,
|
|
944
919
|
"multiple": false,
|
|
945
920
|
"type": "option"
|
|
946
|
-
},
|
|
947
|
-
"sessionId": {
|
|
948
|
-
"char": "s",
|
|
949
|
-
"description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
|
|
950
|
-
"name": "sessionId",
|
|
951
|
-
"hasDynamicHelp": false,
|
|
952
|
-
"multiple": false,
|
|
953
|
-
"type": "option"
|
|
954
921
|
}
|
|
955
922
|
},
|
|
956
923
|
"hasDynamicHelp": false,
|
|
@@ -992,21 +959,6 @@
|
|
|
992
959
|
"hasDynamicHelp": false,
|
|
993
960
|
"multiple": false,
|
|
994
961
|
"type": "option"
|
|
995
|
-
},
|
|
996
|
-
"sessionId": {
|
|
997
|
-
"char": "s",
|
|
998
|
-
"description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
|
|
999
|
-
"name": "sessionId",
|
|
1000
|
-
"hasDynamicHelp": false,
|
|
1001
|
-
"multiple": false,
|
|
1002
|
-
"type": "option"
|
|
1003
|
-
},
|
|
1004
|
-
"refresh": {
|
|
1005
|
-
"char": "r",
|
|
1006
|
-
"description": "Starts a new session even if one already exists. Useful if a session is about to expire.",
|
|
1007
|
-
"name": "refresh",
|
|
1008
|
-
"allowNo": false,
|
|
1009
|
-
"type": "boolean"
|
|
1010
962
|
}
|
|
1011
963
|
},
|
|
1012
964
|
"hasDynamicHelp": false,
|
|
@@ -1026,5 +978,5 @@
|
|
|
1026
978
|
]
|
|
1027
979
|
}
|
|
1028
980
|
},
|
|
1029
|
-
"version": "
|
|
981
|
+
"version": "5.0.0"
|
|
1030
982
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opal-security",
|
|
3
3
|
"description": "Opal allows you to centrally manage access to all of your sensitive systems.",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Opal Security",
|
|
7
7
|
"bin": {
|
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"/scripts"
|
|
73
73
|
],
|
|
74
74
|
"homepage": "https://github.com/opalsecurity/opal-cli/",
|
|
75
|
-
"keywords": [
|
|
75
|
+
"keywords": [
|
|
76
|
+
"oclif"
|
|
77
|
+
],
|
|
76
78
|
"license": "MIT",
|
|
77
79
|
"main": "build/index.js",
|
|
78
80
|
"oclif": {
|