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.
@@ -1,11 +1,9 @@
1
- import { ux } from "@oclif/core";
2
- import inquirer from "inquirer";
3
1
  import moment from "moment";
4
- import open from "open";
5
- import { runMutation, runQueryDeprecated } from "../handler.js";
2
+ import { OidcProviderType } from "../graphql/graphql.js";
3
+ import { runMutation } from "../handler.js";
6
4
  import { handleError } from "./apollo.js";
7
- import { getOrCreateConfigData, urlKey } from "./config.js";
8
- import { sleep } from "./util.js";
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
- metadata {
20
- METADATA_FRAGMENT
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 ListSessionsDocument = `
43
- query ListSessions($id: ResourceId!) {
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 (_d = (_c = resp.data) === null || _c === void 0 ? void 0 : _c.createSession) === null || _d === void 0 ? void 0 : _d.session;
56
+ return (_c = resp.data) === null || _c === void 0 ? void 0 : _c.createSession;
164
57
  }
165
58
  case "MfaInvalidError": {
166
- return openBrowserAndPollForSession(command, "MFA Validation", resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment, wantNewSession);
59
+ await waitForMfa(command);
60
+ return createSession(command, resourceId, accessLevel, sessionId, metadataFragment);
167
61
  }
168
62
  case "OidcIDTokenNotFoundError": {
169
- return openBrowserAndPollForSession(command, "OIDC Authenication", resourceId, accessLevel.accessLevelRemoteId, sessionId, metadataFragment, wantNewSession);
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);
@@ -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": "4.0.4"
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.0.4",
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": ["oclif"],
75
+ "keywords": [
76
+ "oclif"
77
+ ],
76
78
  "license": "MIT",
77
79
  "main": "build/index.js",
78
80
  "oclif": {