opal-security 2.0.7 → 2.0.10

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 CHANGED
@@ -22,7 +22,7 @@ $ npm install -g opal-security
22
22
  $ opal COMMAND
23
23
  running command...
24
24
  $ opal (-v|--version|version)
25
- opal-security/2.0.7 darwin-x64 node-v14.16.1
25
+ opal-security/2.0.10 darwin-x64 node-v14.16.1
26
26
  $ opal --help [COMMAND]
27
27
  USAGE
28
28
  $ opal COMMAND
@@ -86,7 +86,7 @@ EXAMPLE
86
86
  opal aws:identity
87
87
  ```
88
88
 
89
- _See code: [src/commands/aws/identity.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/aws/identity.ts)_
89
+ _See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/aws/identity.ts)_
90
90
 
91
91
  ## `opal curl-example`
92
92
 
@@ -100,7 +100,7 @@ OPTIONS
100
100
  -h, --help show CLI help
101
101
  ```
102
102
 
103
- _See code: [src/commands/curl-example.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/curl-example.ts)_
103
+ _See code: [src/commands/curl-example.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/curl-example.ts)_
104
104
 
105
105
  ## `opal help [COMMAND]`
106
106
 
@@ -136,7 +136,7 @@ EXAMPLES
136
136
  opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398
137
137
  ```
138
138
 
139
- _See code: [src/commands/iam-roles/start.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/iam-roles/start.ts)_
139
+ _See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/iam-roles/start.ts)_
140
140
 
141
141
  ## `opal kube-roles:start`
142
142
 
@@ -158,7 +158,7 @@ EXAMPLES
158
158
  "arn:aws:iam::712234975475:role/acme-eks-cluster-admin-role"
159
159
  ```
160
160
 
161
- _See code: [src/commands/kube-roles/start.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/kube-roles/start.ts)_
161
+ _See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/kube-roles/start.ts)_
162
162
 
163
163
  ## `opal login`
164
164
 
@@ -175,7 +175,7 @@ EXAMPLE
175
175
  $ opal login
176
176
  ```
177
177
 
178
- _See code: [src/commands/login.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/login.ts)_
178
+ _See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/login.ts)_
179
179
 
180
180
  ## `opal logout`
181
181
 
@@ -192,7 +192,7 @@ EXAMPLE
192
192
  $ opal logout
193
193
  ```
194
194
 
195
- _See code: [src/commands/logout.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/logout.ts)_
195
+ _See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/logout.ts)_
196
196
 
197
197
  ## `opal postgres-instances:start`
198
198
 
@@ -213,7 +213,7 @@ EXAMPLES
213
213
  opal postgres-instances:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId "fullaccess"
214
214
  ```
215
215
 
216
- _See code: [src/commands/postgres-instances/start.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/postgres-instances/start.ts)_
216
+ _See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/postgres-instances/start.ts)_
217
217
 
218
218
  ## `opal resources:get`
219
219
 
@@ -231,7 +231,7 @@ EXAMPLE
231
231
  opal resources:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
232
232
  ```
233
233
 
234
- _See code: [src/commands/resources/get.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/resources/get.ts)_
234
+ _See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/resources/get.ts)_
235
235
 
236
236
  ## `opal set-url`
237
237
 
@@ -254,7 +254,7 @@ EXAMPLE
254
254
  $ opal set-host
255
255
  ```
256
256
 
257
- _See code: [src/commands/set-url.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/set-url.ts)_
257
+ _See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/set-url.ts)_
258
258
 
259
259
  ## `opal ssh:copyFrom`
260
260
 
@@ -280,7 +280,7 @@ EXAMPLES
280
280
  opal ssh:copyFrom --src instance/dir --dest my/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
281
281
  ```
282
282
 
283
- _See code: [src/commands/ssh/copyFrom.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/ssh/copyFrom.ts)_
283
+ _See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/ssh/copyFrom.ts)_
284
284
 
285
285
  ## `opal ssh:copyTo`
286
286
 
@@ -306,7 +306,7 @@ EXAMPLES
306
306
  opal ssh:copyTo --src my/dir --dest instance/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
307
307
  ```
308
308
 
309
- _See code: [src/commands/ssh/copyTo.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/ssh/copyTo.ts)_
309
+ _See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/ssh/copyTo.ts)_
310
310
 
311
311
  ## `opal ssh:start`
312
312
 
@@ -325,5 +325,5 @@ EXAMPLES
325
325
  opal ssh:start --id 51f7176b-0464-4a6f-8369-e951e187b398
326
326
  ```
327
327
 
328
- _See code: [src/commands/ssh/start.ts](https://github.com/cli/opal/blob/v2.0.7/src/commands/ssh/start.ts)_
328
+ _See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v2.0.10/src/commands/ssh/start.ts)_
329
329
  <!-- commandsstop -->
@@ -6,13 +6,15 @@ const credentials_1 = require("../lib/credentials");
6
6
  class CurlExample extends command_1.Command {
7
7
  async run() {
8
8
  const accessToken = await credentials_1.cred.accessToken;
9
+ const organizationID = await credentials_1.cred.organizationID;
9
10
  const configData = config_1.getOrCreateConfigData(this.config.configDir);
10
11
  const url = configData[config_1.urlKey];
11
12
  this.log(`
12
13
  curl -v ${url}/query \\
13
14
  --data-binary '{"query":"query ListSSHSessions {resources(input: {serviceType: SSH, onlyMine: true}) {... on ResourcesResult { resources { name } } } }"}' \\
14
15
  --header "Content-Type: application/json" \\
15
- --header "Authorization: Bearer ${accessToken}"
16
+ --header "Authorization: Bearer ${accessToken}" \\
17
+ --header "X-Opal-Organization-ID: ${organizationID}"
16
18
  `);
17
19
  }
18
20
  }
@@ -6,12 +6,83 @@ const open = require("open");
6
6
  const openid_client_1 = require("openid-client");
7
7
  const apollo_1 = require("../lib/apollo");
8
8
  const credentials_1 = require("../lib/credentials");
9
+ const inquirer = require("inquirer");
10
+ const handler_1 = require("../handler");
11
+ const credentials_2 = require("../lib/credentials");
12
+ const config_1 = require("../lib/config");
9
13
  const ISSUER = 'https://auth.opal.dev';
10
14
  const GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
11
15
  const CLIENT_ID = '42rm6E5v7o67LBpRfjdT9KhnjrQHr9UF';
16
+ const CLISignInMethodDocument = `
17
+ query CLISignInMethod($input: SignInMethodInput!) {
18
+ signInMethod(input: $input) {
19
+ __typename
20
+ ... on SignInMethodResult {
21
+ signInOrganizations {
22
+ organizationId
23
+ organizationName
24
+ }
25
+ }
26
+ }
27
+ }`;
28
+ const CLIAuthSessionCheckDocument = `
29
+ query CLIAuthSessionCheck {
30
+ organizationSettings {
31
+ ... on OrganizationSettingsResult {
32
+ settings {
33
+ id
34
+ }
35
+ }
36
+ }
37
+ }
38
+ `;
12
39
  class Login extends command_1.Command {
13
40
  async run() {
41
+ var _a, _b, _c, _d, _e;
14
42
  try {
43
+ await apollo_1.initClient(this);
44
+ if (await credentials_2.cred.accessToken) {
45
+ this.log('Please use `opal logout` to log out before logging in.');
46
+ return;
47
+ }
48
+ const configDir = this.config.configDir;
49
+ const configData = config_1.getOrCreateConfigData(configDir);
50
+ this.log('Welcome to Opal! ⚡️\n');
51
+ this.log('Please confirm your Opal instance URL:', configData[config_1.urlKey]);
52
+ this.log('If this is not correct, please first use `opal set-url --help`\n');
53
+ const { email } = await inquirer.prompt([{
54
+ name: 'email',
55
+ message: 'Enter your email:',
56
+ type: 'input',
57
+ validate: email => Boolean(email),
58
+ }]);
59
+ const { resp: signInOrganizationsResponse, error } = await handler_1.runQuery({
60
+ command: this,
61
+ query: CLISignInMethodDocument,
62
+ variables: { input: { email } },
63
+ });
64
+ if (error) {
65
+ this.log('Could not connect to Opal. Did you set the right URL? (`opal set-url --help`)');
66
+ }
67
+ const signInOrganizations = (_b = (_a = signInOrganizationsResponse === null || signInOrganizationsResponse === void 0 ? void 0 : signInOrganizationsResponse.data) === null || _a === void 0 ? void 0 : _a.signInMethod) === null || _b === void 0 ? void 0 : _b.signInOrganizations;
68
+ let organizationID = '';
69
+ if (signInOrganizations) {
70
+ if (signInOrganizations.length === 1) {
71
+ organizationID = signInOrganizations[0].organizationId;
72
+ }
73
+ else {
74
+ const responses = await inquirer.prompt([{
75
+ name: 'organizationID',
76
+ message: 'Select an organization:',
77
+ type: 'list',
78
+ choices: signInOrganizations.map(signInOrganization => ({
79
+ name: signInOrganization.organizationName,
80
+ value: signInOrganization.organizationId,
81
+ })),
82
+ }]);
83
+ organizationID = responses.organizationID;
84
+ }
85
+ }
15
86
  const issuer = await openid_client_1.Issuer.discover(ISSUER);
16
87
  const client = new issuer.Client({
17
88
  grant_types: [GRANT_TYPE],
@@ -25,13 +96,29 @@ class Login extends command_1.Command {
25
96
  audience: 'https://opal.dev',
26
97
  scope: 'openid email profile',
27
98
  });
28
- this.log('You are being redirected to your browser to authenticate.\n');
99
+ this.log('\nYou are being redirected to your browser to authenticate.\n');
29
100
  this.log(` User Code: ${handle.user_code}\n`);
101
+ // Wait 2 seconds to show the user code clearly
102
+ const sleep = (ms) => {
103
+ return new Promise(resolve => {
104
+ setTimeout(resolve, ms);
105
+ });
106
+ };
107
+ await sleep(2000);
30
108
  await open(handle.verification_uri_complete, { wait: false });
31
109
  const tokenSet = await handle.poll();
32
110
  const userInfo = await client.userinfo(tokenSet);
33
- await keytar.setPassword(credentials_1.OPAL_CREDS_KEY, userInfo.email || 'unset-email', (tokenSet === null || tokenSet === void 0 ? void 0 : tokenSet.access_token) || '');
34
- await apollo_1.initClient(this);
111
+ await keytar.setPassword(credentials_1.OPAL_CREDS_KEY, (userInfo.email || 'unset-email') + '|' + organizationID, (tokenSet === null || tokenSet === void 0 ? void 0 : tokenSet.access_token) || '');
112
+ // "Representative" authenticated call to check the log-in worked as expected.
113
+ const { resp: authCheckResp, error: authCheckErr } = await handler_1.runQuery({
114
+ command: this,
115
+ query: CLIAuthSessionCheckDocument,
116
+ variables: {},
117
+ });
118
+ if (authCheckErr || !((_e = (_d = (_c = authCheckResp === null || authCheckResp === void 0 ? void 0 : authCheckResp.data) === null || _c === void 0 ? void 0 : _c.organizationSettings) === null || _d === void 0 ? void 0 : _d.settings) === null || _e === void 0 ? void 0 : _e.id)) {
119
+ this.log('Error verifying log in. Authenticated commands may fail. Please double check your URL and use `opal logout; opal login` to try again.\n');
120
+ return;
121
+ }
35
122
  this.log('🎉 You have successfully authenticated with Opal! You can now run authenticated commands.\n');
36
123
  }
37
124
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { Command, flags } from '@oclif/command';
2
- export declare const GetResourceDocument = "\nquery GetResource($id: ResourceId!) {\n resource(input: {id: $id}) {\n __typename\n ... on ResourceResult {\n resource {\n name\n id\n description\n ownerTeam {\n name\n id\n }\n connection {\n name\n id\n }\n resourceUsers {\n user {\n email\n }\n }\n access {\n status\n expiration\n }\n }\n }\n ... on ResourceNotFoundError {\n message\n }\n }\n}";
2
+ export declare const GetResourceDocument = "\nquery GetResource($id: ResourceId!) {\n resource(input: {id: $id}) {\n __typename\n ... on ResourceResult {\n resource {\n name\n id\n description\n ownerTeam {\n name\n id\n }\n connection {\n name\n id\n }\n resourceUsers {\n user {\n email\n }\n }\n }\n }\n ... on ResourceNotFoundError {\n message\n }\n }\n}";
3
3
  export default class GetResource extends Command {
4
4
  static description: string;
5
5
  static examples: string[];
@@ -26,10 +26,6 @@ query GetResource($id: ResourceId!) {
26
26
  email
27
27
  }
28
28
  }
29
- access {
30
- status
31
- expiration
32
- }
33
29
  }
34
30
  }
35
31
  ... on ResourceNotFoundError {
package/lib/handler.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { Command } from '@oclif/command';
2
- interface QueryHandlerProps {
2
+ interface QueryHandlerProps<TVar = Record<string, any>> {
3
3
  command: Command;
4
4
  query: string;
5
- variables?: Record<string, any>;
5
+ variables?: TVar;
6
6
  }
7
7
  export declare const runMutation: ({ command, query, variables, }: QueryHandlerProps) => Promise<{
8
8
  resp: import("@apollo/client/core").FetchResult<any, Record<string, any>, Record<string, any>> | null;
9
9
  error: any;
10
10
  }>;
11
- export declare const runQuery: ({ command, query, variables, }: QueryHandlerProps) => Promise<{
12
- resp: import("@apollo/client/core").ApolloQueryResult<any> | null;
11
+ export declare const runQuery: <TResponse = any, TVar = Record<string, any>>({ command, query, variables, }: QueryHandlerProps<TVar>) => Promise<{
12
+ resp: import("@apollo/client/core").ApolloQueryResult<TResponse> | null;
13
13
  error: any;
14
14
  }>;
15
15
  declare const handler: ({ command, query, variables }: QueryHandlerProps) => void;
package/lib/lib/apollo.js CHANGED
@@ -21,6 +21,7 @@ exports.initClient = async (command) => {
21
21
  const currentCLIVersion = command.config.version;
22
22
  const configData = config_1.getOrCreateConfigData(configDir);
23
23
  const accessToken = await credentials_1.cred.accessToken;
24
+ const organizationID = await credentials_1.cred.organizationID;
24
25
  const httpsAgent = new https.Agent({
25
26
  rejectUnauthorized: !configData[config_1.allowSelfSignedCertsKey],
26
27
  });
@@ -36,7 +37,7 @@ exports.initClient = async (command) => {
36
37
  });
37
38
  const authLink = context_1.setContext((_, { headers }) => {
38
39
  return {
39
- headers: Object.assign(Object.assign({}, headers), { authorization: `Bearer ${accessToken}` }),
40
+ headers: Object.assign(Object.assign({}, headers), { authorization: `Bearer ${accessToken}`, 'X-Opal-Organization-ID': organizationID }),
40
41
  };
41
42
  });
42
43
  const checkCLIVersion = (operation) => {
@@ -44,15 +45,15 @@ exports.initClient = async (command) => {
44
45
  const headers = (_a = operation.getContext().response) === null || _a === void 0 ? void 0 : _a.headers;
45
46
  if (headers) {
46
47
  const expectedCLIMajorVersion = headers.get('Opal-CLI-Expected-Major-Version');
47
- // defaults to version 1 since old Opal version won't have an advertised expected client version
48
- const semvarExpectedMinCLIVersion = `${expectedCLIMajorVersion || '1'}.0.0`;
49
- if (semver_1.major(currentCLIVersion) < semver_1.major(semvarExpectedMinCLIVersion)) {
50
- command.warn(chalk_1.default.yellow(`❗️ Update your CLI to match the compatible version. Expected major version ${chalk_1.default.blueBright(semvarExpectedMinCLIVersion)}, but version ${chalk_1.default.blueBright(currentCLIVersion)} is installed.
51
-
52
- Upgrade using the following command: ${chalk_1.default.blueBright('brew upgrade opalsecurity/brew/opal-security')}`));
53
- }
54
- else if (semver_1.major(currentCLIVersion) > semver_1.major(semvarExpectedMinCLIVersion)) {
55
- command.warn(chalk_1.default.yellow('❗️ Uh oh! The currently installed Opal server is outdated. Please contact your Opal admins to let them know they need to upgrade their deployment.'));
48
+ if (expectedCLIMajorVersion) {
49
+ const semvarExpectedMinCLIVersion = `${expectedCLIMajorVersion}.0.0`;
50
+ if (semver_1.major(currentCLIVersion) < semver_1.major(semvarExpectedMinCLIVersion)) {
51
+ command.warn(chalk_1.default.yellow(`❗️ Update your CLI to match the compatible version. Expected major version ${chalk_1.default.blueBright(semvarExpectedMinCLIVersion)}, but version ${chalk_1.default.blueBright(currentCLIVersion)} is installed.
52
+ Upgrade using the following command: ${chalk_1.default.blueBright('brew upgrade opalsecurity/brew/opal-security')}`));
53
+ }
54
+ else if (semver_1.major(currentCLIVersion) > semver_1.major(semvarExpectedMinCLIVersion)) {
55
+ command.warn(chalk_1.default.yellow('❗️ Uh oh! The currently installed Opal server is outdated. Please contact your Opal admins to let them know they need to upgrade their deployment.'));
56
+ }
56
57
  }
57
58
  }
58
59
  };
@@ -1,6 +1,7 @@
1
1
  export declare const OPAL_CREDS_KEY = "opal";
2
2
  export declare const cred: {
3
3
  removeCredentials(after: number): Promise<void>;
4
- readonly accountId: Promise<string>;
5
- readonly accessToken: Promise<string>;
4
+ readonly accountId: Promise<string | undefined>;
5
+ readonly organizationID: Promise<string | undefined>;
6
+ readonly accessToken: Promise<string | undefined>;
6
7
  };
@@ -20,6 +20,9 @@ exports.cred = {
20
20
  return (async () => {
21
21
  try {
22
22
  const keyContents = await keytar.findCredentials(exports.OPAL_CREDS_KEY);
23
+ if (!keyContents[0]) {
24
+ return undefined;
25
+ }
23
26
  const { account } = keyContents[0];
24
27
  return account;
25
28
  }
@@ -28,12 +31,31 @@ exports.cred = {
28
31
  }
29
32
  })();
30
33
  },
34
+ get organizationID() {
35
+ return (async () => {
36
+ try {
37
+ const keyContents = await keytar.findCredentials(exports.OPAL_CREDS_KEY);
38
+ if (!keyContents[0]) {
39
+ return undefined;
40
+ }
41
+ const { account } = keyContents[0];
42
+ const parts = account.split('|');
43
+ if (!parts || parts.length <= 1) {
44
+ return undefined;
45
+ }
46
+ return parts.pop();
47
+ }
48
+ catch (error) {
49
+ throw error;
50
+ }
51
+ })();
52
+ },
31
53
  get accessToken() {
32
54
  return (async () => {
33
55
  try {
34
56
  const keyContents = await keytar.findCredentials(exports.OPAL_CREDS_KEY);
35
57
  if (!keyContents[0]) {
36
- return 'unset-token';
58
+ return undefined;
37
59
  }
38
60
  const { password } = keyContents[0];
39
61
  return password;